我正在使用 @ControllerAdvice 进行全局异常处理。但如果请求是 ajax(xhr),我想以 Json 格式返回异常,如果不是,我想将异常显示为 html。
所以响应类型是动态的。它可能是 html 或 json。我正在使用 thymleaf 来生成我的 html 页面。
如何在 Spring Boot 中做到这一点?
@ResponseBody
@ExceptionHandler(Exception.class)
public ModelAndView handle(Exception exception, WebRequest request) {
this.logException(exception, request);
var model = getErrorPageModel(exception);
return new ModelAndView("exception", model, HttpStatus.OK);
}
从
WebRequest
,您必须检索必要的信息以返回 JSON 或 HTML。一种可能的方法是解析 Accept
标头:
List<MediaType> mediaTypes = MediaType.parseMediaTypes(request.getHeader("Accept"));
if (mediaTypes.contains(MediaType.TEXT_HTML)) {
// TODO: implement
} else {
// TODO: implement
}
ContentNegotiationManager
:
// TODO: Autowire a bean of `ContentNegotiationManager` into your ControllerAdvice class
List<MediaType> mediaTypes = contentNegotiationManager.resolveMediaTypes(request);
if (mediaTypes.contains(MediaType.TEXT_HTML)) {
// TODO: implement
} else {
// TODO: implement
}
现在您要做的就是为 HTML 和 JSON 视图实现模型和视图。
对于 HTML 视图,您可以使用 Thymeleaf 模板,例如
error.html
:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
<p><strong>Error:</strong> <span th:text="${message}"></span></p>
</body>
之后,您可以从异常处理程序返回以下
ModelAndView
:
return new ModelAndView("error", Map.of("message", ex.getMessage()));
对于 JSON 部分,您可以使用
MappingJackson2JsonView
视图类:
// TODO: Autowire a bean of `ObjectMapper` into your ControllerAdvice class
return new ModelAndView(new MappingJackson2JsonView(objectMapper), Map.of("message", ex.getMessage()));
一切结合起来你会得到:
@ExceptionHandler(Exception.class)
public ModelAndView handleException(IllegalArgumentException ex, NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
List<MediaType> mediaTypes = contentNegotiationManager.resolveMediaTypes(request);
Map<String, String> model = Map.of("message", ex.getMessage());
if (mediaTypes.contains(MediaType.TEXT_HTML)) {
return new ModelAndView("error", model);
} else {
return new ModelAndView(new MappingJackson2JsonView(objectMapper), model);
}
}
注意:在此示例中,我假设如果
标头包含Accept
,您希望返回 HTML 视图,而在其他情况下,您希望返回 JSON 视图。您可以将此逻辑更改为您想要的任何内容。text/html