🚩 需求描述
将接口访问所有异常进行统一处理。在业务逻辑编写时,应该将所有可预知的异常定义好,并设定唯一的错误代码,在异常发生时,通过统一的返回值自动包装,提供给前端优化的错误提示,并能够通过错误代码快速定位异常类型。
🐘 异常对象
定义一个业务层异常,用于抛出业务逻辑上的异常错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Getter @AllArgsConstructor(access = AccessLevel.PRIVATE) public class ServiceException extends RuntimeException {
public final String message;
public final String code;
public static ServiceException of(ExceptionEnum exceptionEnum) { return new ServiceException(exceptionEnum.code, exceptionEnum.message); } }
|
其中 ExceptionEnum
类是用于描述异常的枚举类型,定义异常的错误代码及错误信息。
1 2 3 4 5 6
| @AllArgsConstructor public enum ExceptionEnum { ; public final String code; public final String message; }
|
📄 代码实现
通过 @RestControllerAdive
提升作用域,通过 @ExceptionHandler
注解来处理不同的异常。
🔢 业务层异常
所有的业务逻辑错误触发的异常。
1 2 3 4 5 6 7 8 9 10
|
@ExceptionHandler(ServiceException.class) public RestResponse<String> handleServiceException(ServiceException exception) { return RestResponse.error(exception.code, exception.message); }
|
⁉ 未知异常
用于捕捉其他的所有不可预知的错误,可能是表字段的数据异常,导致数据库在更新时无法动态的生成 SQL(在编写时默认该值不为空,但被人为删除了),而产生的 dao 层异常,也有可能时系统资源被占用,导致文件无法写入的 IO 异常,不可控制的网络超时异常等等。
1 2 3 4 5 6 7 8 9 10
|
@ExceptionHandler(Exception.class) public RestResponse<String> handleOtherException(Exception exception) { return RestResponse.error(exception.getMessage()); }
|
🔐 参数校验异常
需要使用 @Validated
注解标记在 Controller 的类、方法或参数上。对于验证规则,使用 javax.validation.constraints
和 org.hibernate.validator.constraints
包中的规则注解。具体使用此处不细究,仅对异常的处理进行展开。
该异常表现为接口访问错误,但具体的错误提示信息并未写入到响应,服务端异常信息如下:
1 2
| javax.validation.ConstraintViolationException: common.msg: 消息不能为空 ...异常堆栈信息
|
可以建立一个指定异常为 ConstraintViolationException 的处理器,用于封装参数错误的异常信息。
1 2 3 4
| @ExceptionHandler(ConstraintViolationException.class) public RestResponse<String> constraintViolationExceptionHandler(ConstraintViolationException e) { return RestResponse.error(((ConstraintViolation) e.getConstraintViolations().toArray()[0]).getMessage()); }
|
其中 ConstraintViolation
可能不止一个参数校验的结果,取第(任)一个即可。
🔍 404 异常
尽管 Spring 默认实现了 /error
(即接口访问出错)情况下的处理,(在 Postman 上随手输出一个接口,模拟 404)如:
1 2 3 4 5 6 7
| { "timestamp": "2020-01-07T16:37:31.160+0000", "status": 404, "error": "Not Found", "message": "No message available", "path": "/123" }
|
但在某些时候,需要针对错误访问进行特殊的处理,此时可以实现 ErrorController
接口进行自定义拓展。
如下对 4xx、5xx 系列的错误状态进行处理:
1 2 3 4 5 6 7 8 9 10 11
| @RequestMapping("error") public RestResponse<Object> errorHandle(HttpServletRequest request) { HttpStatus status = getStatus(request); if (status.is4xxClientError()) { return RestResponse.error("客户端访问出错"); } if (status.is5xxServerError()) { return RestResponse.error("服务器访问异常"); } return RestResponse.error("未知异常"); }
|
当然,在条件分支中,对某个具体的状态做判断,可以更细致化的处理不同的访问错误。
其他方法
- 在
@Exception
中进行状态判断,对错误请求做处理;
- (未测试)在资源目录(
/resources/public/error
)下添加错误访问页面,命名为 404.html、500.html 等;
- (未测试)配置
EmbeddedServletContainerCustomizer
实现;