影子的知识库

影子的知识库

  • 知识库
  • GitHub

›Java系列

JVM系列

  • JVM内存区域
  • 对象创建-布局-访问
  • 内存溢出实战
  • 内存区域回收
  • 四大引用
  • 垃圾回收算法
  • HotSpot回收算法细节

Java系列

  • java注解
  • springboot请求参数绑定
  • springboot请求参数校验框架
  • YAML语法
  • 动态代理
  • classpath和java命令
  • springboot-aop编程
  • springboot统一异常处理
  • springboot数据库和事务
  • springboot拦截器
  • springboot中的web配置
  • docker的简单开发
  • springboot自动配置
  • 数据库的隔离级别
  • springboot监控
  • java类加载
  • java-agent的相关内容
  • 类加载器详解
  • java的SecurityManager
  • maven学习

Node

    JS 基础

    • 语法基础和数据类型
    • 数据类型转换
    • 语句 表达式 运算符
    • 变量与对象
    • 函数
    • 数据处理
    • 常用 API
    • 重点知识

    ES6

    • 块级作用域
    • 字符串和正则表达式
    • 函数
    • 对象
    • Symbol
    • Set和Map
    • 迭代器和生成器
    • 类
    • 数组
    • Promise

    Node 基础

    • 模块系统
    • package.json
    • 内置对象
    • npm脚本的使用
    • Buffer
    • Stream
    • 事件循环机制
    • 示例代码

    stream系列

    • 流的缓冲
    • 可读流
    • 可写流
    • 双工流和转换流
    • 自定义流

后期计划

  • 学习计划
  • 专题研究计划
Edit

本文内容

步骤

一、定义统一返回信息

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GlobalApiResponse<T> {
    private static final String SUCCESS_CODE = "success";

    private String code;
    private String msg;
    private T data;

    public static <T> GlobalApiResponse<T> success(T result) {
        return new GlobalApiResponse<>(SUCCESS_CODE, null, result);
    }
}

我们所有的响应信息都遵循这个格式

我们的 code 应该是 success、xxx error 这种简短的错误信息,而不应该是一个数字,code 应该能够简单的体现出错误的种类。

msg 则是错误的进一步的具体信息,比如:xxx 地方发生了 xx 错误,原因是 xx

二、定义统一异常类

public enum BusinessExceptionEnum {
    DEMO_ERROR("用来测试的demo异常", "demo error", "这是一个demo错误,原因是:%s"),
    PARAMETER_INVALID_ERROR("参数校验不通过的时候抛出此异常","parameter invalid","%s"),
    MAX_UPLOAD_SIZE_ERROR("上传的文件超过大小限制时抛出此异常","max upload size limited",
                                  "请求或者文件大小超过限制值:%s"),
    INTERNAL_SYSTEM_ERROR("内部系统异常,正常来讲不应该抛出这个异常,一旦发现这个异常应该在统一异常处理中添加新种类的异常",
            "internal system error","系统内部发生异常");

    // info信息说明这个异常发生的时机
    private String info;
    private String code;
    private String msg;

    BusinessExceptionEnum(String info, String code, String msg) {
        this.info = info;
        this.code = code;
        this.msg = msg;
    }

    public BusinessException toException(Object... args) {
        String detailMsg = String.format(msg, args);
        return new BusinessException(code, detailMsg);
    }

    @Getter
    @Setter
    @AllArgsConstructor
    public static class BusinessException extends RuntimeException {
        private String code;
        private String msg;
    }
}

我们所有的异常都要包装成 BusinessException

通过 DEMO_ERROR.toException("some message") 的方式来生成新的 BusinessException

三、定义统一异常处理

@Slf4j
@ControllerAdvice
public class GlobalExceptionAdvice {

