POST后重定向vs GET后重定向



我正在做一个Spring项目。这是我的基本控制器:

@Controller
public class Editor {
private static final String EDITOR_URL = "/editor";
@RequestMapping(value = EDITOR_URL, method = {POST, GET})
public ModelAndView edit(HttpServletResponse response,
        HttpServletRequest request,
        RedirectAttributes redirectAttributes, 
        @RequestParam Map<String, String> allRequestParams) {
    // The code is trimmed to keep it short
    // It doesn't really matter where it gets the URL, it works fine
    String redirectURL = getRedirectUrl();
    // redirectURL is going to be /editor/pad.html
    return new ModelAndView("redirect:" + redirectUrl);
}
从web . xml:

<servlet-mapping>
    <servlet-name>edm</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

我已经嵌入了jetty,我正在尝试集成测试:

@Test
public void redirectToEditPadSuccess() throws Exception {
    HttpHeaders requestHeaders = new HttpHeaders();
    UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(END_POINT + "/edm/editor")
            .queryParam("param1", "val1")
            .queryParam("param2", "val2");
    HttpEntity<?> entity = new HttpEntity<>(requestHeaders);
    HttpEntity<String> response = restTemplate.exchange(
            builder.build().encode().toUri(),
            HttpMethod.POST,
            entity,
            String.class);
    HttpHeaders httpResponseHeaders = response.getHeaders();
    List<String> httpReponseLocationHeader = httpResponseHeaders.get("Location");
    assertTrue(httpReponseLocationHeader.size() == 1);
    String redirectLocation = httpReponseLocationHeader.get(0);
    URL redirectURL = new URL(redirectLocation);
    assertEquals("/edm/editor/pad.html", redirectURL.getPath());
}

所以当我执行上面的代码时,它工作得很好,我得到了一个绿色的OK符号。

现在,控制器接受POST和GET方法。如果我使用GET方法(代替HttpMethod)执行测试。POST与HttpMethod.GET),结果将是一个404。

日志显示:

WARN  org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/edm/editor/pad.html] in DispatcherServlet with name 'edm'

我试图调试应用程序到DispatcherServlet和奇怪的事情是,与GET, 302/重定向响应后,Dispatcher被再次调用,并把它变成一个200 -不知道如何和为什么。

我将试着解释发生了什么,然后提供一个解决方案。

首先让我们忘记您正在运行一个rest用例,并假设请求来自浏览器。

场景1:浏览器发出GET请求,服务器响应重定向

在这种情况下,浏览器将响应状态码读取为302,并使用Location响应头发出另一个请求。用户看到快速重新加载,但没有注意到任何错误。

场景2:浏览器发出POST请求,服务器用重定向响应

在这种情况下,浏览器遵循响应代码并发出重定向,但是,第二个请求是GET请求,并且原始请求正文在第二个请求中丢失。这是因为严格按照HTTP标准,如果没有用户的明确请求,浏览器不能将数据"重新发布"到服务器。(有些浏览器会提示用户是否要重新发布)

现在在您的代码中,RestTemplate正在使用我认为是默认的HttpClientFactory,最有可能的是这个:https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/http/client/SimpleClientHttpRequestFactory.java。

RestTemplate是这样处理上面两个场景的:

场景1:Rest模板发出GET请求,服务器用重定向响应这里Rest Template实例将完全像浏览器一样工作。这就是为什么正在发出两个请求,第二个请求正在寻找/edm/editor/pad.html

的原因。

Scenario 2 : Rest Template issues a POST request, and the server responds with a redirect.在这种情况下,Rest Template将在第一次调用后停止,因为它不能自动覆盖您的请求方法并将其更改为GET,并且它不能像浏览器那样提示您获得许可。

解决方案:当创建RestTemplate的实例时,将客户端工厂的重写版本传递给它,类似于

new RestTemplate(new SimpleClientHttpRequestFactory() {
     protected void prepareConnection(HttpURLConnection conn, String httpMethod) throws IOException { 
       super.prepareConnection(conn, httpMethod);    
       conn.setInstanceFollowRedirects(false); 
     } 
});

指示rest模板在第一个请求后停止。

很抱歉这么长时间的回答,但我希望这能澄清一些事情。

最新更新