SpringBoot入门实践(六)-JSR-349实现请求参数验证

1,898 阅读5分钟

 我的博客:兰陵笑笑生,欢迎浏览博客!

 上一章 SpringBoot入门实践(五)-开源日志框架介绍当中,我们介绍了常用的开源日志框架。本期总结我在项目中是如何使用JSR实现请求参数的验证的。

前言

 从Spring4开始,已经实现了对JSR-349(Bean Validation,是JSR303的升级 )的全面支持,Bean Validation API在javax。validation.CONSTRAINTS 中以JAVA注解的形式定义了一组可应用于域对象的约束,另外可以使用注解开发和应用自定义验证器,如类级验证器。

一、Spring中配置Bean Validation的支持

使用方式:

  • 定义对象属性的验证约束:

public class Singer {  
    @NotNull(message = "名称不能为空")  
    @Size(min = 2, max = 5)   
    private String fistName;  
    private String lastName;   
    @NotNull(message = "性别不能为空")  
    private Genre genre;
    ....
}

  • 配置一个类型为LocalValidatorFactoryBean 的bean

package com.miroservice.chapter2.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;@Configuration@ComponentScan("com.miroservice")
public class AppConfig { 
    /**     * 默认会在类路径下搜索Hibernate validator的库     * @return     */  
    @Bean  
    LocalValidatorFactoryBean validator(){     
        return new LocalValidatorFactoryBean();  
    }
}

  • 创建一个验证服务,一旦定义了LocalValidatorFactoryBean ,既可以在应用程序任何位置注入Validator的实例

import com.miroservice.chapter2.pojo.Singer;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import javax.validation.ConstraintViolation;import javax.validation.Validator;import java.util.Set;
@Component
public class ValidateComponent {  
    /**     * 注入一个javax.validation.Validator 实例     */  
    @Autowired    
    private Validator validator;   
    
    public Set<ConstraintViolation<Singer>> validatorSinger(Singer singer) { 
        return validator.validate(singer);  
    }
}

  • 测试:

package com.miroservice.chapter2.web;
import com.miroservice.chapter2.config.AppConfig;
import com.miroservice.chapter2.pojo.Singer;
importorg.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import javax.validation.ConstraintViolation;
import java.util.Set;

public class JSR349Demo { 
public static void main(String[] args) {   
GenericApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);   
ValidateComponent validateController = ctx.getBean(ValidateComponent.class);        Singer singer = new Singer();      
    singer.setFistName("j");    
    singer.setLastName("l");     
    singer.setGenre(null);      
    validatorSinger(singer, validateController);  
}    
    private static void validatorSinger(Singer singer, 
                                        ValidateComponent validateController) {  
        
        final Set<ConstraintViolation<Singer>> constraintViolations = validateController.validatorSinger(singer);    
        listVidlations(constraintViolations);    
    }  
    
    private static void listVidlations(Set<ConstraintViolation<Singer>> set) {     
        for (ConstraintViolation<Singer> v : set) {  
            System.out.print("property:" + v.getPropertyPath() + ",value:" 
                             + v.getInvalidValue() + ";error:" + v.getMessage());  
        } 
    }
}

  • 打印结果

file

二、常用的注解解释

  • 1@Null 被注释的元素必须为 null
  • 2@NotNull 被注释的元素必须不为 null
  • 4 @AssertTrue 被注释的元素必须为 true
  • 5 @AssertFalse 被注释的元素必须为 false
  • 6 @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值7
  • @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
  • 8 @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
  • 9 @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
  • 10 @Size(max=, min=) 被注释的元素的大小必须在指定的范围内
  • 11 @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
  • 12 @Past 被注释的元素必须是一个过去的日期
  • 13 @Future 被注释的元素必须是一个将来的日期14
  • @Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式15
  • 16 Hibernate Validator 附加的 constraint
  • 17 @NotBlank(message =) 验证字符串非null,且长度必须大于0
  • 18 @Email 被注释的元素必须是电子邮箱地址
  • 19 @Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
  • 20 @NotEmpty 被注释的字符串的必须非空21
  • @Range(min=,max=,message=) 被注释的元素必须在合适的范围内

Hibernate Validator参考实现 http://hibernate.org/validator/

三、springBoot中web请求的POJO验证

使用方式:

  • 定义对象属性的验证约束:

