如何在弹簧控制器中应用于验证器的特定方法的不同行为

时间:2013-10-26 20:39:42

标签: java spring validation model-view-controller

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

简单验证器

@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创建具有唯一userName的用户

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);
     }

}

BTW我知道PUT应该创建新的,但在这里我只需要PUT进行更新。

3 个答案:

答案 0 :(得分:5)

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

理论上,您可以使用带有@Username属性的exists注释。这与Adi isUpdate类似,但不要称之为isUpdate。您不关心需要验证的操作,只关心用户名是否存在。

验证组是针对此问题而设计的,即在不同情况下以不同方式验证bean。创建两个验证组NewUserExistingUser。使用Spring @Valid替换@Controller中的@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标志检查用户名是否存在,验证组将负责其余部分。

答案 1 :(得分: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

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

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

答案 2 :(得分:1)

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

我最后使用验证器进行更新和保存。具体检查留在控制器方法中。

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