Java中的三种校验注解的使用(@Valid,@Validated和@PathVariable)

网友投稿 802 2022-08-02


目录@Valid和@Validated@Valid和@Validated比较@Valid高级使用@Valid级联校验@Validated高级使用@Validated分组校验@Validated分组校验顺序@Validated非实体类校验@PathVariable正则表达式校验继承BasicErrorController类自定义校验注解

@Valid和@Validated

@Valid和@Validated比较

相同点:

@Valid注解和 @Validated注解都是开启校验功能的注解不同点:@Validated注解是Spring基于 @Valid注解的进一步封装,并提供比如分组,分组顺序的高级功能使用位置不同:@Valid注解 : 可以使用在方法,构造函数,方法参数和成员属性上@Validated注解 : 可以用在类型,方法和方法参数上. 但是不能用在成员属性上

@Valid高级使用

@Valid级联校验

级联校验: 也叫嵌套检测.嵌套就是一个实体类包含另一个实体类@Valid和可以用在成员属性的字段上,因此 @Valid可以提供级联校验示例:@Data

public class Hair {

@NotBlank(message = "头发长度必须提交!")

private Double length;

@NotBlank(message = "头发颜色必须提交!")

private String color;

}

@Data

public class Person {

@NotBlank(message = "用户姓名必须提交!")

@Size(min=2, max=8)

private String userName;

// 添加@Valid注解实现嵌套检测

@Valid

@NotEmpty(message = "用户要有头发!")

private List hairs;

}

@PostMapping("/person")

public Result addPerson(@Valid @RequestBody Person person) {

return Result.buildSuccess(person);

}

只是在方法参数前面添加 @Valid和 @Validated注解,不会对嵌套的实体类进行校验.要想实现对嵌套的实体类进行校验,需要在嵌套的实体类属性上添加 @Valid注解

@Validated高级使用

@Validated分组校验

分组校验:

对指定的组开启校验,可以分别作用于不同的业务场景中分组校验是由 @Validated注解中的value提供的groups:jsR 303校验注解中的分组方法groups示例:

@Data

public class PersonGroup {

public interface AddGroup {}

public interface UpdateGroup {}

// @Validated注解value方法指定分组UpdateGroup.class时校验

@NotBlank(message = "用户ID必须提交!", groups = UpdateGroup.class)

private String id;

// @Validated注解value方法指定分组AddGroup.class或者分组UpdateGroup.class时校验

@NotBlank(message = "用户的姓名必须提交!", groups = {AddGroup.class, UpdateGroup.class})

private String name;

// @Validated注解value方法未指定分组时校验

@Range(min = 1, max = 200, message = "用户的年龄必须提交!")

private int age;

}开启分组校验: 通过 @Validated注解的value方法对指定的分组开启校验

@RestController

@RequestMapping("/person")

public class PersonGroupController {

// 不指定分组时校验

@GetMapping("/person")

public Result getPerson(@Validated @RequestBody PersonGroup person) {

return Result.buildSuccess(person);

}

// 指定AddGroup分组校验

@PostMapping("/person")

public Result addPerson(@Validated(value = PersonGroup.AddGroup.class) @RequestBody PersonGroup person) {

return Result.buildSuccess(person);

}

// 指定UpdateGroup分组校验

@PutMapping("/person")

public Result updatePerson(@Validated(value = PersonGroup.updateGroup.class) @RequestBody PersonGroup person) {

return Result.buildSuccess(person);

}

}

校验方法添加groups的值来指定分组,只有使用 @Validated注解的value的值指定这个分组时,开会开启注解的校验数据的功能

@Validated分组校验顺序

默认情况下,分组间的约束是无序的,但是在一些特殊的情况下可能对分组间的校验有一定的顺序比如第二组的分组的约束的校验需要依赖第一组的稳定状态来进行,此时,要求分组间的约束校验一定要有顺序分组校验顺序通过使用 @GroupSequence注解实现示例:

@Data

public class UserGroupSequence {

public interface FirstGroup {}

public interface SecondGroup {}

// 使用GroupSequence定义分组校验顺序:按照FirstGroup,SecondGroup分组顺序进行校验

@GroupSequence({FirstGroup.class, SecondGroup.class})

public interface Group {}

@NotEmpty(message = "用户ID必须提交!", group = FirstGroup.class)

private String userId;

@NotEmpty(message = "用户姓名必须提交!", group = FirstGroup.class)

@Size(min = 2, max = 8, message = "用户姓名的长度在2~8之间", goup = Second.class)

private String userName;

}

@RestController

@RequestMapping("/user")

public class UserGroupSequenceController {

// 这里方法中@Validated注解value的值是Group.class

@PostMapping("/user")

public Result addGroup(@Validated(value = Group.class) @RequestBody UserGroupSequence user) {

return Result.buildSuccess(user);

}

}

