一波带走,SpringBoot 中的各种参数校验方案汇总
Posted Java知音_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一波带走,SpringBoot 中的各种参数校验方案汇总相关的知识,希望对你有一定的参考价值。
点击关注公众号,实用技术文章及时了解
1、前言
在控制器类的方法里自己写校验逻辑代码当然也可以,只是代码比较丑陋,有点“low”。业界有更好的处理方法,分别阐述如下。
2、PathVariable校验
@GetMapping("/path/group:[a-zA-Z0-9_]+/userid")
@ResponseBody
public String path(@PathVariable("group") String group, @PathVariable("userid") Integer userid)
return group + ":" + userid;
用法是:路径变量:正则表达式
。
当请求URI不满足正则表达式时,客户端将收到404错误码。不方便的地方是,不能通过捕获异常的方式,向前端返回统一的、自定义格式的响应参数。
3、方法参数校验
@GetMapping("/validate1")
@ResponseBody
public String validate1(
@Size(min = 1,max = 10,message = "姓名长度必须为1到10")@RequestParam("name") String name,
@Min(value = 10,message = "年龄最小为10")@Max(value = 100,message = "年龄最大为100") @RequestParam("age") Integer age)
return "validate1";
如果前端传递的参数不满足规则,则抛出异常。注解Size、Min、Max来自validation-api.jar
,更多注解参见相关标准小节。
4、表单对象/VO对象校验
当参数是VO时,可以在VO类的属性上添加校验注解。
public class User
@Size(min = 1,max = 10,message = "姓名长度必须为1到10")
private String name;
@NotEmpty
private String firstName;
@Min(value = 10,message = "年龄最小为10")@Max(value = 100,message = "年龄最大为100")
private Integer age;
@Future
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Date birth;
。。。
其中,Future注解要求必须是相对当前时间来讲“未来的”某个时间。
@PostMapping("/validate2")
@ResponseBody
public User validate2(@Valid @RequestBody User user)
return user;
5、自定义校验规则
5.1 自定义注解校验
需要自定义一个注解类和一个校验类。
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER,ElementType.FIELD)
@Constraint(validatedBy = FlagValidatorClass.class)
public @interface FlagValidator
// flag的有效值,多个使用,隔开
String values();
// flag无效时的提示内容
String message() default "flag必须是预定义的那几个值,不能随便写";
Class<?>[] groups() default ;
Class<? extends Payload>[] payload() default ;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class FlagValidatorClass implements ConstraintValidator<FlagValidator,Object>
/**
* FlagValidator注解规定的那些有效值
*/
private String values;
@Override
public void initialize(FlagValidator flagValidator)
this.values = flagValidator.values();
/**
* 用户输入的值,必须是FlagValidator注解规定的那些值其中之一。
* 否则,校验不通过。
* @param value 用户输入的值,如从前端传入的某个值
*/
@Override
public boolean isValid(Object value, ConstraintValidatorContext constraintValidatorContext)
// 切割获取值
String[] value_array = values.split(",");
Boolean isFlag = false;
for (int i = 0; i < value_array.length; i++)
// 存在一致就跳出循环
if (value_array[i] .equals(value))
isFlag = true; break;
return isFlag;
使用我们自定义的注解:
public class User
// 前端传入的flag值必须是1或2或3,否则校验失败
@FlagValidator(values = "1,2,3")
private String flag ;
。。。
5.2 分组校验
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
public class Resume
public interface Default
public interface Update
@NotNull(message = "id不能为空", groups = Update.class)
private Long id;
@NotNull(message = "名字不能为空", groups = Default.class)
@Length(min = 4, max = 10, message = "name 长度必须在 min - max 之间", groups = Default.class)
private String name;
@NotNull(message = "年龄不能为空", groups = Default.class)
@Min(value = 18, message = "年龄不能小于18岁", groups = Default.class)
private Integer age;
。。。
/**
* 使用Defaul分组进行验证
* @param resume
* @return
*/
@PostMapping("/validate5")
public String addUser(@Validated(value = Resume.Default.class) @RequestBody Resume resume)
return "validate5";
/**
* 使用Default、Update分组进行验证
* @param resume
* @return
*/
@PutMapping("/validate6")
public String updateUser(@Validated(value = Resume.Update.class, Resume.Default.class) @RequestBody Resume resume)
return "validate6";
建立了两个分组,名称分别为Default、Update。POST方法提交时使用Defaut分组的校验规则,PUT方法提交时同时使用两个分组规则。
6、异常拦截器
通过设置全局异常处理器,统一向前端返回校验失败信息。
import com.scj.springbootdemo.WebResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;
/**
* 全局异常处理器
*/
@ControllerAdvice
public class GlobalExceptionHandler
private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 用来处理bean validation异常
* @param ex
* @return
*/
@ExceptionHandler(ConstraintViolationException.class)
@ResponseBody
public WebResult resolveConstraintViolationException(ConstraintViolationException ex)
WebResult errorWebResult = new WebResult(WebResult.FAILED);
Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
if(!CollectionUtils.isEmpty(constraintViolations))
StringBuilder msgBuilder = new StringBuilder();
for(ConstraintViolation constraintViolation :constraintViolations)
msgBuilder.append(constraintViolation.getMessage()).append(",");
String errorMessage = msgBuilder.toString();
if(errorMessage.length()>1)
errorMessage = errorMessage.substring(0,errorMessage.length()-1);
errorWebResult.setInfo(errorMessage);
return errorWebResult;
errorWebResult.setInfo(ex.getMessage());
return errorWebResult;
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseBody
public WebResult resolveMethodArgumentNotValidException(MethodArgumentNotValidException ex)
WebResult errorWebResult = new WebResult(WebResult.FAILED);
List<ObjectError> objectErrors = ex.getBindingResult().getAllErrors();
if(!CollectionUtils.isEmpty(objectErrors))
StringBuilder msgBuilder = new StringBuilder();
for (ObjectError objectError : objectErrors)
msgBuilder.append(objectError.getDefaultMessage()).append(",");
String errorMessage = msgBuilder.toString();
if (errorMessage.length() > 1)
errorMessage = errorMessage.substring(0, errorMessage.length() - 1);
errorWebResult.setInfo(errorMessage);
return errorWebResult;
errorWebResult.setInfo(ex.getMessage());
return errorWebResult;
7、相关标准
JSR 303 是Bean验证的规范 ,Hibernate Validator
是该规范的参考实现,它除了实现规范要求的注解外,还额外实现了一些注解。
validation-api-1.1.0.jar
包括如下约束注解:
hibernate-validator-5.3.6.jar
包括如下约束注解:
8、参数校验原理
这篇文章 写得比较深入,我没有太理解。
https://blog.csdn.net/wt_better/article/details/84635010
9、同时校验2个或更多个字段/参数
常见的场景之一是,查询某信息时要输入开始时间和结束时间。显然,结束时间要≥开始时间。可以在查询VO类上使用自定义注解。划重点:@ValidAddress
使用在类上。
@ValidAddress
public class Address
@NotNull
@Size(max = 50)
private String street1;
@Size(max = 50)
private String street2;
@NotNull
@Size(max = 10)
private String zipCode;
@NotNull
@Size(max = 20)
private String city;
@Valid
@NotNull
private Country country;
// Getters and setters
public class Country
@NotNull
@Size(min = 2, max = 2)
private String iso2;
// Getters and setters
@Documented
@Target(TYPE)
@Retention(RUNTIME)
@Constraint(validatedBy = MultiCountryAddressValidator.class )
public @interface ValidAddress
String message() default "com.example.validation.ValidAddress.message";
Class<?>[] groups() default ;
Class<? extends Payload>[] payload() default ;
public class MultiCountryAddressValidator
implements ConstraintValidator<ValidAddress, Address>
public void initialize(ValidAddress constraintAnnotation)
@Override
public boolean isValid(Address address,
ConstraintValidatorContext constraintValidatorContext)
Country country = address.getCountry();
if (country == null || country.getIso2() == null || address.getZipCode() == null)
return true;
switch (country.getIso2())
case "FR":
return // Check if address.getZipCode() is valid for France
case "GR":
return // Check if address.getZipCode() is valid for Greece
default:
return true;
来源:blog.csdn.net/jinjiankang/article/details/89711493
推荐
PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!
以上是关于一波带走,SpringBoot 中的各种参数校验方案汇总的主要内容,如果未能解决你的问题,请参考以下文章