3

如果有可能为不同的控制器方法定制验证器,我想得到答案。

简单的验证器

@Component
public class UserDtoValidator implements Validator {
@Autowired
UserService userService;

@Override
public boolean supports(Class<?> aClass) {
    return UserDto.class.isAssignableFrom(aClass);
}

@Override
public void validate(Object target, Errors errors) {
    UserDto userDto = (UserDto) target;

    }
   //how to make 'if' below to be applied only for certain method in controller
   //in this case for controller createUser method
    if (userService.findByUserName(userDto.getUserName())!=null) {
        throw new InvalidPayloadException("Creating user requires unique userName");
    }
   //second 'if' for controller updateUser method 
     if (userService.findByUserName(userDto.getUserName())==null) {
        throw new InvalidPayloadException("Updating unexisting users is not allowed");
    }
  }
}

控制器:

这里我们对验证者有两种相反的情况:

1 创建具有唯一用户名的用户

2 更新用户 - 必需的用户名

@Controller
@RequestMapping(value = "/api/users")
public class ApiUserController extends ExceptionsResolver {

    @Autowired
    private UserService userService;
    @Autowired
    private UserDtoValidator userDtoValidator;
    @InitBinder
    private void initBinder(WebDataBinder binder) {
    binder.setValidator(userDtoValidator);
    }


    @RequestMapping(consumes = "application/json", produces = "application/json", method = RequestMethod.POST)
    @ResponseBody
    public ResponseEntity createUser(@Valid @RequestBody UserDto userDto) throws JsonProcessingException {
        userService.saveUser(userDto);
        return new ResponseEntity(userDto, HttpStatus.ACCEPTED);
    }

    @RequestMapping(value = "/{userName}", consumes = "application/json", method = RequestMethod.PUT)
    @ResponseBody
    public ResponseEntity<UserDto> updateUser(@Valid @RequestBody UserDto userDto, @PathVariable String userName) {
         return new ResponseEntity("User updated", HttpStatus.ACCEPTED);
     }

}

顺便说一句,我知道 PUT 应该创建新的,但在这里我只需要 PUT 进行更新。

4

3 回答 3

5

你真正关心的是用户名是否存在。在某些情况下你想要它,在某些情况下你不想要。

理论上,您可以使用@Username带有exists属性的注释。这和阿迪的很像isUpdate,但不要这么叫isUpdate。您不关心需要验证的操作,您只关心用户名是否存在。

验证组是为这个问题而设计的,即在不同的情况下以不同的方式验证一个bean。创建两个验证组NewUserExistingUser. @Valid将您的调用替换为@ControllerSpring 的@Validated.

public ResponseEntity createUser(@Validated(NewUser.class) @RequestBody UserDto userDto) throws JsonProcessingException {}

public ResponseEntity<UserDto> updateUser(@Validated(ExistingUser.class) @RequestBody UserDto userDto, @PathVariable String userName) {}

UserDto课堂上,理论上你会将username属性标记为

@Username(exists = true, groups = ExistingUser.class);
@Username(exists = false, groups = NewUser.class);
public String getUsername() {}

但是Java不会让你这样做。因此,您需要一种解决方法来设置多个用户名约束。这在 Bean Validation API 中到处使用,例如在NotNull

/**
 * Defines several <code>@NotNull</code> annotations on the same element
 * @see javax.validation.constraints.NotNull
 *
 * @author Emmanuel Bernard
 */
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@interface List {
    NotNull[] value();
}

有了这些东西,您只需要一个UsernameValidator可以根据exists标志检查用户名是否存在的工具,验证组将负责其余的工作。

于 2013-10-29T11:01:35.770 回答
3

我可以想出一种从验证器中知道您是在创建还是更新用户(使用 Interceptor 和 ThreadLocal 对象)的方法,但不是一种优雅的方法。

我知道这不完全是您所要求的,但我认为我会使用看起来像这样的自定义验证注释:

@Target({ ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UsernameValidator.class)
public @interface Username
{
    String message() default "...";

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

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

    boolean isUpdate() default true;
}

和验证器:

public class UsernameValidator implements ConstraintValidator<Username, String>
{
    private boolean isUpdate;

    public void initialize(Username constraintAnnotation)
    {
        isUpdate = constraintAnnotation.isUpdate();
    }

    public boolean isValid(String value, ConstraintValidatorContext context)
    {
        if (isUpdate)
        {
            // Make sure the user exists
        }
        else
        {
            // Make sure the user doesn't exist
        }           
    }
}

这种实现的用法看起来很简单:

@Username                           // In case of an update
@Username(isUpdate = false)         // In case of a creation

当然,您必须将其转换为您的特定用例,但我相信您明白了。

希望我仍然设法以某种方式帮助你。祝你好运!

于 2013-10-28T21:44:54.917 回答
1

我遇到了同样的问题,似乎没有“神奇”的解决方案。

我最终得到了带有用于更新和保存的常用内容的验证器。特定检查留在控制器方法中。

另一种解决方案是创建一个单独的 dto(如果您不将 dto 对象用于 orm 目的)。并在它们上使用 JSR-303 验证注释(为简洁起见)。但是这个解决方案实际上取决于具体情况。例如,如果只能更新一小部分字段,这似乎是合适的。

于 2013-10-28T22:26:21.850 回答