全局异常处理还是基于AOP的思想,在SpringMVC
中实现起来很简单,参照如下代码,其中的Resp
是自定义的统一响应格式类,可自行设计,RespType
是自定义的响应信息常量。
注意:异常捕获的顺序是按照定义的先后顺序来的,由上到下,所以应先定义细节异常。
/**
* 统一异常处理
*/
@RestControllerAdvice
public class ControllerExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(ControllerExceptionHandler.class);
/**
* 400 - Bad Request 参数绑定出错
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(BindException.class)
public Resp<String> handleBindException(BindException e) {
logger.error("绑定参数出错", e);
return new Resp<String>().error(RespType.BIND_PARAM_ERROR);
}
/**
* 400 - Bad Request
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMessageNotReadableException.class)
public Resp<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
logger.error("请求参数读取错误", e);
return new Resp<String>().error(RespType.BAD_REQUEST);
}
/**
* 400 - Bad Request
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({MethodArgumentNotValidException.class})
public Resp<String> handleValidationException(MethodArgumentNotValidException e) {
logger.error("请求参数验证失败", e);
return new Resp<String>().error(RespType.BAD_REQUEST);
}
/**
* 405 - Method Not Allowed。HttpRequestMethodNotSupportedException
* 是ServletException的子类,需要Servlet API支持
*/
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public Resp<String> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
logger.error("请求方法不支持", e);
return new Resp<String>().error(RespType.METHOD_NOT_ALLOWED);
}
/**
* 415 - Unsupported Media Type。HttpMediaTypeNotSupportedException
* 是ServletException的子类,需要Servlet API支持
*/
@ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
@ExceptionHandler({HttpMediaTypeNotSupportedException.class})
public Resp<String> handleHttpMediaTypeNotSupportedException(Exception e) {
logger.error("content-type类型不支持", e);
return new Resp<String>().error(RespType.UNSUPPORTED_MEDIA_TYPE);
}
/**
* 500 - Internal Server Error
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public Resp<String> handleException(Exception e) {
logger.error("服务器内部错误", e);
return new Resp<String>().error(RespType.INTERNAL_SERVER_ERROR);
}
}
如果你使用了验证框架,还可用以下代码进行参数验证异常的处理:
/**
* 处理@Validated参数验证的异常
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(ConstraintViolationException.class)
public ApiResponse handleConstraintViolationException(ConstraintViolationException e) {
log.error("参数验证出错", e);
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
StringBuilder stringBuilder = new StringBuilder();
for (ConstraintViolation<?> violation : violations) {
stringBuilder.append(violation.getMessage()).append(";");
}
return ApiResponse.error("400", stringBuilder.toString());
}
/**
* 处理@Valid参数验证错误
*/
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
log.error("参数验证出错", e);
BindingResult result = e.getBindingResult();
StringBuilder stringBuilder = new StringBuilder();
for (ObjectError error : result.getAllErrors()) {
stringBuilder.append(error.getDefaultMessage()).append(";");
}
return ApiResponse.error("400", stringBuilder.toString());
}
总结
通过切面抓取到不同异常后,进行一系列的自定义处理,比如返回特定结构的提示信息,这样比起默认的返回内容,有更好的展现形式,使我们的接口更加优美。