    /**
     * 处理通用的自定义业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public BusinessException handleBusinessException(BusinessException e, HttpServletRequest request) {
        log.info("处理 BusinessException,URL:【{}】, 原因:【{}】", request.getRequestURI(), e.getMsg());
        return e;
    }

    /**
     * 处理参数校验异常
     * 使用 @Validated 注解在 controller 类上时校验非 Model 类型的参数失败时的异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public BusinessException handleMethodArgumentNotValidException(MethodArgumentNotValidException e,
                                                                   HttpServletRequest request) {
        log.info("处理 MethodArgumentNotValidException,URL:【{}】, 原因:【{}】",
                request.getRequestURI(), e);
        String errorMsg = e.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(error -> String.format("请求参数【%s】%s", error.getField(), error.getDefaultMessage()))
                .collect(Collectors.joining(","));
        return PARAMETER_INVALID_ERROR.toException(errorMsg);
    }

    /**
     * 处理参数绑定时的异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BindException.class)
    public BusinessException handleBindException(BindException e, HttpServletRequest request) {
        log.info("处理 BindException,URL:【{}】, 原因:【{}】",
                request.getRequestURI(), e);
        String errorMsg = e.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(error -> String.format("请求参数【%s】%s", error.getField(), error.getDefaultMessage()))
                .collect(Collectors.joining(","));
        return PARAMETER_INVALID_ERROR.toException(errorMsg);
    }

    /**
     * 处理参数绑定时的异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public BusinessException handleConstraintViolationException(ConstraintViolationException e,
                                                                HttpServletRequest request) {
        log.info("处理 ConstraintViolationException,URL:【{}】, 原因:【{}】",
                request.getRequestURI(), e);
        String errorMsg = e.getConstraintViolations()
                .stream()
                .map(constraintViolation -> {
                    String propertyPath = constraintViolation.getPropertyPath().toString();
                    if (propertyPath.contains(".")) {
                        String[] propertyPathArr = propertyPath.split("\\.");
                        propertyPath = propertyPathArr[propertyPathArr.length - 1];
                    }
                    return String.format("请求参数【%s】%s", propertyPath, constraintViolation.getMessage());
                })
                .collect(Collectors.joining(","));
        return PARAMETER_INVALID_ERROR.toException(errorMsg);
    }

    /**
     * 处理参数转换时的异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public BusinessException handleHttpMessageNotReadableException(HttpMessageNotReadableException e,
                                                                   HttpServletRequest request) {
        log.info("处理 HttpMessageNotReadableException,URL:【{}】, 原因:【{}】",
                request.getRequestURI(), e);
        return PARAMETER_INVALID_ERROR.toException("请求参数异常,请检查参数类型");
    }

    /**
     * 处理参数转换时的异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public BusinessException handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e,
                                                                       HttpServletRequest request) {
        log.info("处理 MethodArgumentTypeMismatchException,URL:【{}】, 原因:【{}】",
                request.getRequestURI(), e);
        return PARAMETER_INVALID_ERROR.toException("请求参数异常,请检查参数类型");
    }

    /**
     * 处理上传文件超出限制的异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public BusinessException handleMaxUploadSizeExceededException(MaxUploadSizeExceededException e,
                                                                  HttpServletRequest request) {
        log.info("处理 MaxUploadSizeExceededException,URL:【{}】, 原因:【{}】",
                request.getRequestURI(), e);

        return MAX_UPLOAD_SIZE_ERROR.toException(e.getMaxUploadSize() + " 字节");
    }

    /**
     * 处理其它异常
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(Throwable.class)
    public BusinessException handleThrowable(Throwable e,
                                             HttpServletRequest request) {
        log.info("处理 Throwable,URL:【{}】, 原因:【{}】",
                request.getRequestURI(), e);
        return INTERNAL_SYSTEM_ERROR.toException();
    }


}

四、定义统一响应处理

@ControllerAdvice
public class GlobalResponseAdvice implements ResponseBodyAdvice<Object> {
    private static final String API_FALG = "/";

    // 定义在哪些方法上支持统一响应
    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType,
                                  Class<? extends HttpMessageConverter<?>> aClass,
                                  ServerHttpRequest serverHttpRequest,
                                  ServerHttpResponse serverHttpResponse) {
        if (o instanceof BusinessException) {
            serverHttpResponse.setStatusCode(HttpStatus.BAD_REQUEST);
            BusinessException businessException = (BusinessException) o;
            return new GlobalApiResponse<>(businessException.getCode(), businessException.getMsg(), null);
        }
        if(o instanceof String){
            String str = (String) o;
            ObjectMapper mapper = new ObjectMapper();
            return mapper.writeValueAsString(GlobalApiResponse.success(str));
        }
        if (serverHttpRequest.getURI().toString().contains(API_FALG)) {
            return GlobalApiResponse.success(o);
        }
        return o;
    }
}

我们的统一响应会作用在包含 API_FALG 的 URL 返回值上

Last updated on 11/8/2020
← springboot-aop编程springboot数据库和事务 →
  • 步骤
    • 一、定义统一返回信息
    • 二、定义统一异常类
    • 三、定义统一异常处理
    • 四、定义统一响应处理
影子的知识库
Docs
Getting Started (or other categories)Guides (or other categories)API Reference (or other categories)
Community
User ShowcaseStack OverflowProject ChatTwitter
More
BlogGitHub
Copyright © 2020 Cen ZhiPeng