要全局处理可能在Controller外部发生的错误(例如HTTP 404),我的web.xml中的条目类似于以下内容:
<error-page>
<error-code>404</error-code>
<location>/errors/404</location>
</error-page>
在我的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);
}
}
我面临的问题是我配置的ContentNegotiationManager
和消息转换器在这种情况下没有被使用。我怀疑由于请求被重定向到错误页面,因此内容协商中使用的原始请求属性将丢失,并将其视为完全独立的请求。 (即原始请求/mycontroller/badresource.json - &gt; / errors / 404(没有文件扩展名))
在这样的错误处理程序中是否有任何方法确定和/或使用原始请求中请求的相应内容类型进行响应?
答案 0 :(得分:3)
我为此想出了一些黑客,但似乎有效。它基本上涉及错误处理中的额外转发,以确定原始请求的文件扩展名。
在我的web.xml中,我将错误转发给了一个中间操作:
<error-page>
<error-code>404</error-code>
<location>/errors/redirect</location>
</error-page>
然后,在转发到将生成错误响应的操作之前,将进行检查以查看原始请求上是否存在文件扩展名。如果有,则确保将其附加到转发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);
}
}
答案 1 :(得分:3)
Spring MVC 3.2现在包含一个名为@ControllerAdvice
的有用注释。您可以添加一个ExceptionHandler
方法,该方法将全局处理您定义的任何异常。
对我来说,我只关心两种可能的内容类型,以便返回客户端 - application/json
或text/html
。
我将如何设置它 -
@ControllerAdvice
public class ExceptionControllerAdvice {
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private static final MediaType JSON_MEDIA_TYPE = new MediaType("application", "json", DEFAULT_CHARSET);
//I decided to handle all exceptions in this one method
@ExceptionHandler(Throwable.class)
public @ResponseBody String handleThrowable(HttpServletRequest request, HttpServletResponse response, Throwable ex) throws IOException {
...
if(supportsJsonResponse(request.getHeader("Accept"))) {
//return response as JSON
response.setStatus(statusCode);
response.setContentType(JSON_MEDIA_TYPE.toString());
//TODO serialize your error in a JSON format
//return ...
} else {
//return as HTML
response.setContentType("text/html");
response.sendError(statusCode, exceptionMessage);
return null;
}
}
private boolean supportsJsonResponse(String acceptHeader) {
List<MediaType> mediaTypes = MediaType.parseMediaTypes(acceptHeader);
for(MediaType mediaType : mediaTypes) {
if(JSON_MEDIA_TYPE.includes(mediaType)) {
return true;
}
}
return false;
}
}
答案 2 :(得分:2)
确实,应用程序Exceptions和HTTP Response错误代码是两回事。
您可以调整以下代码,以便访问requestUri
。
我想你可以找到基于它的内容类型。我知道它的原油,但我认为我们没有替代解决方案:
@RequestMapping(value = "/404", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<ErrorResponse> error404(HttpServletRequest request) {
ErrorResponse errorBody = new ErrorResponse(404, "Resource Not Found!");
String requestUri = request.getRequestURI();
return new ResponseEntity<ErrorResponse>(errorBody, HttpStatus.NOT_FOUND);
}
从示例中我假设您的应用程序是REST服务,那么您可能可以在REST完整服务中引用this link on how 404 is handled。