06-搭建Rest服务-02 参数校验

Validator

       作为一个web服务对参数的校验是必不可少的,这节我们将介绍Hibernate Validator 在Quarkus中的使用。

本章目标

  • 校验方式
  • 接口参数校验
  • 方法参数校验
  • 参数校验扩展

1 搭建项目

项目依赖:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-hibernate-validator</artifactId>
</dependency>

2 校验方式

Quarkus 提供了两种校验方式

  • 通过注入校验工具对象,手动校验参数
    Validator: 关于Java中的Validator,大家应该不会陌生它是Java原生提供的。Hibernate Validator 只是对它进行扩展。不了解的同学可以自行了解一下。
  • 通过注解修饰需要校验的对象自动校验
    @Valid 同上

我们本章以对Json请求的方式的校验,单个参数的校验更简单,直接使用对应的校验注解即可。
定义VO类:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Fruit {

    @NotBlank(message = "name not null")
    public String name;
    @NotBlank(message = "description not null")
    public String description;
}

我这里用了Lombok插件,通过注解省略了构造方法和Getter&Setter方法。同时将常用的校验注解贴出来:

@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=) 被注释的元素必须在合适的范围内

同时校验注解也支持自定义实现,有需求的同学可自行百度。

3 手动校验

上代码:

@Slf4j
@Path("/fruits")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class FruitResource {

    @Inject
    Validator validator;

    private final Map<String, Fruit> fruits = new HashMap<>();

    public FruitResource() {
        fruits.put("Apple", new Fruit("Apple", "Winter fruit"));
        fruits.put("Pineapple", new Fruit("Pineapple", "Tropical fruit"));
    }

    @GET
    public Map<String, Fruit> list() {
        return fruits;
    }

    @PUT
    public Map<String, Fruit> edit(Fruit fruit) {
        Set<ConstraintViolation<Fruit>> violationSet = validator.validate(fruit);
        if(!violationSet.isEmpty()){
            String message = violationSet.stream()
                    .map(ConstraintViolation::getMessage)
                    .collect(Collectors.joining(", "));
            log.error("param validate error {}", message);
            return fruits;
        }
        Fruit old = fruits.get(fruit.getName());
        old.setDescription(fruit.getDescription());
        return fruits;
    }

    @POST
    public Map<String, Fruit> add(@Valid Fruit fruit) {
        fruits.put(fruit.getName(), fruit);
        return fruits;
    }

    @DELETE
    public Map<String, Fruit> delete(Fruit fruit) {
        fruits.remove(fruit.getName());
        return fruits;
    }
}

这里简单实现了一个Fruit的Rest CRUD, 手动校验看edit方法, 使用validator.validate(value)方法即可。但是需要注意的是需要手动处理校验结果。如果不处理的话相当于这个校验是无效的。在上面的代码中只是将校验信息log一下,更优雅一点的话可以通过抛出异常,在全局异常中统一处理。
测试一下:
在这里插入图片描述
在这里插入图片描述

4 自动校验

代码同上,这次测试add 方法:
在这里插入图片描述
这次使用注解,框架是已经做了处理了,直接将校验异常信息返回了,看起来还是比较清晰明了的。相比之下是不是注解的自动校验方式更好一点呢。

5 方法参数校验

在服务的方法中校验参数跟接口的校验是一样的,并没有太大的区别,这里思考一下,如果我们的需求是按我们的响应格式返回结果怎么办。
通过捕获异常发现,当参数校验失败时抛出的异常是ConstraintViolationException 所以思路就是提供一个全局异常的Handler 处理一下。全局异常我们上节应该已经讲过了。

@Slf4j
public class GlobalExceptionHandler {

    @ServerExceptionMapper
    public Response globalExceptionHandler(Exception e){
        log.error("global exception ", e);
        return Response.status(500).entity("SYSTEM ERROR").build();
    }

    @ServerExceptionMapper(ConstraintViolationException.class)
    public Response ConstraintViolationExceptionHandler(ConstraintViolationException e){

        String message = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(","));

        log.error("param validate error {}", message);

        HashMap<String, Object> result = new HashMap<>();

        result.put("code", 400);
        result.put("msg", "参数校验失败");
        result.put("data", message);

        return Response.status(400).entity(result).build();
    }

}

在这里插入图片描述

6 参数校验扩展

Validator 本身是Java 提供的扩展包,Hibernate Validator 进行扩展的,Quarkus只是将他们集成进框架。同样留给我们自定的扩展空间。Quarkus 将 Validator 与 CDI进行了集成我们只需要按需实现我们自己的实现即可。

javax.validation.ClockProvider
javax.validation.ConstraintValidator
javax.validation.ConstraintValidatorFactory
javax.validation.MessageInterpolator
javax.validation.ParameterNameProvider
javax.validation.TraversableResolver
org.hibernate.validator.spi.properties.GetterPropertySelectionStrategy
org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory

但是官网有这么一句提示Obviously, for each listed type, you can declare only one bean.These beans should be declared as @ApplicationScoped. 就是说没种类型只能声明一个Bean 并且应该用@ApplicationScoped 修饰。 但是实测没有@ApplicationScope 也是可以的。或许我没看懂作者的意思。
定义注解:

@Documented
@Constraint(
        validatedBy = StartWithValidator.class
)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface StartWith {
    String prefix() default "";

    String message() default "";

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

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

校验器实现:

@Slf4j
public class StartWithValidator implements ConstraintValidator<StartWith, String> {

    private StartWith startWith;

    @Override
    public boolean isValid(String s, ConstraintValidatorContext context) {
        log.info("自定义参数校验");
        System.out.println(context.getDefaultConstraintMessageTemplate());
        System.out.println(startWith.prefix());
        return false;
    }

    @Override
    public void initialize(StartWith constraintAnnotation) {
        this.startWith  = constraintAnnotation;
    }
}

使用

    @StartWith(prefix = "__", message = "value must start witch __")
    @NotBlank(message = "description not null")
    public String description;

结果:

{
    "msg": "参数校验失败",
    "code": 400,
    "data": "value must start witch __"
}

总结

本章主要介绍了在Quarks项目中如何使用Validator 校验 参数,同时 Validator还有更多用法,如校验分组等等都是可以使用的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值