#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 идентификатор страницы не меняется, и Калитка переопределяет старую запись в магазине. Посмотрите в этом направлении.