方式一:使用hibernate-validator注解方式参数校验
类似的框架太多了。缺点:返回的提示信息顺序不太确定
文档:https://hibernate.org/validator/documentation/
参考资料:https://blog.csdn.net/weixin_45080272/article/details/128413908
<dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId>
</dependency>
① 在实体类的字段上添加参数校验的注解,如@Max、@Min、@NotNull等校验规则
/*** 模板类型:1-同城寄,2-省内寄,3-经济区互寄,4-跨省*/
@ApiModelProperty(value = "模板类型:1-同城寄,2-省内寄,3-经济区互寄,4-跨省", required = true)
@Max(value = 4, message = "类型值必须是1、2、3、4")
@Min(value = 1, message = "类型值必须是1、2、3、4")
@NotNull(message = "模板类型不能为空")
private Integer templateType;
② 在对应controller类上添加@@Validated 注解,表示开启参数校验
@Slf4j
@Validated // 开启参数校验
@RestController
@Api(tags = "运费管理")
@RequestMapping("/carriages")
public class CarriageController {......
}
对于表单、url参数校验,在controller方法入参上添加校验规则:
常见的校验注解:
- @Null 被注释的元素必须为 null
- @NotNull 被注释的元素必须不为 null
- @AssertTrue 被注释的元素必须为 true
- @AssertFalse 被注释的元素必须为 false
- @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
- @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
- @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
- @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
- @Size(max=, min=) 被注释的元素的大小必须在指定的范围内
- @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
- @Past 被注释的元素必须是一个过去的日期
- @Future 被注释的元素必须是一个将来的日期
- @Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
- Hibernate Validator 提供的校验注解:
- @NotBlank(message =) 验证字符串非null,且长度必须大于0
- @Email 被注释的元素必须是电子邮箱地址
- @Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
- @NotEmpty 被注释的字符串的必须非空
- @Range(min=,max=,message=) 被注释的元素必须在合适的范围内
对于controller方法入参的实体类数进行校验:(需要在方法形参前面添加@Valid注解,这样实体类里的字段校验规则才能生效)
@PostMapping
public CarriageDTO saveOrUpdate(@Valid @RequestBody CarriageDTO carriageDto) {return carriageService.saveOrUpdate(carriageDto);
}
但是这样操作太耦合了,每次使用都要往形参对象上加注解。
补充:使用AOP切面方式,在方法入参时对请求体对象进行参数校验
/*** 请求参数校验切面,统一对Controller中@RequestBody映射的对象进行校验,在Controller方法中无需单独处理*/
@Aspect // 声明当前类为切面
@Slf4j
@EnableAspectJAutoProxy // 开启AOP注解功能
@Component
public class ValidatedAspect {@Resourceprivate Validator validator; //javax提供的参数校验/*** 定义切面---在controller方法入参时对请求体对象进行参数校验(校验规则在实体类中)** @param proceedingJoinPoint 切入点对象* @return* @throws Throwable*/// @Around: 环绕通知,可以在目标方法执行前后进行一些处理,方法参数与原方法一致, 返回值类型与原方法返回值类型一致// 切点表达式: "execution(* com.sl..controller.*Controller.*(..))",匹配所有controller方法@Around("execution(* com.sl..controller.*Controller.*(..))")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {// 获取@RequestBody映射的对象:请求体对象Object body = AspectUtil.getBody(proceedingJoinPoint);// 不为空的body进行拦截校验if (!ObjectUtil.isEmpty(body)) {// 进行校验// validator.validate(body):验证实体对象中的所有约束,他把校验失败的字段信息封装成一个Set集合返回Set<ConstraintViolation<Object>> validateResult = validator.validate(body);if (CollUtil.isNotEmpty(validateResult)) {//如果校验结果不为空,表示没有通过校验,则抛出异常,由统一异常处理机制进行处理,响应400String info = JSONUtil.toJsonStr(validateResult.stream().map(ConstraintViolation::getMessage).collect(Collectors.toList()));throw new SLException(info, HttpStatus.BAD_REQUEST.value());}}//校验通过,执行原方法:roceedingJoinPoint.proceed(传原方法的入参)return proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());}
}
/*** 切面工具类* * @author 白豆五* @date 2023/09/04* @since JDK8*/
@Slf4j
public class AspectUtil {/*** 获取被拦截方法对象* @param pjp {@link ProceedingJoinPoint}* @return {@link Method}*/public static Method getMethod(ProceedingJoinPoint pjp) {//获取参数的类型Signature sig = pjp.getSignature();if (sig instanceof MethodSignature) {MethodSignature methodSignature = (MethodSignature) sig;return methodSignature.getMethod();} else {throw new IllegalArgumentException("It's not method");}}/*** 解析SPEL表达式** @param key key* @param method {@link Method}* @param args {@code Object[]}* @return key*/public static String parse(String key, Method method, Object[] args) {if (StringUtils.isNotBlank(key) && key.indexOf("#") > -1) {Pattern pattern = Pattern.compile("(\\#\\{([^\\}]*)\\})");Matcher matcher = pattern.matcher(key);List<String> keys = new ArrayList<>();while (matcher.find()) {keys.add(matcher.group());}if (!CollectionUtils.isEmpty(keys)) {LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();String[] paraNameArr = u.getParameterNames(method);ExpressionParser parser = new SpelExpressionParser();StandardEvaluationContext context = new StandardEvaluationContext();for (int i = 0; i < paraNameArr.length; i++) {context.setVariable(paraNameArr[i], args[i]);}for (String tmp : keys) {key = key.replace(tmp, parser.parseExpression("#" + tmp.substring(2, tmp.length() - 1)).getValue(context, String.class));}return key;}}return key;}/*** 获取请求体** @param pjp {@link ProceedingJoinPoint}* @return {@code Object}*/public static Object getBody(ProceedingJoinPoint pjp) {Object[] args = pjp.getArgs();Method method = getMethod(pjp);if (ObjectUtil.isNotEmpty(args)) {Annotation[][] parameterAnnotations = method.getParameterAnnotations();for (int count = 0; count < parameterAnnotations.length; count++) {for (Annotation annotation : parameterAnnotations[count]) {if (annotation instanceof RequestBody) {return args[count];}}}}return null;}
}
方式二:在程序中使用if语句进行参数校验
最粗暴的方式。
public boolean updateStatus(List<String> ids) {if(CollUtil.isEmpty(ids)){return false; }
}