数据校验

04数据校验


1.业务层校验

首先我们来看一下最常见的做法,就是在业务层进行参数校验:

public String addUser(User user) {
    if (user == null || user.getId() == null || user.getAccount() == null || user.getPassword() == null || user.getEmail() == null) {
    return "对象或者对象字段不能为空";
    }
}

代码繁琐不够优雅。

2.SpringMvc数据校验:

在SpringMvc中做数据校验有2中方式,一种是 Spring 自带的验证框架,另外一种是利用 JSR 实现。JSR 是一个规范,提供了完整得一套 API,通过标注给对象属性添加约束。Hibernate Validator 就是 对JSR 规范所有注解的具体实现,以及一些附加的约束注解。用户还可以自定义约束注解。

Validator提供得注解如下:

注解作用目标检查规则
@Length(min=, max=)属性(String)检查字符串长度是否符合范围
@Max(value=)属性(以 numeric 或者 string 类型来表示一个数字)检查值是否小于或等于最大值
@Min(value=)属性(以 numeric 或者 string 类型来表示一个数字)检查值是否大于或等于最小值
@NotNull(value=)属性检查值是否非空(not null)
@NotEmpty(value=)属性(String,Object,Collection)检查值不能是null 不能是空字符 集合框架中的元素不能为空
@NotBlank(value=)属性(String)检查值不能是null或不能是空字符
@Future属性(date 或 calendar)检查日期是否是未来
@Pattern(regex=”regexp”, flag=)属性(string)检查属性是否与给定匹配标志的正则表达式相匹配
@Range(min=, max=)属性(以 numeric 或者 string 类型来表示一个数字)检查值是否在最小和最大值之间(包括临界值)
@Size(min=, max=)属性(array,collection,map)检查元素大小是否在最小和最大值之间(包括临界值)
@AssertFalse属性检查方法的演算结果是否为 false(对以代码方式而不是注解表示的约束很有用)
@AssertTrue属性检查方法的演算结果是否为 true(对以代码方式而不是注解表示的约束很有用)
@Valid属性(object)对关联对象递归进行验证。如果对象是集合或数组,就递归地验证其元素;如果对象是 Map,则递归验证其值元素
@Email属性(String)检查字符串是否符合有效的 email 地址规范
@Past属性(date 或 calendar)检查日期是否是过去
3.SpringBoot对数据校验的支持

默认提供的参数校验依赖于 hibernate-validator来实现。使用 Hibernate Validator校验数据,需要定义一个接收的数据模型,使用注解的形式描述字段校验的规则。

@Data
public class User {
    @NotNull(message = "用户id不能为空")
    private Long id;
    @NotNull(message = "用户账号不能为空")
    @Size(min = 6, max = 11, message = "账号长度必须是6-11个字符")
    private String account;
    @NotNull(message = "用户密码不能为空")
    @Size(min = 6, max = 11, message = "密码长度必须是6-16个字符")
    private String password;
    @NotNull(message = "用户邮箱不能为空")
    @Email(message = "邮箱格式不正确")
    private String email;
}
  • 在pom.xml文件中引入依赖:
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.16.Final</version>
</dependency>
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>
  • 实体类,对相关属性字段通过注解添加校验规则

  • Validator + 自动抛出异常

    参数上加上@Valid注解即可完成校验

    @PostMapping("/addUser")
    public String addUser(@RequestBody @Valid User user) {
    return userService.addUser(user);
    }
    
  • 全局异常处理

    参数校验失败会自动引发异常,SpringBoot全局异常处理:

    类上加上@ControllerAdvice@RestControllerAdvice注解

    @RestControllerAdvice
    public class ExceptionControllerAdvice {
    @ExceptionHandler(MethodArgumentNotValidException.class)
        public String MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
            // 从异常对象中拿到ObjectError对象
            ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
            // 然后提取错误提示信息进行返回
            return objectError.getDefaultMessage();
        }
    }
    
4.数据统一响应

现在我们规范好了参数校验方式和异常处理方式,然而还没有规范响应数据!比如我要获取一个分页信息数据,获取成功了呢自然就返回的数据列表,获取失败了后台就会响应异常信息,即一个字符串,就是说前端开发者压根就不知道后端响应过来的数据格式!所以,统一响应数据是前后端规范中必须要做的!

  • 自定义统一响应体

统一数据响应第一步肯定要做的就是自定义一个响应体类,无论后台是运行正常还是发生异常,响应给前端的数据格式是不变的!

更优雅的设计异常响应体:

/**
 * 通用返回对象
 */
