spring使用自带的参数校验的详细教程

引入依赖

​ 如果spring-boot版本小于2.3.x,spring-boot-starter-web会自动传入hibernate-validator依赖。如果spring-boot版本大于2.3.x,则需要手动引入依赖:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.1.Final</version>
</dependency>

全局处理异常

​ 我们做项目的时候,有时候请求会返回http400或者500的状态码,可能是参数校验失败或者程序本身抛出异常,其实我们系统要求无论发送什么异常,http的状态码必须返回200,由业务码和异常信息去区分系统的异常情况。在实际的项目开发中,通常会用统一异常处理来返回一个更友好的提示。

​ 我们先定义一个Result,统一所有接口的返回结果结构

mport java.io.Serializable;

import com.fasterxml.jackson.annotation.JsonProperty;




public class Result<T> implements Serializable {

    /**
     *
     */
    private static final long serialVersionUID = 1326625485688697147L;

    public static final int ERROR = -1;

    public static final int SUCCESS = 0;

//    @ApiModelProperty("状态码(可选)")
    private int status;

//    @ApiModelProperty("提示消息")
    private String msg;

//    @ApiModelProperty("响应数据")
    private T data;

    //@JsonCreator
    protected Result(@JsonProperty("status") int status, @JsonProperty("msg") String msg, @JsonProperty("data") T data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }

    public static <T> Result<T> create(int statusCode, String message, T data) {
        return new Result<>(statusCode, message, data);
    }

    public static <T> Result<T> success(T data, String msg) {

        return create(SUCCESS, msg, data);
    }

    public static <T> Result<T> success(T data) {

        return create(SUCCESS, "操作成功", data);
    }

    public static <T> Result<T> success(String message) {

        return create(SUCCESS, message, null);
    }

    public static <T> Result<T> error(String message) {

        return create(ERROR, message, null);
    }

    public static <T> Result<T> error() {

        return create(ERROR, "操作失败", null);
    }
    public String getMsg() {
        return msg;
    }

    public T getData() {
        return data;
    }

    public int getStatus() {
        return status;
    }

    @Override
    public String toString() {
        return "Result{" + "data=" + data + ", status=" + status + ", msg='" + msg + '\'' + '}';
    }

}

​ 其次,定义GlobalExceptionHandler 全局处理异常类,一般参数校验失败会抛出MethodArgumentNotValidException或者ConstraintViolationException异常,我们就会捕获这些异常,去做统一的异常处理

mport org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolationException;

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler({MethodArgumentNotValidException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
    	LOGGER.error(e.getMessage(), e);
        BindingResult bindingResult = ex.getBindingResult();
        StringBuilder sb = new StringBuilder("校验失败:");
        for (FieldError fieldError : bindingResult.getFieldErrors()) {
            sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage()).append(", ");
        }
        String msg = sb.toString();
        return Result.error(msg);
    }

    @ExceptionHandler({ConstraintViolationException.class})
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Result handleConstraintViolationException(ConstraintViolationException ex) {
    	LOGGER.error(e.getMessage(), e);
        return Result.error( ex.getMessage());
    }
    
    @ExceptionHandler(Exception.class)
    public Result exception(Exception e) {
        // 打印出错误日志
        LOGGER.error(e.getMessage(), e);
        return Result.error("这是一个全局处理异常 ,"+e.getMessage());
    }

}

微信截图_20220823133733

基本校验

​ 需要在contrller类加@Validated注解,然后在方法参数上加@NotBlank等各种参数校验注解

@RestController
@RequestMapping(value = "/test")
@Validated
public class TestController {

    @GetMapping(value = "/test1")
    public Result test1(@RequestParam @NotBlank @Length(min = 2, max = 10) String param1,@RequestParam @NotNull @Min(0) @Max(100) Integer param2){
        return Result.success("aaa");
    }
    
    @PostMapping(value = "/test3")
    public  Result test3(@RequestBody UserSearchDto userSearchDto) throws Exception{
        return Result.success("ccccc");
    }

}

​ 一般post请求的话,@RequestBody请求参数是一个对象,我们也可以在dto里面加上注解

@Data
public class UserSearchDto {

    

    @ApiModelProperty(value = "年龄")
    @NotNull
    @Min(0) 
    @Max(150)
    private Integer age;

    /*分页参数*/
    @ApiModelProperty(value = "当前页")
    private String currentPage;

    @ApiModelProperty(value = "页大小")
    private String pageSize;


}

​ 下面在列一下常用的注解

