我正在尝试创建一个UniqueName注释,作为针对create project api的定制bean验证注释:
UniqueName
@PostMapping("/users/{userId}/projects") public ResponseEntity createNewProject(@PathVariable("userId") String userId, @RequestBody @Valid ProjectParam projectParam) { User projectOwner = userRepository.ofId(userId).orElseThrow(ResourceNotFoundException::new); Project project = new Project( IdGenerator.nextId(), userId, projectParam.getName(), projectParam.getDescription() ); ... } @Getter @NoArgsConstructor(access = AccessLevel.PRIVATE) class ProjectParam { @NotBlank @NameConstraint private String name; private String description; } @Constraint(validatedBy = UniqueProjectNameValidator.class) @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD }) public @interface UniqueName { public String message() default "already existed"; public Class<?>[] groups() default {}; public Class<? extends Payload>[] payload() default{}; } public class UniqueProjectNameValidator implements ConstraintValidator<UniqueName, String> { @Autowired private ProjectQueryMapper mapper; public void initialize(UniqueName constraint) { } public boolean isValid(String value, ConstraintValidatorContext context) { // how can I get the userId info?? return mapper.findByName(userId, value) == null; } }
问题在于name字段只需要用户级别的唯一性。因此,我需要{userId}从URL字段获取进行验证。但是如何将其添加到中UniqueProjectNameValidator?还是有一些更好的方法来处理此验证?这只是大对象的一小部分,真实对象在请求处理程序中还有许多其他复杂的验证,这会使代码变得很脏。
name
{userId}
UniqueProjectNameValidator
如@Abhijeet所述,将userId属性动态传递给约束验证器是不可能的。至于如何更好地处理此验证案例,有干净的解决方案和肮脏的解决方案。
userId
干净的解决方案 是将所有业务逻辑提取到服务方法中,并ProjectParam在服务级别进行验证。这样,您可以在上添加userId属性ProjectParam,然后在调用服务之前将其从映射@PathVariable到@RequestBody。然后UniqueProjectNameValidator,您调整以验证ProjectParam而不是String。
ProjectParam
@PathVariable
@RequestBody
String
肮脏的解决方案 是使用Hibernate Validator的交叉参数约束(另请参见此链接以获取示例)。本质上 ,您 将 两个控制器方法参数都 视为自定义验证器的输入。