hibernate-validator扩展自定义注解校验请求参数及请求体

此文章已同步更新至我的个人博客https://simonting.gitee.io


SpringBoot项目中一般使用hibernate-validator来对请求参数进行校验,但hibernate-validator提供的注解有限,有时候需要根据具体业务扩展自定义注解对参数进行校验。

前言

在实际运用中,hibernate-validator的注解分为校验请求参数及请求体,两个校验的情况在处理上有一些差别,下面分开讲解。

校验请求参数

自定义注解

模拟对Id进行校验

@Documented
@Constraint(validatedBy = IdCheck.IdValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR,
        ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface IdCheck {
    String message() default "Id is invalid.";

    Class<?>[] groups() default {};

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

    class IdValidator implements ConstraintValidator<IdCheck, String> {
        @Override
        public void initialize(IdCheck constraintAnnotation) {
        }

        @Override
        public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
            // 这里写业务校验逻辑,此处为模拟,id不允许等于001
            return !Objects.equals(s, "001");
        }
    }
}

其中:

@Constraint(validatedBy = IdCheck.IdValidator.class) 指定具体校验逻辑实现类。

@Target 指定注解所修饰的对象范围

String message() default “Id is invalid.”; 可以自定义校验失败的提示内容

异常统一处理

全局异常统一处理请查阅我之前的文章

当hebernate-validator校验的是请求参数时,校验失败抛出的异常类型为ConstraintViolationException。

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ErrorResult handleConstraintViolationException(ConstraintViolationException ex) {
        Set<ConstraintViolation<?>> errs = ex.getConstraintViolations();
        Set<String> fields = new HashSet<>();
        Set<String> msgs = new HashSet<>();
        for (ConstraintViolation<?> err : errs) {
            String path = err.getPropertyPath().toString();
            String field = StringUtils.substringAfterLast(path, ".");
            if (StringUtils.isEmpty(field)) {
                field = path;
            }
            fields.add(field);
            String msg = err.getMessage();
            msgs.add(msg);
        }
        String params = StringUtils.join(fields.toArray(), ",");
        String cause = StringUtils.join(msgs.toArray(), ",");
        Map<String, Object> errorParams = new HashMap<>();
        errorParams.put("param", params);
        errorParams.put("cause", cause);
        return ErrorResult.builder()
                .errorCode(500)
                .errorMsg("参数校验失败")
                .errorParams(errorParams)
                .build();
    }
}

测试

需要特别注意的是,对@PathVariable、@RequestParam修饰的请求参数校验时,需要在类上面加上@Validated注解

校验哪个参数就将注解放在前面即可

1、校验@PathVariable参数

@Slf4j
@Validated //校验@PathVariable、@RequestParam参数时需加上此注解
@RestController
@RequestMapping("/v1")
public class TestController {

    @GetMapping("/check/{id}")
    public ResponseEntity<?> check(@PathVariable @IdCheck String id) {
        log.info("id is {}", id);
        return new ResponseEntity<>(HttpStatus.OK);
    }
}

使用postman发起请求测试:
在这里插入图片描述

2、校验@RequestParam参数

新增一个对name参数的校验自定义注解,作用在@RequestParam后
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3、当@PathVariable参数与@RequestParam参数同时校验失败时

在这里插入图片描述

校验请求体

自定义注解

这里直接使用上面自定义的两个注解IdCheck、NameCehck。

异常统一处理

hibernate-validator在校验请求体时失败抛出的异常与校验请求参数时抛出的异常有差别,类型为MethodArgumentNotValidException,因此要对此类型的异常额外做全局异常统一处理。

@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResult handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
    List<FieldError> errs = ex.getBindingResult().getFieldErrors();
    Set<String> fields = new HashSet<>();
    Set<String> msgs = new HashSet<>();
    for (FieldError err : errs) {
        fields.add(err.getField());
        String msg = err.getDefaultMessage();
        msgs.add(msg);
    }
    String params = StringUtils.join(fields.toArray(), ",");
    String cause = StringUtils.join(msgs.toArray(), ",");
    Map<String, Object> errorParams = new HashMap<>();
    errorParams.put("param", params);
    errorParams.put("cause", cause);
    return ErrorResult.builder()
        .errorCode(500)
        .errorMsg("参数校验失败")
        .errorParams(errorParams)
        .build();
}

测试

在需要校验的实体类前加上@Validated注解,在实体类内部各字段前面加上具体的校验注解。

@PostMapping("/checkUser")
public ResponseEntity<?> checkUser(@RequestBody @Validated UserReq userReq) {
    log.info("id is {}", userReq.getId());
    log.info("name is {}", userReq.getName());
    return new ResponseEntity<>(HttpStatus.OK);
}
@Data
public class UserReq {
    @IdCheck
    private String id;

    @NameCheck
    private String name;
}

postman请求结果:

在这里插入图片描述

### 创建和使用Hibernate Validator中的自定义注解 #### 定义自定义约束注解 为了创建自定义验证注解,在Java中需先定义一个新的注解类。此注解应指定`@Constraint`来表明这是一个用于字段或方法参数上的验证规则[^1]。 ```java import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = MyCustomValidator.class) public @interface MyCustom { String message() default "Invalid value"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } ``` 上述代码展示了如何声明名为 `MyCustom` 的新注解,它将在运行时被保留并应用于字段上。通过设置默认消息以及分组和支持负载的属性,可以增强其灵活性。 #### 实现约束校验器 接着实现具体的逻辑处理程序——即实现了`ConstraintValidator<A,T>`接口的一个类,其中A代表所定义注解类型而T则是待检验的目标数据类型。 ```java import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; public class MyCustomValidator implements ConstraintValidator<MyCustom, String> { @Override public void initialize(MyCustom constraintAnnotation) {} @Override public boolean isValid(String value, ConstraintValidatorContext context) { // Add your validation logic here. return true; // Replace this line with actual validation code. } } ``` 这段示例说明了怎样编写一个简单的字符串有效性检查函数;实际应用中应当替换掉返回true的那一行以加入特定业务需求下的判断条件。 #### 应用自定义注解到实体模型 最后一步就是把新建好的注解标记给相应的域或者getter/setter方法之上: ```java public class UserEntity { private Long id; @MyCustom(message="This field must follow specific rules.") private String specialField; // Getters and setters... } ``` 这样每当保存含有此类标注的对象实例之前都会触发对应的验证过程,并依据配置的消息模板给出反馈提示。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值