如何在自定义对象验证中结合路径参数实现特定时间比较逻辑?
如何在自定义对象验证中结合路径参数实现特定时间比较逻辑?
嘿,我看你现在遇到的问题是原来的DTO类级验证只能拿DTO自己的属性,没法结合接口里的路径参数id去做时间对比对吧?刚好我之前也处理过类似的需求,给你两个实用的解决方案,你可以根据自己的项目情况选:
思路一:把路径参数绑定到DTO中(简单直接,适合允许修改DTO的场景)
如果你的DTO结构可以调整,这是最省事的方法:
- 先给DTO加个
id字段和对应的Setter方法,这样就能把路径参数传进去:
@Getter @Setter @AllArgsConstructor @NoArgsConstructor public class Dto { @NotNull private LocalDateTime start; @NotNull private LocalDateTime end; private int id; // 新增用于接收路径参数的字段 }
- 在接口方法里,把路径参数
id手动设置到DTO对象上:
public Response create(@PathParam("id") int id, @Valid @NotNull final Dto dto) { dto.setId(id); // 把路径参数绑定到DTO里 return Response.ok(service.save(id, dto)).build(); }
- 最后改造你的自定义Validator,注入业务Service(用来根据
id查询你需要的基准时间,比如从数据库或配置里拿),然后就能在验证逻辑里用这个基准时间和start对比了:
@Component @AllArgsConstructor public class FutureValidator implements ConstraintValidator<Future, Dto> { private final Service service; // 注入业务服务,用来根据id查基准时间 @Override public boolean isValid(final Dto dto, final ConstraintValidatorContext constraintValidatorContext) { // 这里替换成你实际的业务逻辑:用dto的id获取要对比的基准时间 LocalDateTime targetBaseTime = service.getBaseTimeById(dto.getId()); // 执行时间对比:start要晚于基准时间,end要晚于start return dto.getStart().isAfter(targetBaseTime) && dto.getEnd().isAfter(dto.getStart()); } }
思路二:使用方法级自定义验证(无需修改DTO,更灵活)
如果不想动DTO的结构,那用方法级验证是更好的选择,因为它能直接拿到方法上的所有参数(包括路径参数和DTO):
- 先定义一个方法级的自定义验证注解:
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = TimeWithIdValidator.class) public @interface ValidateTimeAgainstId { String message() default "时间不符合路径参数对应的基准要求"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
- 实现这个方法级的验证器,注意这里的验证目标是
Object[],对应方法的所有参数数组:
@Component public class TimeWithIdValidator implements ConstraintValidator<ValidateTimeAgainstId, Object[]> { private final Service service; public TimeWithIdValidator(Service service) { this.service = service; } @Override public boolean isValid(Object[] methodArgs, ConstraintValidatorContext context) { // 这里的参数顺序和接口方法的参数顺序完全一致:第一个是路径参数id,第二个是Dto int targetId = (int) methodArgs[0]; Dto dto = (Dto) methodArgs[1]; // 同样用id获取基准时间,执行对比逻辑 LocalDateTime targetBaseTime = service.getBaseTimeById(targetId); return dto.getStart().isAfter(targetBaseTime) && dto.getEnd().isAfter(dto.getStart()); } }
- 最后在接口方法上加上这个注解,别忘了给Resource类加上
@Validated(Spring环境下)来开启方法级验证支持;如果是JAX-RS环境(比如Jersey),还要注册对应的方法验证Feature:
@Component @Validated // 开启Spring方法级验证支持 public class Resource { private final Service service; public Resource(Service service) { this.service = service; } @POST @Path("/{id}/create") @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) @ValidateTimeAgainstId // 加上我们自定义的方法级验证注解 public Response create(@PathParam("id") int id, @Valid @NotNull final Dto dto) { return Response.ok(service.save(id, dto)).build(); } }
为什么原来的方案不行?
之前的类级验证(把@Future加在DTO类上)只能访问DTO自身的属性,没法获取方法层面的路径参数id,所以要实现结合路径参数的验证,必须把id带入到验证逻辑里,上面两种思路都是围绕这个核心来的。
备注:内容来源于stack exchange,提问作者thmasker
相关产品推荐
相关产品推荐

