项目中使用了全局异常处理,如下
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
public static final String RESPONSE_ERROR = "Error";
@Autowired
MailPublisher mailPublisher;
/**
* Exception handler
*
* @param ex
* @param request
* @return
*/
@ExceptionHandler(value = {Exception.class})
public ResponseEntity handleExceptions(Exception ex, WebRequest request) {
logger.error("Exception", ex);
// Send email to administrator 如果这里出现异常呢?
mailPublisher.publishMailToAdministrator(ex);
return handleExceptionInternal(ex, RESPONSE_ERROR, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request);
}
}
但是如果异常处理中抛出了新的异常呢?这样就会导致返回的response不是我们定义的RESPONSE_ERROR字符串常量,而是默认的JSON格式:
{
"timestamp": 1531362962859,
"status": 500,
"error": "Internal Server Error",
"message": "test exception",
"path": "/app/test"
}
或者如果我们还保持这种格式,但是想添加一些字段呢?
这两个问题是相关的,解决了问题2也就解决了问题1,那先看问题2。默认的error处理在BasicErrorController
,默认异常URL是“/error”,这里有针对html和json的处理,如果需要其他响应格式的,比如plain/text,可以自己写个ErrorController。
/**
* Basic global error {@link Controller}, rendering {@link ErrorAttributes}. More specific
* errors can be handled either using Spring MVC abstractions (e.g.
* {@code @ExceptionHandler}) or by adding servlet
* {@link AbstractServletWebServerFactory#setErrorPages server error pages}.
*
* @author Dave Syer
* @author Phillip Webb
* @author Michael Stummvoll
* @author Stephane Nicoll
* @see ErrorAttributes
* @see ErrorProperties
*/
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
@RequestMapping(produces = "text/html")
public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map model = Collections.unmodifiableMap(getErrorAttributes(
request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);
return (modelAndView != null ? modelAndView : new ModelAndView("error", model));
}
@RequestMapping
@ResponseBody
public ResponseEntity
我们可以自己写个ErrorController来替换默认BasicErrorController
来自定义响应内容:
@Controller
public class AppErrorController implements ErrorController {
@Override
public String getErrorPath() {
return "/error";
}
@RequestMapping("/error")
public ResponseEntity handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
HttpStatus httpStatus;
if (status != null) {
Integer statusCode = Integer.valueOf(status.toString());
httpStatus = HttpStatus.valueOf(statusCode);
} else {
httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
}
return new ResponseEntity<>(RESPONSE_ERROR, httpStatus);
}
}
或者在配置类中自定义 ErrorAttributes
,添加自定义字段,默认返回的JSON中就会有自定义的字段。
@Bean
public ErrorAttributes errorAttributes() {
return new DefaultErrorAttributes() {
@Override
public Map getErrorAttributes(
RequestAttributes requestAttributes,
boolean includeStackTrace) {
Map errorAttributes = super.getErrorAttributes(requestAttributes, includeStackTrace);
Object errorMessage = requestAttributes.getAttribute(RequestDispatcher.ERROR_MESSAGE, RequestAttributes.SCOPE_REQUEST);
if (errorMessage != null) {
errorAttributes.put("message", errorMessage);
}
return errorAttributes;
}
};
}
针对问题1,除了自定义ErrorController外,还可以在异常处理中添加try-catch来处理。
参考:
https://blog.jdriven.com/2016/06/spicy-spring-custom-error-json-response-with-errorattributes/
https://github.com/spring-projects/spring-boot/issues/1731