Quarkus框架下REST服务JSON请求校验最佳实践咨询
你当前手写校验工具类存在明显缺陷:大量重复空判断逻辑、嵌套层级深易触发空指针、规则散落在工具类难以维护、还存在字符串用==判断相等的语法错误,完全可以用Quarkus原生支持的Jakarta Bean Validation规范替代,实现声明式校验,大幅降低维护成本。
Quarkus对Jakarta Validation(原JSR-380,Hibernate Validator实现)做了开箱即用的集成,不需要额外自研工具,所有校验规则通过注解标注在实体字段上,框架自动完成校验、错误抛出,完全匹配你需要的非空、长度校验等需求。
第一步:引入校验扩展
如果项目还未集成校验组件,直接执行Quarkus扩展添加命令即可:
./mvnw quarkus:add-extension -Dextensions="hibernate-validator"
该扩展为Quarkus官方维护,和RESTEasy/Jackson层无缝适配,启动时会自动加载校验规则。
第二步:给实体类添加校验注解
直接将原有手写判断的规则转化为注解标注在对应字段上,嵌套对象通过@Valid注解触发级联校验,不需要手动逐层写判空逻辑:
- 最外层请求实体
FindPukCodeBSInput
package com.tmve.subscriber.domain.request; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.*; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @Builder @AllArgsConstructor @NoArgsConstructor @Data public class FindPukCodeBSInput { @JsonProperty("FindPukCodeBS") @NotNull(message = "FindPukCodeBS节点不能为空") @Valid // 触发嵌套对象的内部校验 FindPukCode findPukCode; }
- 二级节点
FindPukCode
package com.tmve.subscriber.domain.request; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.*; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @Setter @Getter @Builder @AllArgsConstructor @NoArgsConstructor @Data public class FindPukCode { @JsonProperty("Header") @NotNull(message = "Header节点不能为空") @Valid HeaderRequest header; @JsonProperty("Body") @NotNull(message = "Body节点不能为空") @Valid BodyRequest body; }
- 请求头实体
HeaderRequest
说明:
@NotBlank注解会自动校验字符串不为null、去除首尾空格后长度大于0,完全替代你原有xx != null && xx.length() > 0的判断,还会拦截全空格的非法入参;长度限制直接通过@Size注解配置即可。
package com.tmve.subscriber.domain.request; import lombok.*; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; @Setter @Getter @Builder @Data @AllArgsConstructor @NoArgsConstructor public class HeaderRequest { @NotBlank(message = "country不能为空") @Size(max = 2, message = "country长度不能超过2位") private String country; @NotBlank(message = "lang不能为空") private String lang; @NotBlank(message = "entity不能为空") private String entity; @Min(value = 1, message = "system字段不能为0") private int system; @NotBlank(message = "subsystem不能为空") private String subsystem; @NotBlank(message = "originator不能为空") private String originator; @NotBlank(message = "userId不能为空") private String userId; @NotBlank(message = "operation不能为空") private String operation; @NotBlank(message = "destination不能为空") private String destination; @NotBlank(message = "timestamp不能为空") private String timestamp; @NotBlank(message = "msgType不能为空") private String msgType; }
- 请求体节点
BodyRequest
package com.tmve.subscriber.domain.request; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import lombok.Getter; import lombok.Setter; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @Setter @Getter @Data public class BodyRequest { @JsonProperty("findPukCodeBSRequest") @NotNull(message = "findPukCodeBSRequest节点不能为空") @Valid FindPukCodeBSRequest findPukCodeBSRequest; }
- 业务参数节点
FindPukCodeBSRequest
package com.tmve.subscriber.domain.request; import lombok.Data; import lombok.Getter; import lombok.Setter; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; @Setter @Getter @Data public class FindPukCodeBSRequest { @NotBlank(message = "iccid不能为空") @Size(min = 18, max = 20, message = "iccid长度不符合规范") private String iccid; }
第三步:在接口层开启自动校验
在REST资源类的方法入参上添加@Valid注解,Quarkus会在请求进入业务逻辑前自动完成所有规则校验,校验不通过直接抛出400错误,不需要在业务代码中手动调用校验方法:
import jakarta.validation.Valid; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; @Path("/subscriber") public class PukResource { @POST @Path("/findPuk") // 加@Valid即可触发全链路嵌套校验 public PukResponse findPuk(@Valid FindPukCodeBSInput request) { // 执行到此处时所有字段已符合校验规则,直接写业务逻辑即可 return processPukQuery(request); } }
(可选)统一校验错误返回格式
如果你们组织有固定的接口返回规范,可以自定义异常处理器捕获校验异常,统一组装返回结构,所有接口的校验错误都会自动走该逻辑,不需要单独处理:
import jakarta.validation.ConstraintViolationException; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.ext.ExceptionMapper; import jakarta.ws.rs.ext.Provider; import java.util.stream.Collectors; @Provider public class ValidationExceptionHandler implements ExceptionMapper<ConstraintViolationException> { @Override public Response toResponse(ConstraintViolationException e) { // 收集所有校验失败的字段和错误信息,按组织规范组装返回 String errorDetail = e.getConstraintViolations().stream() .map(violation -> violation.getPropertyPath() + ":" + violation.getMessage()) .collect(Collectors.joining("; ")); return Response.status(Response.Status.BAD_REQUEST) .entity(new CommonResponse("PARAM_INVALID", errorDetail)) .build(); } }
对比原有手写校验的优势
- 无冗余判空:级联校验自动完成嵌套对象的逐层检查,不需要写长串链式判空逻辑,从根源避免空指针
- 规则易维护:校验规则和实体字段定义放在一起,修改规则直接调整注解参数即可,不需要在工具类中查找对应判断分支
- 内置能力丰富:除了非空、长度校验,还内置正则匹配、日期格式、数值范围、邮箱格式等几十种常用校验注解,不需要自行实现
- 修复原有代码缺陷:自动规避原代码中字符串用
==判断相等、重复校验msgType字段等问题 - 性能可靠:Hibernate Validator是经过十余年迭代的成熟实现,校验性能远高于手写反射/字符串判断逻辑
内容的提问来源于stack exchange,提问作者Cesar Justo

