问题描述
我正在尝试创建UniqueName
注释,作为用于创建项目api的定制bean验证注释:
I am trying to create a UniqueName
annotation as a cutomize bean validation annotation for a create project api:
@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
字段仅需要用户级别的唯一性.因此,我需要从URL字段获取{userId}
进行验证.但是如何将其添加到UniqueProjectNameValidator
中?还是有一些更好的方法来处理此验证?这只是一个大对象的一小部分,实际对象在请求处理程序中还有许多其他复杂的验证,这些验证使代码很脏.
The problem is that name
field just need uniqueness for user level. So I need to get the {userId}
from the URL field for validation. But how can I add this into the UniqueProjectNameValidator
? Or is there some better way to handle this validation? This is just a small part of a large object, the real object has many other complex validations in the request handler which make the code quite dirty.
推荐答案
如@Abhijeet所述,将userId
属性动态传递到约束验证器是不可能的.至于如何更好地处理此验证案例,有干净的解决方案和肮脏的解决方案.
As @Abhijeet mentioned, dynamically passing the userId
property to the constraint validator is impossible. As to how to handle this validation case better, there's the clean solution and the dirty solution.
干净的解决方案是将所有业务逻辑提取到服务方法中,并在服务级别上验证ProjectParam
.这样,您可以在ProjectParam
中添加userId
属性,并在调用服务之前将其从@PathVariable
映射到@RequestBody
.然后,您调整UniqueProjectNameValidator
以验证ProjectParam
,而不是String
.
The clean solution is to extract all the business logic to a service method, and validate the ProjectParam
at the service level. This way, you can add a userId
property to ProjectParam
, and map it from the @PathVariable
onto the @RequestBody
before calling the service. You then adjust UniqueProjectNameValidator
to validate ProjectParam
s rather than String
s.
肮脏的解决方案是使用Hibernate Validator的跨参数约束(另请参见此链接为例).实际上,您将这两个控制器方法参数都作为自定义验证器的输入.
The dirty solution is to use Hibernate Validator's cross-parameter constraints (see also this link for an example). You essentially treat both of your controller method parameters as the input for your custom validator.
这篇关于带有额外信息的Bean验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!