spring boot 全局错误处理
这两天在做 spring cloud 的 API gateway 的时候,遇到了一个全局错误处理的坑,我在在 spring security 中加入了一个 filter,该 filter 用来验证 token 是否合法,如果该 token 不合法,就抛出自定义错误 InvalidTokenException ,并且要返回的状态码为403,告诉前端该用户未认证。
我开始以为使用 @ControllerAdvice
来定义全局错误处理即可,最后发现过滤器中抛出了错误 @ControllerAdvice
却无法捕获到,并抛出我需要的错误信息。最后阅读 spring 的官方文档发现,spring 的全局错误处理不是只有 @ControllerAdvice
。
@ControllerAdvice
主要处理的就是 controller
层的错误信息,而没有进入 controller
层的错误 @ControllerAdvice
是无法处理的,那么我需要另外的一个全局错误处理。
@ControllerAdvice
public class ExceptionTranslator {
@ExceptionHandler(ConcurrencyFailureException.class)
@ResponseStatus(HttpStatus.CONFLICT)
@ResponseBody
public ErrorVM processConcurencyError(ConcurrencyFailureException ex) {
return new ErrorVM(ErrorConstants.ERR_CONCURRENCY_FAILURE);
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ErrorVM processValidationError(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
return processFieldErrors(fieldErrors);
}
@ExceptionHandler(CustomParameterizedException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ParameterizedErrorVM processParameterizedValidationError(CustomParameterizedException ex) {
return ex.getErrorVM();
}
@ExceptionHandler(InvalidTokenException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
@ResponseBody
public ErrorVM processInvalidTokenException(InvalidTokenException ex) {
return new ErrorVM(ErrorConstants.INVALID_TOKEN, ex.getMessage());
}
@ExceptionHandler(AccessDeniedException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
@ResponseBody
public ErrorVM processAccessDeniedException(AccessDeniedException e) {
return new ErrorVM(ErrorConstants.ERR_ACCESS_DENIED, e.getMessage());
}
private ErrorVM processFieldErrors(List<FieldError> fieldErrors) {
ErrorVM dto = Objects.nonNull(fieldErrors.get(0)) ? new ErrorVM(ErrorConstants.ERR_VALIDATION, fieldErrors.get(0).getDefaultMessage()) : new ErrorVM(ErrorConstants.ERR_VALIDATION);
for (FieldError fieldError : fieldErrors) {
dto.add(fieldError.getObjectName(), fieldError.getField(), fieldError.getCode());
}
return dto;
}
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
@ResponseBody
@ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
public ErrorVM processMethodNotSupportedException(HttpRequestMethodNotSupportedException exception) {
return new ErrorVM(ErrorConstants.ERR_METHOD_NOT_SUPPORTED, exception.getMessage());
}
@ExceptionHandler(CustomException.class)
@ResponseBody
@ResponseStatus(HttpStatus.IM_USED)
public ErrorVM processCustomException(CustomException ex) {
return new ErrorVM(ErrorConstants.ERR_CUSTOM, ex.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorVM> processRuntimeException(Exception ex) {
BodyBuilder builder;
ErrorVM errorVM;
ResponseStatus responseStatus = AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
if (responseStatus != null) {
builder = ResponseEntity.status(responseStatus.value());
errorVM = new ErrorVM("error." + responseStatus.value().value(), responseStatus.reason());
} else {
builder = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
errorVM = new ErrorVM(ErrorConstants.ERR_INTERNAL_SERVER_ERROR, "Internal server error");
}
return builder.body(errorVM);
}
}
BasicErrorController
这个类就是用来捕获 /error
的所有错误,而过滤器中的错误会被重定向到 /error
。我编写一个新的控制层类 TokenErrorController
并继承 BasicErrorController
类,这样错误 json 类型的错误都会被重定向到这个控制层里。
@RestController
public class TokenErrorController extends BasicErrorController {
public TokenErrorController(){
super(new DefaultErrorAttributes(), new ErrorProperties());
}
private static final String PATH = "/error";
@RequestMapping(produces = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
if (!Strings.isNullOrEmpty((String)body.get("exception")) && body.get("exception").equals(InvalidTokenException.class.getName())){
body.put("status", HttpStatus.FORBIDDEN.value());
status = HttpStatus.FORBIDDEN;
}
return new ResponseEntity<Map<String, Object>>(body, status);
}
@Override
public String getErrorPath() {
return PATH;
}
}
我的java进阶之旅
记录我的进阶之旅
82 声望
6 粉丝
推荐阅读
API编写规范
为什么设计api详情规范 主要是为了保证公司的小伙伴写出的api尽量少出bug,加快研发速度。 api 设计规范 绝大部分api都请务必加入用户id限定,主要为了防止某个用户可以请求所有的数据库数据; 管理员api,必须加...
刘祖明赞 1阅读 3.9k
Spring Aop 动态代理
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施...
KerryWu赞 5阅读 8.7k评论 1
SpringBoot集成LibreOffice+jodconverter做文件预览(office转pdf)
LibreOffice 是一款开放源代码的自由免费全能办公软件,可运行于 Microsoft Windows, GNU/Linux 以及 macOS 等操作系统上。它包含了 Writer, Calc, Impress, Draw, Math 以及 Base 等组件,可分别用于文本文档、...
Zeran赞 2阅读 6.3k
之前很火给女朋友推送微信服务号消息是怎么做的?
经过了几天的奋战,终于把微信服务号的模板消息给写完了。后端其实没花多少时间,因为之前已经有同学提过pull request了,我在这基础之上简单优化下就完事了,主要的时间都是花在前端上,对前端页面和参数的适配...
Java3y赞 3阅读 1.2k
简单使用spring cloud 服务注册做一个请求转发中心
背景上篇文章 记录多项目共用一个公众号逻辑修改, 实现了多个项目共用一个公众号。 但是也存在几点问题,比如:中间服务器拦截了微信的请求,虽然方便了项目不再需要写微信授权的代码,但如果以后需要再拓展新的...
weiweiyi赞 2阅读 777
消息推送平台终于要上线啦!
我的开源项目消息推送平台Austin终于要上线了,迎来在线演示的第一版!🔥项目在线演示地址:[链接]消息推送平台🔥推送下发【邮件】【短信】【微信服务号】【微信小程序】【企业微信】【钉钉】等消息类型。[链接][链...
Java3y赞 3阅读 1.1k
Java项目是不是分布式,真有那么重要吗?
「微服务」「分布式」在我刚毕业的时候还是比较关注的,那时候还入门了一把SpringCloud,写了一篇很长的文章,还是很顶的,有不少的大号都给我转载了,在知乎又获得了很多的赞。
Java3y赞 2阅读 586评论 1
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。