import javax.validation.constraints.NotNull;import javax.validation.constraints.Size;
public class Singer { 
    
    @NotNull(message = "名称不能为空")    
    @Size(min = 2, max = 5)  
    private String fistName;   
    
    private String lastName; 
    
    @NotNull(message = "性别不能为空")   
    private Genre genre;
   ...   
}

  • 定义Controller

import com.miroservice.chapter2.common.HttpResponse;
import com.miroservice.chapter2.pojo.Singer;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;

@RestController
public class SingerController { 
    /**    
    * 添加@Valid 注解    
    * @param singer    
    * @return   
    */ 
    @PostMapping("/singer")    
    private String validator(@Valid @RequestBody Singer singer) {      
        System.out.print(singer);     
        return "ok"; 
}

  • 测试:请求 http://localhost:8081/singer

{
    "fistName":null,
    "genre":"FEMALE"    
}

  • 返回结果

{
    "timestamp": "2020-01-11T15:16:22.851+0000",
    "status": 400,
    "error": "Bad Request",
    "errors": [
        {
            "codes": [
                "NotNull.singer.fistName",
                "NotNull.fistName",
                "NotNull.java.lang.String",
                "NotNull"
            ],
            "arguments": [
                {
                    "codes": [
                        "singer.fistName",
                        "fistName"
                    ],
                    "arguments": null,
                    "defaultMessage": "fistName",
                    "code": "fistName"
                }
            ],
            "defaultMessage": "名称不能为空",
            "objectName": "singer",
            "field": "fistName",
            "rejectedValue": null,
            "bindingFailure": false,
            "code": "NotNull"
        }
    ],
    "message": "Validation failed for object='singer'. Error count: 1",
    "path": "/singer"
}

  • 自定义返回结果

 入参的第二个地方,绑定BindingResult,当Bean中属性不满足是,可以获取到注解上的信息,并放回到

自定义的结果中。

@PostMapping("/singer") 
private HttpResponse validator(@Valid @RequestBody Singer singer, BindingResult result) 
{   
    if (result.hasErrors()) {    
    return HttpResponse.error(result.getFieldError().getDefaultMessage());    
    }     return HttpResponse.ok();
    
}

  • 返回结果

{
    "code": 500,
    "message": "名称不能为空",
    "requestid": "eebb0bf86dc24aabb9051bf832542828"
}

四、创建自定义注解验证器

 对于一些复杂的验证,比如说手机号码的验证,需要多个条件,或者一个验证的POJO中包含了另一个类,出现了这样的需求也是常有的,这就需要我们自定义创建验证器了,以手机的长度为例,我们自定义一个手机号的验证。

  • 创建自定义注解

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Documented
//使用的验证器,自定义验证器
@Constraint(validatedBy = PhoneValidator.class)
//注解的使用级别是方法、属性上
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Phone {  
    //如果 验证经过PhoneValidator的验证返回false ,则默认返回的信息  
    String message() default "长度必须大于11";   
    /**  
    *   
    * @return 
    */  
    Class<?>[] groups() default {};  
    Class<? extends Payload>[] payload() 
        default {}
    ;

}

  • 创建验证器

package com.miroservice.chapter2.pojo;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/** 
* 
自定义实现对phone的验证逻辑
*/
public class PhoneValidator implements ConstraintValidator<Phone,String> {   
    /** 
    *  value就是属性
    *   
    */ 
    @Override  
    public boolean isValid(String value, ConstraintValidatorContext context) { 
        if(value==null||value.trim().length()<12){       
            return false;   
        }else {         
            return true;     
        }   
    }
}

  • 对象添加手机号属性并添加注解

public class Singer { 
    
@NotNull(message = "名称不能为空")  
@Size(min = 2, max = 5)  
    
private String fistName; 
    
private String lastName;  
@NotNull(message = "性别不能为空") 
    
private Genre genre;   
    
@Phone()  
private String phone;
...
}

  • 测试 http://localhost:8081/singer

 请求参数

{
    "phone" : 1,
    "fistName": "zhang",
    "genre":"MALE"
}

 请求结果

{
  "code": 500,
  "message": "长度必须大于11",
  "requestid": "38cc357c07144d098e7c5730cc8f4c08"
}

五、总结

 以上就是本期的分享,你还可以关注本博客的#Spring Boot入门实践系列!#

本文由博客一文多发平台 OpenWrite 发布!