Как я могу передать дополнительные параметры в валидаторе для полей? - PullRequest
0 голосов
/ 10 июля 2019

У меня есть подтверждение, что электронная почта уникальна.Это работа при регистрации.Но когда я пытаюсь изменить адрес электронной почты в профиле пользователя, у меня появляется ошибка.

Я проверяю электронную почту, чтобы запустить 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;
        }

Как мне решить эту проблему?

PS Если я сделаю валидатор для класса с кросс-полями, я не смогу показать ошибки с их полями в Thymeleaf.Поэтому мне нужен валидатор для полей.

PSS Мой контроллер:

@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 голосов
/ 11 июля 2019
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.

0 голосов
/ 10 июля 2019

Мы не рекомендуем использовать Hibernate Validator для проверки ограничений базы данных.

Но если вы хотите это сделать, идея состоит в том, чтобы использовать полезную нагрузку средства проверки ограничения: https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#constraint-validator-payload.Они были созданы для такого рода требований.

Пример 6.7 - это то, что вы ищете, так как вы не хотите создавать полный ValidatorFactory каждый раз, только конкретный контекст.

Теперь, это для Hibernate Validator, но вам понадобится способ правильно настроить Validator, созданный Spring для добавления в него вошедшего в систему пользователя.И я не могу помочь тебе с этим.

...