使用 @GroupSequence注解指定分组校验顺序后,第一组分组的约束的校验没有通过后,就不会进行第二组分组的约束的校验

@Validated非实体类校验

在非实体类上添加 @Validated注解对非实体类进行校验

@Validated

public class AnnotationController {

@GetMapping("/person")

public Result getAge(@Range(min = 2, max = 8, message = "年龄在3~8岁!") @RequestParam int age) {

return Result.buildSuccess(age);

}

}

在GlobalExceptionHandler中添加全局统一异常处理方法:

@ExceptionHandler(ConstraintViolationException.class)

@ResponseBody

public Result resolveConstraintViolationException(ConstraintVilationException exception) {

Set> constraintVilations = exception.getConstraintVilations();

// 处理异常信息

if (!CollectionUtils.isEmpty(constraintVilations)) {

StringBuilder messageBuilder = new StringBuilder();

for (ConstraintVilation constraintViolation : constraintVilations) {

messageBuilder.append(constraintVilation.getMessage()).append(",");

}

String errorMessage = messageBuilder.toString();

if (errorMessage.length() > 1) {

errorMessage.substring(0, errorMessage.length() - 1);

}

return Result.builderFailure(ErrorStatus.ILLEGAL_DATA.getCode(), errorMessage);

}

return Result.builderFailure(ErrorStatus.ILLEGAL_DATA.getCode(), exception.getMessage())

}

@PathVariable

@PathVariable的作用: 用来指定请求URL路径里面的变量@PathVariable和 @RequestParam的区别:@PathVariable用来指定请求URL中的变量@RequestParam用来获取静态的URL请求入参

正则表达式校验

使用正则表达式校验 @PathVariable指定的路径变量

// 请求路径中的id必须是数字,否则寻找不到这个路径404

@GetMapping("/user/{id:\\d+}")

public Result getId(@PathVariable(name="id") String userId) {

return Result.buildSuccess(userId);

}

继承BasicErrorController类

@ControllerAdvice注解只能处理进入控制器方法抛出的异常BasicErrorController接口可以处理全局异常@PathVariable路径校验异常不是控制器方法抛出的,此时还没有进入控制器方法:BasicErrorController处理异常,比如404异常时,会跳转到 /error路径,此时会返回错误的html页面为了保证返回结果统一,继承BasicErrorController类,重写BasicErrorController接口中的错误处理方法

@RestController

public class PathErrorController extends BasicErrorController {

@Autowired

public PathErrorController(ErrorAttributes errorAttributes, ServerProperties serverPrhttp://operties, List errorViewResolvers) {

super(errorAttributes, serverProperties.getError(), errorViewResolvers);

}

/**

* 处理html请求

*/

@Override

public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {

HttpStatus status = getStatus(request);

Map model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML));

ModelAndView modelAndView = new ModelAndView("pathErrorPage", model, status);

return modelAndView;

}

/**

* 处理json请求

*/

@Override

public ResponseEntity> error(HttpServletRequest request) {

Map body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));

Map responseBody = new HashMap<>(8);

responseBody.put("success", false);

responseBody.put("code", body.get("status"));

responseBody.put("message", body.get("error"));

return new ResponseEntity<>(responseBody, HttpStatus.OK);

}

}

自定义校验注解

使用场景:对某一个只能输入指定值的字段进行校验. 此时需要使用自定义注解实现定义自定义的注解 @Show :

@Documented

@Constraint(validateBy = {Show.ShowConstraintValidator.class})

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})

@Rentation(RUNTIME)

public @interface Show {

String message() default "{com.oxford.annotation.Show.message}";

Class>[] groups() default {};

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

int[] value();

class ShowConstraintValidator implements ConstraintValidator {

private Set set = new HashSet<>();

/**

* 初始化操作

* 获取value属性指定的数字,保存到Set集合中

*/

@Override

public void initilize(Show constraintAnnotation) {

int[] value = constraintAnnotation.value();

for (int v : value) {

set.add(i);

}

}

@Override

public boolean isValid(Integer value, ConstraintValidatorContext context) {

return set.contains(value);

}

}

}

注意点:

@Constraint注解:

将自定义的注解和实现的校验类联系起来自定义校验注解类需要实现ConstraintValidator 接口接口中第一个泛型参数表示的是自定义注解类接口中第二个泛型参数表示的是校验的属性的值的类型initialize() 方法:获取到自定义注解中的相关的数据isValid() 方法:实现自定义的校验逻辑返回boolean类型的校验结果自定义注解的使用:

@Data

public class AnnotationQuery {

@Show(value = {0, 1}, message = "数值只能是0或者1")

private Integer isShow;

}

@PostMapping("/annotation")

public Result addAnnotation(@Validated @RequestBody AnnotationQuery annotation) {

return Result.buildSuccess(annotation);

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:MyBatis深入解读动态SQL的实现(mybaties动态sql)
下一篇:在Java中按值调用和按引用调用(java中怎么调用)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~