весенняя сессия с поддержкой redis в приложении wicket, проблема с вызовами ajax, приводящая к перенаправлению всей страницы

#java #spring #redis #wicket

#Ява #весна #редис #калитка

Вопрос:

Я пытаюсь изменить приложение калитки, чтобы сохранить сеанс в redis через весеннюю сессию. Сеанс отображается в redis, но я столкнулся с проблемой, что всякий раз, когда приложение выполняет стандартный вызов ajax wicket, ответ от wicket включает заголовок местоположения Ajax, который интерпретируется wicket-ajax-jquery.js запуск перенаправления страницы. Но это происходит только ПОСЛЕ того, как первый вызов ajax был успешным. Например, первый вызов ajax может выглядеть следующим образом:

http://host:port/context/help/admin?0-1.IBehaviorListener.0-smartTable-tableArea-records-0-row-detailToggleCell-detailToggleLinkamp;_=1636756805561

и заголовки ответов НЕ включают Ajax-Местоположение. А затем, позже, следующий вызов ajax может выглядеть так:

http://host:port/context/help/admin?1-1.IBehaviorListener.0-smartTable-tableArea-records-0-row-detailToggleCell-detailToggleLinkamp;_=1636756906417

Но заголовок ответа теперь включает в себя следующее:

Ajax-Location: ./admin?2

и вместо того, чтобы просто обновлять страницу ajax, вся страница перенаправляется на URL-адрес, указанный в этом заголовке, из-за кода в src/main/java/org/apache/wicket/ajax/res/js/wicket-ajax-jquery.js

Копаясь в коде ядра калитки с помощью отладчика, подумайте об этом, если он не создает заголовок местоположения Ajax и работает должным образом:

 Step completed: "thread=ba6f07:3", org.apache.wicket.core.request.handler.ListenerInterfaceRequestHandler.respond(), line=197 bci=169 ba6f07:3[1] print canCallListenerInterfaceAfterExpiry  canCallListenerInterfaceAfterExpiry = false ba6f07:3[1] print freshPage  freshPage = false ba6f07:3[1] print isStateless  isStateless = false ba6f07:3[1] print component  component = "[AjaxLink [Component id = detailToggleLink]]"  

а затем сравните с этим, где он создает заголовок местоположения Ajax и не работает должным образом:

 Breakpoint hit: "thread=ba6f07:7", org.apache.wicket.core.request.handler.ListenerInterfaceRequestHandler.respond(), line=197 bci=169 ba6f07:7[1] print canCallListenerInterfaceAfterExpiry  canCallListenerInterfaceAfterExpiry = false ba6f07:7[1] print freshPage  freshPage = true ba6f07:7[1] print isStateless  isStateless = false ba6f07:7[1] print component  component = null  

The difference being that when it doesn’t work, freshPage is true and component is null.

Note: this pattern is fully functional in another similar application that I have and I’ve spent some time comparing the two. Clearly, something is missing from the original application in the app that I’m working on but I haven’t been able to identify it yet.

My redis http session config class looks like this:

 import javax.annotation.PostConstruct;  import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession; import org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration; import org.springframework.session.web.http.CookieHttpSessionStrategy; import org.springframework.session.web.http.DefaultCookieSerializer;  @Configuration @EnableRedisHttpSession public class MyRedisHttpSessionConfig extends RedisHttpSessionConfiguration {  private JedisConnectionFactory connectionFactory;   @PostConstruct  public void init()  {  CookieHttpSessionStrategy strategy = new CookieHttpSessionStrategy();  DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();  cookieSerializer.setCookieName( "SESSION" );  strategy.setCookieSerializer(cookieSerializer);  setHttpSessionStrategy( strategy );  }    @Bean  public JedisConnectionFactory connectionFactory() throws Exception  {  return connectionFactory;  }   public void setConnectionFactory( JedisConnectionFactory connectionFactory )  {  this.connectionFactory = connectionFactory;  }  }  

