在项目整体架构设计的时候,我们经常需要做以下工作:
- 返回值的统一封装处理,因为只有规范好统一的返回值格式,才能不会给接口使用者带来疑惑和方便前端对接口的统一处理。
- 对异常码进行严格规定,什么错误返回什么码制,这样前端就可以根据不同的码制进行错误提示,同时也方便三方调用者对异常进行处理。要注意,微服务架构,不同的微服务之间的码制前缀要做区分,这样我们就可以根据码制很快定位到是哪个微服务出现问题。
- 对异常的统一拦截,这一点非常重要,因为一旦对异常没有做统一处理,严重就会导致堆栈乃至sql信息暴露给前端,这是非常严重的事故。
所以今天我会给大家展示如何对返回值进行统一封装,如何对异常进行统一拦截,异常码定义,根据自己的业务自行定义即可。
1. 返回值的封装
分为两步,首先定义返回值类型和字段,然后定义BaseController。
1.1 响应信息主体
我定义的这个涵盖了我们要返回的各种情况,大家可以参考
/**
 * 响应信息主体
 */
@Data
@NoArgsConstructor
public class R<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 成功
     */
    public static final int SUCCESS = 200;
    /**
     * 失败
     */
    public static final int FAIL = 500;
    private int code;
    private String msg;
    private T data;
    public static <T> R<T> ok() {
        return restResult(null, SUCCESS, "操作成功");
    }
    public static <T> R<T> ok(T data) {
        return restResult(data, SUCCESS, "操作成功");
    }
    public static <T> R<T> ok(String msg) {
        return restResult(null, SUCCESS, msg);
    }
    public static <T> R<T> ok(String msg, T data) {
        return restResult(data, SUCCESS, msg);
    }
    public static <T> R<T> fail() {
        return restResult(null, FAIL, "操作失败");
    }
    public static <T> R<T> fail(String msg) {
        return restResult(null, FAIL, msg);
    }
    public static <T> R<T> fail(T data) {
        return restResult(data, FAIL, "操作失败");
    }
    public static <T> R<T> fail(String msg, T data) {
        return restResult(data, FAIL, msg);
    }
    public static <T> R<T> fail(int code, String msg) {
        return restResult(null, code, msg);
    }
    /**
     * 返回警告消息
     *
     * @param msg 返回内容
     * @return 警告消息
     */
    public static <T> R<T> warn(String msg) {
        return restResult(null, HttpStatus.WARN, msg);
    }
    /**
     * 返回警告消息
     *
     * @param msg 返回内容
     * @param data 数据对象
     * @return 警告消息
     */
    public static <T> R<T> warn(String msg, T data) {
        return restResult(data, HttpStatus.WARN, msg);
    }
    private static <T> R<T> restResult(T data, int code, String msg) {
        R<T> r = new R<>();
        r.setCode(code);
        r.setData(data);
        r.setMsg(msg);
        return r;
    }
    public static <T> Boolean isError(R<T> ret) {
        return !isSuccess(ret);
    }
    public static <T> Boolean isSuccess(R<T> ret) {
        return R.SUCCESS == ret.getCode();
    }
}
1.2 BaseController 的写法
**
 * web层通用数据处理
 */
public class BaseController {
    /**
     * 响应返回结果
     *
     * @param rows 影响行数
     * @return 操作结果
     */
    protected R<Void> toAjax(int rows) {
        return rows > 0 ? R.ok() : R.fail();
    }
    /**
     * 响应返回结果
     *
     * @param result 结果
     * @return 操作结果
     */
    protected R<Void> toAjax(boolean result) {
        return result ? R.ok() : R.fail();
    }
	//同时也可以将一些常用的方法放在这里,比如获取登录用户信息
}
1.3 示例
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/category")
public class WfCategoryController extends BaseController {
 @GetMapping("/eventTest")
    public R<Void> eventTest(){
        applicationEventPublisher.publishEvent(new UserChangePasswordEvent("11111"));
        return R.ok();
    }
}
返回值:
 
2. 全局异常拦截
全局异常拦截的本质实际上采用的是spring的AOP拦截实现的,然后根据异常类型映射到不同的ExceptionHandler处理方法上。
2.1 引入AOP依赖
必须引入AOP依赖,我刚开始忘了依赖,怎么试都不对,所以一定要检查好,不要忘了。
        <!-- SpringBoot 拦截器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
2.2 写异常拦截器
在写异常拦截器的时候,我们得要分为这么几个部分,首先是自定义的业务异常拦截,验证类异常,比如参数校验类异常拦截处理,还有未知的运行时异常拦截,如果使用到Mybatis,还得要注意Mybatis系统异常的通用拦截处理,还有就是请求不支持异常,最后再是统一的大异常拦截。
可以看我下面写的案例:
/**
 * 全局异常处理器
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 请求方式不支持
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public R<Void> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
                                                       HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
        return R.fail(e.getMessage());
    }
    /**
     * Mybatis系统异常 通用处理
     */
    @ExceptionHandler(MyBatisSystemException.class)
    public R<Void> handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        String message = e.getMessage();
        if (message.contains("CannotFindDataSourceException")) {
            log.error("请求地址'{}', 未找到数据源", requestURI);
            return R.fail("未找到数据源,请联系管理员确认");
        }
        log.error("请求地址'{}', Mybatis系统异常", requestURI, e);
        return R.fail(message);
    }
    /**
     * 业务异常
     */
    @ExceptionHandler(ServiceException.class)
    public R<Void> handleServiceException(ServiceException e, HttpServletRequest request) {
        log.error(e.getMessage(), e);
        Integer code = e.getCode();
        return ObjectUtil.isNotNull(code) ? R.fail(code, e.getMessage()) : R.fail(e.getMessage());
    }
    /**
     * 拦截未知的运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public R<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生未知异常.", requestURI, e);
        return R.fail(e.getMessage());
    }
    /**
     * 系统异常
     */
    @ExceptionHandler(Exception.class)
    public R<Void> handleException(Exception e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生系统异常.", requestURI, e);
        return R.fail(e.getMessage());
    }
    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public R<Void> handleBindException(BindException e) {
        log.error(e.getMessage(), e);
        String message = StreamUtils.join(e.getAllErrors(), DefaultMessageSourceResolvable::getDefaultMessage, ", ");
        return R.fail(message);
    }
    /**
     * 自定义验证异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public R<Void> constraintViolationException(ConstraintViolationException e) {
        log.error(e.getMessage(), e);
        String message = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ", ");
        return R.fail(message);
    }
    /**
     * 自定义验证异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error(e.getMessage(), e);
        String message = e.getBindingResult().getFieldError().getDefaultMessage();
        return R.fail(message);
    }
}
运行返回显示
 
异常统一处理和返回值封装就到此结束了,上面的稍微修改下,完全可以做生产用,希望大家多多点赞关注支持,后续给大家分享更多有用的知识点!!!



















