基于JerseyExtension与JUnit5的测试:请求间会话无法保持的解决问询
如何在JerseyExtension(JUnit5)测试中保持会话一致性?
这个问题的核心在于Jersey客户端默认不会自动管理会话Cookie,所以每次请求都会被服务器视为新的会话,生成新的Session ID。要解决这个问题,只需要让客户端启用Cookie存储和自动传递功能,这样后续请求就能带上之前的JSESSIONID Cookie,服务器就能识别同一个会话了。
解决步骤
1. 配置客户端启用Cookie管理
修改configureJerseyClient方法,在客户端配置中添加Cookie处理逻辑,让客户端自动保存和发送Cookie:
import java.net.CookieManager; import javax.ws.rs.client.ClientProperties; // ... 其他代码不变 protected ClientConfig configureJerseyClient(ExtensionContext extensionContext, ClientConfig clientConfig) { assertNotNull(extensionContext); assertNotNull(clientConfig); // 启用Cookie管理器,自动维护会话Cookie clientConfig.property(ClientProperties.COOKIE_HANDLER, new CookieManager()); return clientConfig; }
2. 在测试中复用请求构建器(可选,但更可靠)
在测试方法中,使用同一个Invocation.Builder发起多次请求,它会自动维护Cookie上下文,确保后续请求能带上之前的会话标识:
import javax.ws.rs.client.Invocation; // ... 其他代码不变 @Test public void testSessionSet(WebTarget target) { // 创建一个请求构建器,后续请求复用它以保持Cookie上下文 Invocation.Builder requestBuilder = target.path("session").request(MediaType.APPLICATION_JSON_TYPE); // 执行PUT请求设置会话属性 Response response0 = requestBuilder.put(Entity.entity("{}", MediaType.APPLICATION_JSON_TYPE)); System.out.println("PUT: status="+response0.getStatus()+" response="+response0.readEntity(String.class)); response0.close(); // 同一个构建器执行GET请求,自动携带JSESSIONID Cookie Response response1 = requestBuilder.get(); System.out.println("GET: status="+response1.getStatus()+" response="+response1.readEntity(String.class)); response1.close(); }
为什么这样有效?
当服务器第一次返回JSESSIONID Cookie时,启用了Cookie管理的客户端会自动保存这个Cookie。后续请求时,客户端会把这个Cookie添加到请求头中,服务器通过JSESSIONID就能找到之前创建的HttpSession,从而获取到之前设置的name属性。
修改后,你应该会看到PUT和GET请求的Session ID一致,并且GET请求能正确返回foo值:
PUT: status=200 response=123456789: Set name attribute called GET: status=200 response=123456789: foo
完整修改后的代码
package com.test.jerseysession; import com.github.hanleyt.JerseyExtension; import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.servlet.ServletContainer; import org.glassfish.jersey.test.DeploymentContext; import org.glassfish.jersey.test.ServletDeploymentContext; import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory; import org.glassfish.jersey.test.spi.TestContainerFactory; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.RegisterExtension; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.client.Entity; import javax.ws.rs.client.Invocation; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.CookieManager; import static org.junit.jupiter.api.Assertions.assertNotNull; public class JerseyTestWithGrizzly { private final static TestContainerFactory testContainerFactory; private final ServletContainer servletContainer; private final ResourceConfig resourceConfig; private final DeploymentContext deploymentContext; static { testContainerFactory = new GrizzlyWebTestContainerFactory(); } @RegisterExtension @SuppressWarnings("unused") JerseyExtension jerseyExtension = new JerseyExtension( this::getTestContainerFactory, this::configureDeploymentContext, this::configureJerseyClient); public JerseyTestWithGrizzly() { this.resourceConfig = new ResourceConfig() .packages("com.test.jerseysession") .register(getClass()); this.servletContainer = new ServletContainer(resourceConfig); this.deploymentContext = ServletDeploymentContext.builder(resourceConfig) .servlet(servletContainer) .servletPath("api") .build(); } @Path("session") public static class SessionResource { @GET public String get(@Context HttpServletRequest request) { HttpSession session = request.getSession(true); Object obj = session.getAttribute("name"); return session.getId() + ": " + obj; } @PUT public String put(@Context HttpServletRequest request) { HttpSession session = request.getSession(true); session.setAttribute("name", "foo"); return session.getId()+": Set name attribute called"; } } protected ClientConfig configureJerseyClient(ExtensionContext extensionContext, ClientConfig clientConfig) { assertNotNull(extensionContext); assertNotNull(clientConfig); // 启用Cookie管理器,自动维护会话Cookie clientConfig.property(ClientProperties.COOKIE_HANDLER, new CookieManager()); return clientConfig; } protected DeploymentContext configureDeploymentContext(ExtensionContext extensionContext) { assertNotNull(extensionContext); return deploymentContext; } protected TestContainerFactory getTestContainerFactory(ExtensionContext extensionContext) { assertNotNull(extensionContext); return testContainerFactory; } @Test public void testSessionSet(WebTarget target) { Invocation.Builder requestBuilder = target.path("session").request(MediaType.APPLICATION_JSON_TYPE); // 执行PUT请求 Response response0 = requestBuilder.put(Entity.entity("{}", MediaType.APPLICATION_JSON_TYPE)); System.out.println("PUT: status="+response0.getStatus()+" response="+response0.readEntity(String.class)); response0.close(); // 执行GET请求,复用同一个构建器以携带Cookie Response response1 = requestBuilder.get(); System.out.println("GET: status="+response1.getStatus()+" response="+response1.readEntity(String.class)); response1.close(); } }
内容的提问来源于stack exchange,提问作者AlexC
相关产品推荐
相关产品推荐