@ApiModel(description = "结果对象")
@Data
@ToString
public class CommonResult<T> {
    @ApiModelProperty(value = "状态码",required = true)
    private int code;
    @ApiModelProperty(value = "消息",required = true)
    private String message;
    @ApiModelProperty(value = "数据",required = true)
    private T data;
    protected CommonResult() {
    }
    protected CommonResult(int code, String message) {
        this.code = code;
        this.message = message;
    }
    protected CommonResult(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }
    /**
     * 成功返回结果
     *
     */
    public static  CommonResult success() {
        return new CommonResult(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage());
    }
    /**
     * 成功返回结果
     *
     * @param data 获取的数据
     */
    public static <T> CommonResult<T> success(T data) {
        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
    }
    /**
     * 成功返回结果
     *
     * @param data 获取的数据
     * @param  message 提示信息
     */
    public static <T> CommonResult<T> success(T data, String message) {
        return new CommonResult<T>(ResultCode.SUCCESS.getCode(), message, data);
    }
    /**
     * 失败返回结果
     * @param code 错误码
     */
    public static <T> CommonResult<T> failed(IErrorCode errorCode) {
        return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage());
    }
    /**
     * 失败返回结果
     * @param errorCode 错误码
     * @param message 错误信息
     */
    public static <T> CommonResult<T> failed(int code,String message) {
        return new CommonResult<T>(code, message);
    }
    /**
     * 失败返回结果
     * @param message 提示信息
     */
    public static <T> CommonResult<T> failed(String message) {
        return new CommonResult<T>(ResultCode.FAILED.getCode(), message);
    }
    /**
     * 失败返回结果
     */
    public static <T> CommonResult<T> failed() {
        return failed(ResultCode.FAILED);
    }
    /**
     * 参数验证失败返回结果
     */
    public static <T> CommonResult<T> validateFailed() {
        return failed(ResultCode.VALIDATE_FAILED);
    }
    /**
     * 参数验证失败返回结果
     * @param message 提示信息
     */
    public static <T> CommonResult<T> validateFailed(String message) {
        return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message);
    }
    /**
     * 参数校验失败
     * @param data   详细信息
     * @param <T>
     * @return
     */
    public static <T> CommonResult<T> methodArgumentNotValid(T  data){
        return new CommonResult<T>(ResultCode.METHOD_ARGUMENT_NOT_VALID.getCode(),ResultCode.METHOD_ARGUMENT_NOT_VALID.getMessage(),data);
    }
    /**
     * 未登录返回结果
     */
    public static <T> CommonResult<T> unauthorized() {
        return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage());
    }
    /**
     * 未授权返回结果
     */
    public static <T> CommonResult<T> forbidden(T data) {
        return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage());
    }
    /**
     * 请求方法被禁止返回结果
     */
    public static <T> CommonResult<T> validateMethodFailed() {
        return new CommonResult<T>(ResultCode.VALIDATE_METHOD.getCode(), ResultCode.VALIDATE_METHOD.getMessage());
    }
}
  • 响应码枚举
/**
 * 枚举了一些常用API操作码
 */
@ToString
public enum ResultCode implements IErrorCode {
    SUCCESS(200, "操作成功"),
    VALIDATE_FAILED(400, "Invalid input(无效的输入)"),
    UNAUTHORIZED(401, "暂未登录或token已经过期"),
    FORBIDDEN(403, "403 Forbidden(请求被拒绝)没有相关权限"),
    NOTFOUND(404, "not found(没有找到相关资源)"),
    VALIDATE_METHOD(405, "请求方法被禁止"),
    FAILED(500, "服务器内部错误"),
    METHOD_ARGUMENT_NOT_VALID(10000,"参数校验失败"),
    REPETITIVE_OPERATION(10001, "请勿重复操作"),
    ACCESS_LIMIT(10002, "请求太频繁, 请稍后再试");
    private int code;
    private String message;
    private ResultCode(int code, String message) {
        this.code = code;
        this.message = message;
    }
    @Override
    public int getCode() {
        return code;
    }
    @Override
    public String getMessage() {
        return message;
    }
}
  • 封装API的错误码
/**
 * 封装API的错误码
 */
public interface IErrorCode {
    int getCode();
    String getMessage();
}
  • 设置全局异常处理的响应码:
@RestControllerAdvice
@Slf4j
public class GlobalExceptionAdvice {
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public CommonResult exceptionHandler(HttpRequestMethodNotSupportedException e) {
        log.error("Exception: ", e);
        return CommonResult.validateMethodFailed();
    }
    @ExceptionHandler(Exception.class)
    public CommonResult exceptionHandler(Exception e) {
        log.error("Exception: ", e);
        return CommonResult.failed(ResultCode.FAILED.getMessage());
    }
    @ExceptionHandler(BindException.class)
    public CommonResult bindExceptionHandler(BindException e) {
        log.error("BindException: ", e);
        return CommonResult.validateFailed();
    }
    @ExceptionHandler(CustomException.class)
    public CommonResult customExceptionHandler(CustomException e) {
        log.error("CustomException: ", e);
        return CommonResult.failed("响应失败:"+e.getMessage());
    }
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public CommonResult methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) {
        log.error("MethodArgumentNotValidException: ", e);
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        return CommonResult.methodArgumentNotValid(objectError.getDefaultMessage());
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值