头图

前言

前后端分离开发,经常用json数据格式进行交互,统一的响应数据格式,形成统一规范,可以大大方便前后端人员的开发。

Response数据封装

  • 编码枚举类 ResultCode.java
public enum ResResultCode {

    /**
     * 成功
     */
    SUCCESS(200, "ok"),

    /**
     * 请求错误
     */
    BAD_REQUEST(400, "请求参数错误"),

    /**
     * 未登录授权
     */
    UNAUTHORIZED(401, "无效令牌"),

    /**
     * 权限不足
     */
    FORBIDDEN(403, "权限不足"),

    /**
     * 服务器错误
     */
    FAILED(500, "server error"),

    /**
     * 业务异常
     */
    BIZ_ERROR(600, "业务异常");

    private int status;
    private String message;

    ResResultCode(int status, String message) {
        this.status = status;
        this.message = message;
    }

    public int getStatus() {
        return status;
    }

    public String getMessage() {
        return message;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
  • json数据封装类
@Data
public class ResResult {

    /**
     * 响应编码
     */
    private int code;
    /**
     * 响应信息
     */
    private String msg;
    /**
     * 响应数据
     */
    private Object data;

    public ResResult(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public ResResult(int code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public static ResResult success() {
        return new ResResult(ResResultCode.SUCCESS.getStatus(), ResResultCode.SUCCESS.getMessage(), null);
    }

    public static ResResult success(Object data) {
        return new ResResult(ResResultCode.SUCCESS.getStatus(), ResResultCode.SUCCESS.getMessage(), data);
    }

    public static ResResult failure() {
        return new ResResult(ResResultCode.FAILED.getStatus(), ResResultCode.FAILED.getMessage());
    }

    public static ResResult failure(ResResultCode ResResultCode) {
        return new ResResult(ResResultCode.getStatus(), ResResultCode.getMessage());
    }

}

自定义异常类

  • BizErrorException.java 用来抛出业务异常
public class BizErrorException extends RuntimeException {

    private ResResultCode resResultCode;
    private String message;

    public BizErrorException(ResResultCode resResultCode) {
        this.resResultCode = resResultCode;
    }

    public BizErrorException(ResResultCode resResultCode, String message) {
        this.resResultCode = resResultCode;
        this.message = message;
    }

    public ResResultCode getResResultCode() {
        return resResultCode;
    }

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
  • BadRequestException.java 用来抛出请求参数异常
public class BadRequestException extends RuntimeException {

    public BadRequestException() {

    }

    public BadRequestException(String message) {
        super(message);
    }
}

全局请求参数校验

虽然spring提供了@Valid可以进行请求参数的校验

注意一个坑:hibernate-validator在springboot 2.3之后就需要手动引入了,不再自动配置。

<!-- springboot 2.3之后消失的hibernate-validator解决方法-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

作为一个前后端分离项目,并且接口并不对外暴露,后端每个接口调用都创建一个vo类,显然是非常麻烦的,这边就不用这种方式

这里就选择了一个偷懒的方法,就是利用Knife4j动态请求参数 再加上AOP 进行校验。具体实现步骤:

  1. 引入AOP依赖
<!-- AOP -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
  1. 新建一个aop类 ReqValidateAop.java

在执行每个@DynamicParameters注解修饰的接口是进行校验,对每个required = true 的参数进行校验。

/**
 * 对含有@DynamicParameters动态参数的接口 进行请求参数校验
 */
@Aspect
@Component
@Slf4j
public class ReqValidateAop {

    /**
     * 所有方法: @Pointcut("execution(* com.ify.formwork.web.controller.*.*.*(..))")
     * 含有@DynamicParameters注解:@Pointcut("@annotation(com.github.xiaoymin.knife4j.annotations.DynamicParameters)")
     */
    @Pointcut("@annotation(com.github.xiaoymin.knife4j.annotations.DynamicParameters)")
    public void paramValidate() {
    }

    @Before("paramValidate()")
    public void before(JoinPoint joinPoint) throws BadRequestException {
        List<String> errList = new ArrayList<>();
        Object[] objects = joinPoint.getArgs();
        JSONObject param = (JSONObject) objects[0];
        // 获取当前方法
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        // 获取方法@DynamicParameters properties
        DynamicParameters annotation = method.getAnnotation(DynamicParameters.class);
        List<DynamicParameter> list = Arrays.asList(annotation.properties());
        for (DynamicParameter dynamicParameter : list) {
            // 必传参数校验
            if (dynamicParameter.required()) {
                Object o = param.get(dynamicParameter.name());
                // 请求参数为null 或请求参数为字符串为空串
                boolean badRequest = (null == o) || (o instanceof String && StrUtil.isEmpty((String) o));
                if (badRequest) {
                    errList.add(dynamicParameter.name() + "不能为空");
                }
            }
        }
        if (!errList.isEmpty()) {
            throw new BadRequestException(StrUtil.join(",", errList));
        }
    }
}

全局异常拦截处理

对服务抛出的异常进行接管,统一处理返回给前端

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /***
     * 功能描述:  捕获请求参数异常 ReqValidateAop.java 自定义校验方式
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BadRequestException.class)
    public ResResult handleBadRequestException(BadRequestException e) {
        return new ResResult(ResResultCode.BAD_REQUEST.getStatus(), e.getMessage());
    }

    /**
     * 功能描述:  捕获请求参数异常 @valid校验参数方式 (如果采用这种方式的话可以进行使用)
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResResult handleBadRequestException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        List<String> errors = new ArrayList<>();
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            errors.add(fieldError.getField() + fieldError.getDefaultMessage());
        }
        return new ResResult(ResResultCode.BAD_REQUEST.getStatus(), StrUtil.join("|", errors));
    }

    /**
     * 业务异常捕获 统一返回500状态,具体信息封装在msg里
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(BizErrorException.class)
    public ResResult handleBizErrorException(BizErrorException e) {
        if (StrUtil.isNotEmpty(e.getMessage())) {
            e.getResResultCode().setMessage(e.getMessage());
        }
        return ResResult.failure(e.getResResultCode());
    }

    /**
     * 功能描述: 异常统一返回, 统一返回500状态
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public ResResult handleException(Exception e) {
        log.error("server error = {}", e.getMessage());
        return ResResult.failure();
    }
}

后端的统一数据封装差不多就这样了。。。


方一杠
9 声望1 粉丝

弃我去者,昨日之日不可留; 乱我心者,今日之日多烦忧。