带有HTTP Location头的ResponseEntity未引发重定向

3
一个 Spring MVC 控制器需要将应用程序的控制流重定向到同一应用程序中的不同 URL 终点。但当前代码返回一个空白页面以及包括预期目标 URL 作为 "forward" 标头的 response headers。当将 "forward" 头的内容复制粘贴到 Web 浏览器中时,预期的终结点得到了成功调用。请问下面的代码需要进行哪些具体修改才能使 POST 控制器成功地将控制流重定向到预期的目标终端点,而不是返回空白页?
以下是控制器方法的代码:
@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public ResponseEntity<?> auth(FormData formData, HttpServletRequest req, HttpServletResponse resp) {
    System.out.println("11111111111111 inside POST");
    HttpHeaders responseHeaders = new HttpHeaders();
    boolean passedTheTest = true;//ACTUAL LOGIC IS OMITTED HERE FOR SIMPLICITY
    if (passedTheTest) {
        //SOME OFF TOPIC LOGIC HERE IS OMITTED
        CsrfToken csrf = (CsrfToken) req.getAttribute(CsrfToken.class.getName());
        String updateCsrf = csrf.getToken();
        responseHeaders.set("XSRF-TOKEN", updateCsrf);
        if(resp.getHeaders("Cache-Control")!=null){responseHeaders.put("Cache-Control" , new ArrayList<String>(resp.getHeaders("Cache-Control")));}
        if(resp.getHeader("Content-Language")!=null){responseHeaders.set("Content-Language" , resp.getHeader("Content-Language"));}
        if(resp.getHeader("Content-Length")!=null){responseHeaders.set("Content-Length" , resp.getHeader("Content-Length"));}
        if(resp.getHeader("Date")!=null){responseHeaders.set("Date" , resp.getHeader("Date"));}
        if(resp.getHeader("Expires")!=null){responseHeaders.set("Expires" , resp.getHeader("Expires"));}
        if(resp.getHeader("Pragma")!=null){responseHeaders.set("Pragma" , resp.getHeader("Pragma"));}
        if(resp.getHeader("Server")!=null){responseHeaders.set("Server" , resp.getHeader("Server"));}
        if(resp.getHeader("X-Application-Context")!=null){responseHeaders.set("X-Application-Context" , resp.getHeader("X-Application-Context"));}
        if(resp.getHeader("X-Frame-Options")!=null){responseHeaders.set("X-Frame-Options" , resp.getHeader("X-Frame-Options"));}
        if(resp.getHeader("X-XSS-Protection")!=null){responseHeaders.set("X-XSS-Protection" , resp.getHeader("X-XSS-Protection"));}
        if(resp.getHeader("x-content-type-options")!=null){responseHeaders.set("x-content-type-options" , resp.getHeader("x-content-type-options"));}
        if(req.getSession().getAttribute("forwardTo")!=null){
            String redirectTo = getValidUriFromAnotherFunction();
            try {
                URI location = new URI(redirectTo);
                responseHeaders.setLocation(location);
            } catch (URISyntaxException e) {e.printStackTrace();}
            ResponseEntity<Void> forwardResponseEntity = new ResponseEntity<Void>(responseHeaders, HttpStatus.CREATED);                 
            return forwardResponseEntity;
        }
    };
    return new ResponseEntity<String>("aDifferentViewTemplateName", responseHeaders, HttpStatus.CREATED);
}

在浏览器的开发者工具中,请求头如下:
Host: localhost:7777
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Firefox/38.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://localhost:7777/path/to/controller_method
Cookie: JSESSIONID=911B34457B69F7729091DD97A160AD79; JSESSIONID=95AA730306330CF15E3776C495807354; XSRF-TOKEN=04ae2a0c-3c58-4e85-88bd-3818bb10402a
Connection: keep-alive

同一 POSTresponse 头信息如下:

Cache-Control: no-cache, no-store, max-age=0, must-revalidate, no-cache, no-store, max-age=0, must-revalidate
Content-Length: 0
Date: Sun, 29 May 2016 21:48:24 GMT
Expires: 0, 0
Location: http://localhost:7777/path/to/forward_destination?long_querystring
Pragma: no-cache, no-cache
Server: Apache-Coyote/1.1
X-Application-Context: application:7777, application:7777
X-Content-Type-Options: nosniff, nosniff
X-Frame-Options: DENY, DENY
X-XSS-Protection: 1; mode=block, 1; mode=block
XSRF-TOKEN: 04ae2a0c-3c58-4e85-88bd-3818bb10402a

相同的POST的Spring Boot调试日志包括三个部分,为了提高可读性已经分开如下:

显示控制器内部SYSO的调试日志部分:

11111111111111 inside POST
redirectTo is: http://localhost:7777/path/to/forward_destination?long_querystring

控制器之后的调试日志部分(最重要的部分?):

2016-05-29 14:48:24.489 DEBUG 5533 --- [io-7777-exec-10] o.s.s.w.a.ExceptionTranslationFilter     : Chain processed normally
2016-05-29 14:48:24.489 DEBUG 5533 --- [io-7777-exec-10] w.c.HttpSessionSecurityContextRepository : SecurityContext 'org.springframework.security.core.context.SecurityContextImpl@42259e42: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@42259e42: Principal: org.springframework.security.core.userdetails.User@40fecce: Username: SomeUser; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ONE,ROLE_TWO; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@fffe3f86: RemoteIpAddress: 127.0.0.1; SessionId: 02A95844E8A829868542290D471503F5; Granted Authorities: ROLE_ONE, ROLE_TWO, ROLE_THREE' stored to HttpSession: 'org.apache.catalina.session.StandardSessionFacade@64307ead
2016-05-29 14:48:24.489 DEBUG 5533 --- [io-7777-exec-10] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
1个回答

5

不要返回201 Created状态码,而应该返回3XX状态码,以请求用户代理加载不同的网页。否则,Location头部没有“特殊”的含义。

例如,您可以编写以下内容:

ResponseEntity<Void> forwardResponseEntity = new ResponseEntity<Void>(responseHeaders, HttpStatus.MOVED_PERMANENTLY);

2
3xx没错,但在这种情况下应该使用303。 - Julian Reschke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接