如何在字段的验证器中传递其他参数?

时间:2019-07-10 15:05:21

标签: spring validation bean-validation hibernate-validator

我已经确认电子邮件是唯一的。在注册过程中工作。但是,当我尝试更改用户个人资料中的电子邮件时,出现错误。

我检查电子邮件以运行userRepository.findByEmail。如果找到用户,则电子邮件不是唯一的。但是,当用户在个人资料中更改其电子邮件时,findByEmail会返回该用户。验证失败。

我需要检查findByUser返回的用户是否与更改电子邮件的用户不同。为此,我需要在验证器中传递更改电子邮件的用户。

这是我的代码。

实体:

@Data
@Table(name = "users")
@NoArgsConstructor
@CheckPasswordConfirm(message = "Password not equal!")
@Entity
public class User implements UserDetails{

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @NotBlank(message = "Username is empty!")
    @UniqueUsername(message = "Username isn't unique")
    private String username;

    @NotBlank(message = "Password is empty!")
    private String password;

    @Transient
    @NotBlank(message = "Password confirm is empty!")
    private String passwordconfirm;

    private boolean active;

    @Email(message = "E-mail!")
    @NotBlank(message = "E-mail is empty!")
    @UniqueEmail(message = "Email isn't unique!")
    private String email;

    private String activationCode;

    @ElementCollection(targetClass = Role.class, fetch = FetchType.EAGER)
    @CollectionTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"))
    @Enumerated(EnumType.STRING)
    private Set<Role> roles;

    public boolean isAdmin()
    {
        return roles.contains(Role.ADMIN);
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return getRoles();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

AnnotationEmail批注:

@Constraint(validatedBy = UniqueEmailValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface UniqueEmail {

    public String message();

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

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

}

验证者:

package ru.watchlist.domain.validation.validators;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.springframework.beans.factory.annotation.Autowired;

import ru.watchlist.domain.validation.annotations.UniqueEmail;
import ru.watchlist.service.UserService;

public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {

    @Autowired
    private UserService userService;

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return value != null && !userService.isEmailAlredyUse(value);
    }

}

userService.isEmailAlredyUse:

    public boolean isEmailAlredyUse(String value) {
        User user = userRepository.findByEmail(value);
        if(user != null) {
            return true;
        }
        return false;
    }

在这里我需要检查:

User userFromDB = userRepository.findByEmail(value);
        if(userFromDB != null && user != userFromDB) {
            return true;
        }

我该如何解决这个问题?

P.S。如果我将使用验证器对交叉字段进行分类,则无法在Thymeleaf中显示其字段的错误。因此,我需要字段验证器。

P.S.S。我的控制器:

@Controller
@RequestMapping("/profile")
public class ProfileController {

    @Autowired
    UserService userService;

    @GetMapping
    public String profile(@AuthenticationPrincipal User user, Model model) {

        model.addAttribute(user);
        return "profile";

    }

    @PostMapping("/update")
    public String saveChanges(@Valid User user, Errors errors) {

        if(errors.hasErrors()) {
            return "profile";
        }

        userService.addUser(user);

        return "redirect:/profile";
    }

}

UserRepository:

public interface UserRepository extends JpaRepository<User, Long>{

    User findByUsername(String username);

    User findByActivationCode(String code);

    User findByEmail(String email);

}

2 个答案:

答案 0 :(得分:0)

我们不建议使用Hibernate Validator来检查数据库约束。

但是,如果您想这样做,则可以使用约束验证器有效负载https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#constraint-validator-payload。它们是为满足此类要求而创建的。

您想要的是示例6.7,因为您不想每次都构建一个完整的ValidatorFactory,而只是一个特定的上下文。

现在,这是用于Hibernate Validator端的,但是您将需要一种方法来正确配置Spring创建的Validator以便将已登录的用户注入其中。而且我帮不上你。

答案 1 :(得分:0)

User userFromDB = userRepository.findByEmail(value);
    if(userFromDB != null && user != userFromDB) {
        return true;
    }

在这种情况下,“ user”和“ userFromDB”始终可以不同,因为此对象可以具有不同的hashCode。您可以在返回user的地方添加存储库和类的实现吗?我假设您没有使用JPA规范,但是如果您使用它,请尝试使用此声明@Column(unique = true)

替换您自己的验证

如果要验证控制器中的对象,可以使用@Valid批注,其中控制器一侧的代码如下所示:

 @PostMapping
public String saveObject(@Valid @ModelAttribute("email") Object object, BindingResult bindingResult) {

    if(bindingResult.hasErrors()){
        return view_with_form;
    }

    repository.save(object)
    return "redirect:path";
}

从Thymeleaf的形式:

 <div class="col-md-6 mt-2 form-group">
                                <label>Recipe Description:</label>
                                <input type="text" class="form-control" th:field="*{email}" th:errorclass="is-invalid"/>
                                <span class="invalid-feedback" th:if="${#fields.hasErrors('email')}">
                                    <ul>
                                        <li th:each="error : ${#fields.errors('email')}" th:text="${error}"></li>
                                    </ul>
                                </span>
                            </div>

此示例适用于Bootstrap 4。