栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Springboot 参数校验

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Springboot 参数校验

Springboot 参数校验

​ Java API规范(JSR303)定义了Bean校验的标准 validation-api,但没有提供实现。hibernate validation 是对这个规范的实现,并增加了校验注解如 @Email、@Length 等。

​ Spring Validation 是对 hibernate validation 的二次封装,用于支持 spring mvc 参数自动校验。

引入依赖

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


    org.hibernate
    hibernate-validator
    6.0.1.Final

requestBody 参数校验

​ POST、PUT请求一般会使用requestBody传递参数,这种情况下,只要给 DTO 对象加上 @Validated 注解就能实现自动参数校验。

​ 比如,有一个保存Brand的接口,要求 name 长度是不能超过30字符。

@Data
public class BrandDto {

    private Long id;

    @NotBlank(message = "品牌名不能为空!")
    @Length(max = 30, message = "品牌名太长了!")
    private String name;

    private String chineseSpell;

    private String englishName;
}


@PostMapping("/brand")
public Result saveBrand(@RequestBody @Valid BrandDto brand){
    return brandService.saveBrand(brand);
}

​ 校验失败,会抛出 MethodArgumentNotValidException 异常,Spring默认会将其转为400(Bad Request)请求。


requestParam/PathVariable 参数校验

​ 如果是 GET 请求一般会使用 requestParam/PathVariable 传参,此时要在类上标注 @Validated 注解,并在入参上声明约束注解。

@RestController
@RequestMapping("/v1/web/brand")
@Validated
public class BrandController {
 	@Autowired
    private BrandService brandService;

 	
   @GetMapping("/brand")
    public Result getBrand(@RequestParam("id")
                               @NotNull(message = "id不能为空!")
                               @Max(value = 10000, message = "id超过最大范围了!") Long id){
        return brandService.getBrand(id);
    }
}

​ 校验失败,会抛出 ConstraintViolationException 异常:

统一异常处理

​ 如果校验失败,会抛出 MethodArgumentNotValidException 或者 ConstraintViolationException 异常。在实际项目开发中,通常会用统一异常处理根据业务情况来返回一个更友好的提示:

// 等价于 @ControllerAdvice + @ResponseBody
@RestControllerAdvice  
public class GlobalExectionHandler {


    
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException exception){
        BindingResult bindingResult = exception.getBindingResult();
        List fieldErrorList =  bindingResult.getFieldErrors();
        int size = fieldErrorList.size();

        StringBuilder sb = new StringBuilder("参数校验失败:");
        for (int i = 0; i < size; i++) {
            FieldError fieldError = fieldErrorList.get(i);
            sb.append(fieldError.getField()).append(":").append(fieldError.getDefaultMessage());
            if(i != (size-1)){
                sb.append(", ");
            }
        }
        String msg = sb.toString();
        return Result.parameterError().setMessage(msg);
    }


    
    @ExceptionHandler({ConstraintViolationException.class})
    public Result handleConstraintViolationException(ConstraintViolationException exception) {
        return Result.parameterError().setMessage("参数校验失败:" + exception.getMessage());
    }
}

快速失败

​ Spring Validation 默认会校验完所有字段,然后才抛出异常。可以开启 Fali Fast 模式,一旦校验失败就立即返回。

@Configuration
public class ValidateConfig {

    
    @Bean
    public Validator validator() {
        ValidatorFactory validatorFactory = Validation.byProvider(Hibernatevalidator.class)
                .configure()
                // 快速失败模式
                .failFast(true)
                .buildValidatorFactory();
        return validatorFactory.getValidator();
    }
}

嵌套校验

​ 实际场景中,可能存在一个实体类中还嵌套着另外的实体类,即一个对象的某个属性是对另一个对象的引用。这种情况下可以使用嵌套校验,此时,该属性字段上必须加上 @Valid注解,下面是一个具体的例子:

@Data
public class BrandDto {

    private Long id;

    @NotBlank(message = "品牌名不能为空!")
    @Length(max = 30, message = "品牌名太长了!")
    private String name;

    private String chineseSpell;

    private String englishName;
    
    
    @Valid
    private EnterpriseBrandDto enterpriseBrandDto;

}


@Data
public class EnterpriseBrandDto {
    private Long id;

    @NotNull(message = "企业账号id不能为空!")
    private Long enterpriseId;

    @NotNull(message = "品牌id不能为空!")
    private Long brandId;

    private Date createDate;

    private Date updateDate;
}



@RestController
@RequestMapping("/v1/web/brand")
public class BrandController {
    
    @PostMapping("/brand")
    public Result saveBrand(@RequestBody @Valid BrandDto brand){
        return brandService.saveBrand(brand);
    }
}

集合校验

​ 如果请求体直接传递了json 数组给后台,并希望对数组中的每一项都进行参数校验。此时要使用自定义 list 集合来接收参数:

@Data
public class ValidList implements List {

    @Delegate
    @Valid
    public List list = new ArrayList<>();
}


@RestController
@RequestMapping("/v1/web/brand")
public class BrandController {
   
    @PostMapping("/brands")
    public Result saveBrands(@RequestBody @Valid ValidList brands){
        return brandService.saveBrands(brands);
    }
}

自定义校验

​ spring validation 支持扩展,可以根据实际业务场景,定制化校验规则。

​ 自定义校验主要分两步进行:

​ 1、自定义校验注解:

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@documented
public @interface BrandName {

    
    String message() default "品牌名不合法!";

    
    Class[] groups() default {};

    
    Class[] payload() default {};

}

​ 2、实现 ConstraintValidator 接口编写校验器,制定校验规则:

public class BrandNamevalidator implements ConstraintValidator {
    private String value;
    private static List brandList = new ArrayList<>();

    
    static{
        brandList.add("H&M");
        brandList.add("范思哲");
        brandList.add("纪梵希");
        brandList.add("施华洛世奇");
        brandList.add("杜嘉班纳");
    }


    
    @Override
    public void initialize(BrandName brandName) {
        this.value = brandName.message();
    }


    
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(StringUtils.isEmpty(value)){
            return false;
        }

        if(brandList.contains(value)){
            // 禁止默认消息返回
            context.disableDefaultConstraintViolation();

            //自定义返回消息
            context.buildConstraintViolationWithTemplate("拒绝与辱华品牌合作,谢谢理解!").addConstraintViolation();
            return false;
        }
        return true;
    }
}

​ 3、在自定义注解上面指定使用的校验器类(通过@Constraint 指定):

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@documented

@Constraint(validatedBy = {BrandNamevalidator.class})
public @interface BrandName{

    String message() default "品牌名不合法!";

    Class[] groups() default {};

    Class[] payload() default {};
}

​ 4、使用自定义校验注解:

@Data
public class BrandDto {

    private Long id;

    
    @BrandName
    private String name;
}

​ 5、运行结果如下:

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/582854.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号