Spring MVC 3.2 - 错误页面的内容协商?

Way*_*neC 12 java spring spring-mvc content-negotiation

要全局处理可能在Controller外部发生的错误(例如HTTP 404),我的web.xml中的条目类似于以下内容:

<error-page>
    <error-code>404</error-code>
    <location>/errors/404</location>
</error-page>
Run Code Online (Sandbox Code Playgroud)

在我的ErrorController中,我有类似于以下的相应方法:

@Controller
@RequestMapping("/errors")
public class ErrorController {

    @RequestMapping(value = "/404", method = RequestMethod.GET)
    @ResponseBody
    public ResponseEntity<ErrorResponse> error404() {

        ErrorResponse errorBody = new ErrorResponse(404, "Resource Not Found!");

        return new ResponseEntity<ErrorResponse>(errorBody, HttpStatus.NOT_FOUND);
    }
}
Run Code Online (Sandbox Code Playgroud)

我面临的问题是ContentNegotiationManager我配置的和消息转换器在这种情况下没有被使用.我怀疑由于请求被重定向到错误页面,因此内容协商中使用的原始请求属性将丢失,并将其视为完全独立的请求.(即原始请求/mycontroller/badresource.json - >/errors/404(没有文件扩展名))

在这样的错误处理程序中是否有任何方法确定和/或使用原始请求中请求的相应内容类型进行响应?

Way*_*neC 3

我为此想出了一些技巧,但它似乎有效。它基本上涉及错误处理中的额外转发以确定原始请求的文件扩展名。

在我的 web.xml 中,我将错误转发到中间操作:

<error-page>
    <error-code>404</error-code>
    <location>/errors/redirect</location>
</error-page>
Run Code Online (Sandbox Code Playgroud)

然后,在转发到将生成错误响应的操作之前,将检查原始请求是否有文件扩展名。如果有,它会确保将其附加到转发 URI。HTTP 标头会自动转发,因此如果您设置的内容协商仅涉及文件扩展名或 HTTP 标头,这将有效地允许“错误页面”以适当的内容类型返回错误。

@Controller
@RequestMapping("/errors")
public class ErrorController {

    @RequestMapping(value = "/redirect", method = RequestMethod.GET)
    public void errorRedirect(HttpServletRequest request, HttpServletResponse response) {

        // Get original request URI
        String uri = (String)request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE);

        // Try to determine file extension
        String filename = WebUtils.extractFullFilenameFromUrlPath(uri);
        String extension = StringUtils.getFilenameExtension(filename);
        extension = StringUtils.hasText(extension) ? "." + extension : "";

        // Forward request to appropriate handler with original request's file extension (i.e. /errors/404.json)
        String forwardUri = "/errors/404" + extension); 
        request.getRequestDispatcher(forwardUri).forward(request, response);
    }

    @RequestMapping(value = "/404", method = RequestMethod.GET)
    @ResponseBody
    public ResponseEntity<ErrorResponse> error404() {

        ErrorResponse errorBody = new ErrorResponse(404, "Resource Not Found!");

        return new ResponseEntity<ErrorResponse>(errorBody, HttpStatus.NOT_FOUND);
    }
}
Run Code Online (Sandbox Code Playgroud)