【问题标题】:How can I to pass on additional parameters in validator for fields?如何在字段验证器中传递其他参数?
【发布时间】:2019-07-10 15:05:21
【问题描述】:

我确认电子邮件是唯一的。这是注册期间的工作。但是当我尝试更改用户个人资料中的电子邮件时,我遇到了错误。

我检查电子邮件以运行 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;
    }
}

注解 UniqueEmail:

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

我该如何解决这个问题?

附:如果我要使用交叉字段对类做验证器,我不能在 Thymeleaf 中显示他们的字段错误。因此我需要字段验证器。

附言我的控制器:

@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";
    }

}

用户存储库:

public interface UserRepository extends JpaRepository<User, Long>{

    User findByUsername(String username);

    User findByActivationCode(String code);

    User findByEmail(String email);

}

【问题讨论】:

    标签: spring validation bean-validation hibernate-validator


    【解决方案1】:

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

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

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

    现在,这是针对 Hibernate Validator 端的,但您需要一种方法来正确配置 Spring 创建的 Validator 以将登录用户注入其中。我无法帮你解决这个问题。

    【讨论】:

    • "我们不建议使用 Hibernate Validator 来检查数据库约束。"在这种情况下我需要如何检查?我是否认为我需要添加像 ProfileForm 这样的类。检查他,然后填写属性用户并保存?
    【解决方案2】:
    User userFromDB = userRepository.findByEmail(value);
        if(userFromDB != null && user != userFromDB) {
            return true;
        }
    

    在这种情况下,'user' 和 'userFromDB' 总是可以不同的,因为这些对象可以有不同的 hashCodes。您可以添加返回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。

    【讨论】:

    • 这里可以这样做:userFromDB != null && user.id != userFromDB.id OR userFromDB != null && user.username != userFromDB.username
    • 我认为没有必要通过用户名进行检查。但就像我之前说的,尝试使用 JPA @Column 注释进行电子邮件验证,而不是自定义验证。此外,比在存储库方法中返回对象更好的做法是使用Optional 并使用isPresent () 方法检查对象,请查看这篇文章:stackoverflow.com/questions/28746482/…
    猜你喜欢
    • 2016-05-10
    • 2014-01-30
    • 2014-07-03
    • 1970-01-01
    • 2019-09-06
    • 1970-01-01
    • 1970-01-01
    • 2022-12-14
    • 1970-01-01
    相关资源
    最近更新 更多