注解作用类型来源说明
@Null任何类型属性必须为null
@NotNull任何类型属性不能为null
@NotEmpty集合hibernate validator扩展注解集合不能为null,且size大于0
@NotBlank字符串、字符hibernate validator扩展注解字符类不能为null,且去掉空格之后长度大于0
@AssertTrueBoolean、boolean布尔属性必须是true
@Min数字类型(原子和包装)限定数字的最小值(整型)
@Max同@Min限定数字的最大值(整型)
@DecimalMin同@Min限定数字的最小值(字符串,可以是小数)
@DecimalMax同@Min限定数字的最大值(字符串,可以是小数)
@Range数字类型(原子和包装)hibernate validator扩展注解限定数字范围(长整型)
@Length(min=,max=)字符串hibernate validator扩展注解限定字符串长度
@Size集合限定集合大小
@Past时间、日期必须是一个过去的时间或日期
@Future时期、时间必须是一个未来的时间或日期
@Email字符串hibernate validator扩展注解必须是一个邮箱格式
@Pattern字符串、字符正则匹配字符串

集合校验

​ 如果我们需要批量新增的时候,如果我们直接使用java.util.Collection下的list或者set来接收数据,参数校验并不会生效!我们可以使用自定义list集合来接收参数。包装List类型,并声明@Valid注解

public class ValidationList<E> implements List<E> {

    @Delegate // @Delegate是lombok注解
    @Valid // 一定要加@Valid注解
    public List<E> list = new ArrayList<>();

    // 一定要记得重写toString方法
    @Override
    public String toString() {
        return list.toString();
    }
}

​ @Delegate注解受lombok版本限制,1.18.6以上版本可支持。如果校验不通过,会抛出NotReadablePropertyException,同样可以使用统一异常进行处理。

比如,我们需要一次性保存多个对象,Controller层的方法可以这么写:

@PostMapping(value = "/saveList")
    public  Result test4(@RequestBody @Validated({Insert.class}) ValidationList<FireHistoryForestryAddDto> list) {
        return Result.success("ccccc");
    }

分组校验

​ 比如说有时侯新增接口和更新接口用的是同一个dto,但是只有更新的时候id字段才需要验证,这个时候就需要分组验证

​ 定义分组Insert接口(新增接口使用),如何类推

public interface Insert {
}

微信截图_20220823164519

​ 然后再接口指定Insert.class

@PostMapping(value = "/test3")
public  Result test3(@RequestBody @Validated({Insert.class}) FireHistoryForestryAddDto dto) throws Exception{
    return Result.success("ccccc");
}

​ 在参数校验处指定Insert.class

@Data
public class FireHistoryForestryAddDto {

    /**
     * 编号
     */
    @NotBlank(groups = { Update.class, Delete.class}, message = "编号不能为空")
    private String id;

    @NotBlank(groups = {Insert.class}, message = "火灾名称不能为空")
    private String name;

    /**
     * 行政编码
     */
    @NotBlank(groups = {Insert.class}, message = "行政编码不能为空")
    private String districtCode;

}

自定义校验

​ 当自带的校验规则不满足,我们所需时,我们可以自定义校验规则,比如我们需要一个日期格式的校验器,这个需要两步。

​ 首先,自定义约束注解

/**
 * 判断时间类型字符串是否正确【注解】
 *
 * @author: Jreffer
 * @date: 2022年3月7日13:12:52
 */
@Target({TYPE, ANNOTATION_TYPE, FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {DateCorrectValidator.class})
public @interface DateCorrect {

    boolean required() default true;

    String message() default "日期格式错误";

    String value() default "";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    String format() default "yyyy-MM-dd";
}

​ 实现ConstraintValidator接口编写约束校验器

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.text.SimpleDateFormat;

/**
 * 判断日期类型【校验器】
 *
 * @author: Jreffer
 * @date: 2022年3月7日13:12:52
 */
public class DateCorrectValidator implements ConstraintValidator<DateCorrect, String> {
    private DateCorrect dateTime;

    @Override
    public void initialize(DateCorrect dateTime) {
        this.dateTime = dateTime;
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // 如果 value 为空则不进行格式验证,为空验证可以使用 @NotBlank @NotNull @NotEmpty 等注解来进行控制,职责分离
        if (value == null) {
            return true;
        }
        String format = dateTime.format();
        if (value.length() != format.length()) {
            return false;
        }
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);

        try {
            simpleDateFormat.parse(value);
        } catch (Exception e) {
            return false;
        }
        return true;
    }
}

​ 这样我们就可以使用@DateCorrect注解了

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值