my web.xml has this:

 ...  lt;filtergt;  lt;filter-namegt;requestLoggingFilterlt;/filter-namegt;  lt;filter-classgt;org.springframework.web.filter.DelegatingFilterProxylt;/filter-classgt;  lt;/filtergt;   lt;filtergt;  lt;filter-namegt;springSessionRepositoryFilterlt;/filter-namegt;  lt;filter-classgt;org.springframework.web.filter.DelegatingFilterProxylt;/filter-classgt;  lt;/filtergt;   lt;filtergt;  lt;filter-namegt;myApplicationWicketFilterlt;/filter-namegt;  lt;filter-classgt;org.apache.wicket.protocol.http.WicketFilterlt;/filter-classgt;  lt;init-paramgt;  lt;param-namegt;applicationFactoryClassNamelt;/param-namegt;  lt;param-valuegt;org.apache.wicket.spring.SpringWebApplicationFactorylt;/param-valuegt;  lt;/init-paramgt;  lt;init-paramgt;  lt;param-namegt;filterMappingUrlPatternlt;/param-namegt;  lt;param-valuegt;/*lt;/param-valuegt;  lt;/init-paramgt;  lt;/filtergt; ...  lt;filter-mappinggt;  lt;filter-namegt;springSessionRepositoryFilterlt;/filter-namegt;  lt;url-patterngt;/*lt;/url-patterngt;  lt;/filter-mappinggt;  ...  lt;filter-mappinggt;  lt;filter-namegt;ariesApplicationWicketFilterlt;/filter-namegt;  lt;url-patterngt;/*lt;/url-patterngt;  lt;dispatchergt;REQUESTlt;/dispatchergt;  lt;dispatchergt;ERRORlt;/dispatchergt;  lt;/filter-mappinggt; ...  

и в моем конфигурационном файле spring beans есть это:

 ... lt;!-- The RedisHttpSessionConfiguration creates an http Filter bean with name "springSessionRepositoryFilter" which is referenced in web.xml --gt;  lt;context:annotation-config/gt;   lt;util:constant static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/gt;  lt;bean class="MyRedisHttpSessionConfig"gt;  lt;property name="connectionFactory" ref="webTierRedisConnectionFactory"/gt;  lt;/beangt;   lt;bean id="webTierRedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"gt;  lt;property name="hostName" value="${service-tier:redisSentinelMasterName}"/gt;  lt;property name="port" value="${service-tier:redisSentinelHostPortCsv}"/gt;  lt;property name="usePool" value="true"/gt;  lt;property name="poolConfig"gt;  lt;bean class="redis.clients.jedis.JedisPoolConfig"gt;  lt;property name="maxWaitMillis" value="5000"/gt;  lt;property name="maxTotal" value="50"/gt;  lt;property name="maxIdle" value="5"/gt;  lt;property name="minIdle" value="1"/gt;  lt;property name="testWhileIdle" value="true"/gt;  lt;property name="timeBetweenEvictionRunsMillis" value="60000"/gt;  lt;property name="numTestsPerEvictionRun" value="10"/gt;  lt;/beangt;  lt;/propertygt;  lt;/beangt; ...  

Зависимости от плюща включают:

 lt;!-- these are for redis httpsession --gt;  lt;dependency org="redis.clients" name="jedis" rev="2.8.1"/gt;  lt;dependency org="org.springframework.data" name="spring-data-redis" rev="1.7.4.RELEASE"/gt;  lt;dependency org="org.springframework.data" name="spring-data-keyvalue" rev="1.1.4.RELEASE"/gt;  lt;dependency org="org.springframework.session" name="spring-session" rev="1.2.2.RELEASE"/gt;  

и калитка 7.5.0 и пружина 4.2.8.

У кого-нибудь есть какие-нибудь соображения о том, что может происходить? Почему после помещения сеанса в redis (который он отображается там, я вижу это (с помощью redis-cli и клавиш и команд дампа), большинство вызовов ajax вызывают перенаправление на всю страницу из-за заголовков ответов от вызова ajax, включая Ajax-Местоположение?

Комментарии:

1. Правильно ли работает приложение, если вы используете хранилище сеансов по умолчанию (HttpSession DiskPageStore) ? Если это работает, то проблема должна быть в (весенней) интеграции Redis. Если он все еще перенаправляется на новую страницу, мы можем исключить Redis, так что это либо Калитка, либо ваше приложение.

2. Да, приложение корректно работает с хранилищем сеансов по умолчанию, и вы правильно заметили, что это означает, что проблема заключается в интеграции redis весенней сессии.

3. Калитка хранит данные страницы с ключом SessionID pageId и значением сериализованной страницы (т. е. в байтах). В случае запросов Ajax идентификатор страницы не меняется, и Калитка переопределяет старую запись в магазине. Посмотрите в этом направлении.