TestRestTemplate设置ENABLE_REDIRECTS后仍不执行重定向的问题排查
问题背景
基于Spring框架采用PRG(Post-Redirect-Get)模式开发控制器,结合RedirectView、RedirectAttributes和addFlashAttributes实现功能,手动浏览器访问时重定向逻辑正常。但使用TestRestTemplate进行全链路集成测试时,即便设置了HttpClientOption.ENABLE_REDIRECTS,调用postForEntity仍仅返回302状态码和重定向地址,无法自动完成重定向,也无法获取Post控制器中的Flash属性,导致后续GET请求无法复用这些属性。已知MockMVC可获取Flash属性,但无法满足全链路测试需求,需解决以下问题:
ENABLE_REDIRECTS配置是否有误?- 有无其他配置重定向的方式(例如在
postForEntity的HttpRequest中配置)? - 如何让TestRestTemplate自动完成重定向?
- 若无法实现自动重定向,能否配置TestRestTemplate返回Flash属性,供后续针对重定向地址的GET请求使用?
解决方案
1. 关于ENABLE_REDIRECTS配置的有效性
你的配置方式本身没有错误,但TestRestTemplate的ENABLE_REDIRECTS仅对标准GET请求的重定向生效。根据HTTP规范,Post请求返回302时,客户端默认不会自动跳转(需主动发起GET请求),因此该选项无法处理Post转Get的PRG场景重定向,并非配置错误。
2. 其他重定向配置方式
可以通过自定义RestTemplate的ClientHttpRequestFactory,配置宽松重定向策略,支持Post转Get的重定向逻辑:
// 构建支持Post转Get重定向的HttpClient CloseableHttpClient httpClient = HttpClientBuilder.create() .setRedirectStrategy(new LaxRedirectStrategy()) // 宽松策略自动处理302/303重定向 .build(); HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); // 用自定义工厂构建TestRestTemplate RestTemplateBuilder rtb = new RestTemplateBuilder() .rootUri("http://localhost:7777") .requestFactory(() -> factory); TestRestTemplate trt = new TestRestTemplate(rtb);
LaxRedirectStrategy会自动跟进重定向,无论原请求方法是什么,都会转为GET请求访问目标地址。
3. 让TestRestTemplate自动完成重定向
使用上述自定义LaxRedirectStrategy配置后,postForEntity会自动跟进重定向,返回最终GET请求的响应结果。需注意:
- 重定向后的响应是目标GET接口(如
/view)的返回内容 - Flash属性存储在Session中,测试时需使用同一个TestRestTemplate实例,或手动维护会话Cookie(JSESSIONID)以保持上下文
修改后的测试代码示例:
@Test @Order(0020) void givenTestMyentityWhenSubmitThenExpectedViewDataReturned() throws Exception { Myentity tempMyentity = testData.getGoodTestData().get("test-myentity-001"); // 配置支持重定向的HttpClient CloseableHttpClient httpClient = HttpClientBuilder.create() .setRedirectStrategy(new LaxRedirectStrategy()) .build(); HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); RestTemplateBuilder rtb = new RestTemplateBuilder() .rootUri("http://localhost:7777") .requestFactory(() -> factory); TestRestTemplate trt = new TestRestTemplate(rtb); // 自动跟进重定向,直接返回/view的响应 ResponseEntity<String> responseView = trt.postForEntity("/submit", HttpRequester.makeMyEntityHttpRequest(tempMyentity), String.class); assertEquals(HttpStatus.OK, responseView.getStatusCode()); // 断言视图内容,比如包含Flash属性对应的成功提示 assertTrue(responseView.getBody().contains("操作成功")); }
4. 手动处理重定向时获取Flash属性
若不需要自动重定向,可通过维护会话Cookie的方式,让后续GET请求读取到Flash属性:
- 发送Post请求,获取302响应及会话Cookie(JSESSIONID)
- 携带该Cookie发送GET请求到重定向地址,此时Controller可读取到Session中的Flash属性
示例代码:
@Test @Order(0020) void givenTestMyentityWhenSubmitThenUseFlashAttributesInGet() throws Exception { Myentity tempMyentity = testData.getGoodTestData().get("test-myentity-001"); TestRestTemplate trt = new TestRestTemplate(new RestTemplateBuilder().rootUri("http://localhost:7777")); // 发送Post请求,获取302响应 ResponseEntity<String> responseSubmit = trt.postForEntity("/submit", HttpRequester.makeMyEntityHttpRequest(tempMyentity), String.class); assertEquals(HttpStatus.FOUND, responseSubmit.getStatusCode()); String location = responseSubmit.getHeaders().getLocation().toString(); // 提取会话Cookie List<String> cookies = responseSubmit.getHeaders().get(HttpHeaders.SET_COOKIE); String sessionCookie = cookies.stream() .filter(c -> c.startsWith("JSESSIONID=")) .findFirst() .orElseThrow(() -> new IllegalStateException("未获取到会话Cookie")); // 构造携带Cookie的GET请求 HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.COOKIE, sessionCookie); HttpEntity<Void> getRequest = new HttpEntity<>(headers); // 发送GET请求,读取Flash属性 ResponseEntity<String> responseView = trt.exchange(location, HttpMethod.GET, getRequest, String.class); assertEquals(HttpStatus.OK, responseView.getStatusCode()); // 断言视图中包含Flash属性对应的内容 }
额外说明
- 全链路测试中,
TestRestTemplate默认无状态,每次请求新建会话,需复用实例或手动传递Cookie保持会话上下文 LaxRedirectStrategy依赖Apache HttpClient,需确保项目引入org.apache.httpcomponents:httpclient依赖
内容的提问来源于stack exchange,提问作者tlarson

