我有兩個彼此靠近的控制器——一個用于產品,一個用于用戶。我想將 Spring Validation 添加到兩個控制器中。所以我添加了實作 Validator 的 ProductValidator 和 UserValidator 類。ProductValidator 作業正常。問題是永遠不會呼叫 UserValidator。正在呼叫 InitBinder 方法,但驗證不起作用。同時,ProductController 中的產品一切正常。
這是用戶邏輯的代碼。
用戶控制器
@Controller
@RequestMapping(path = "/users")
public class UserController {
private final UserService userService;
private final RoleService roleService;
private PasswordEncoder passwordEncoder;
@Autowired
public UserController(UserService userService, RoleService roleService, PasswordEncoder passwordEncoder) {
this.userService = userService;
this.roleService = roleService;
this.passwordEncoder = passwordEncoder;
}
@Autowired
@Qualifier("userValidator")
private UserValidator userValidator;
@InitBinder("user")
private void initBinder(WebDataBinder binder) {
binder.setValidator(userValidator);
binder.registerCustomEditor(Role.class, "roles", new RoleEditor(roleService));
}
...
@RequestMapping(path = "/save", method = RequestMethod.POST)
public ModelAndView submit(@ModelAttribute("user") @Valid User user,
BindingResult result) {
...
}
...
}
用戶驗證器
@Component("userValidator")
public class UserValidator implements Validator {
private final UserRepository userRepository;
private final RoleRepository roleRepository;
@Autowired
public UserValidator(UserRepository userRepository, RoleRepository roleRepository) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
}
@Override
public boolean supports(Class<?> paramClass) {
return User.class.equals(paramClass);
}
@Override
public void validate(Object obj, Errors errors) {
User user = (User) obj;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "username.required", "Enter username");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "firstName.required", "Enter user first name");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "lastName.required", "Enter user last name");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "password.required", "Enter user password");
User userToFind = userRepository.findByUsername(user.getUsername()).orElse(new User());
if (Objects.isNull(user.getId()) && Objects.nonNull(userToFind.getId())) {
errors.rejectValue("username", "username.already.exist", "user with this username already exists");
}
if (Objects.isNull(user.getRoles())) {
errors.rejectValue("roles", "role.required", "role must be assigned");
}
Role adminRole = roleRepository.getAdminRole();
if (Objects.nonNull(user.getId())
&& Objects.nonNull(user.getRoles())
&& !user.getRoles().contains(adminRole)
&& userRepository.findUsersWithAdministratorRole().size() == 1) {
errors.rejectValue("roles", "admin.required", "at least one admin role must exist");
}
}
public ErrorMessage validateUserToDelete(UUID id) {
ErrorMessage errorMessage = new ErrorMessage();
List<String> errors = new ArrayList<>();
Set<User> admins = userRepository.findUsersWithAdministratorRole();
if (admins.size() == 1) {
errors.add(String.format("User with email %s is the one with Admin role. Impossible to delete last Admin user."
, userRepository.findById(id).get().getUsername()));
}
errorMessage.setErrors(errors);
return errorMessage;
}
}
用戶
@Entity
@Table(name = "users")
public class User {
private UUID id;
private String username;
private String password;
private String firstName;
private String lastName;
private BigDecimal money;
private Set<Role> roles;
private Set<Product> products;
@Id
@Type(type="org.hibernate.type.PostgresUUIDType")
@Column(name = "id", columnDefinition = "uuid")
@GeneratedValue(strategy = GenerationType.AUTO)
public UUID getId() {
return id;
}
public void setId(UUID id) {
this.id = id;
}
@Column(name = "username")
@NotEmpty
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Column(name = "password")
@NotEmpty
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Column(name = "first_name")
@NotEmpty
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
@Column(name = "last_name")
@NotEmpty
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
@Column(name = "money")
@DecimalMin(value = "0.0", inclusive = false)
@Digits(integer = 10, fraction = 2)
public BigDecimal getMoney() {
return money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
@ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.REFRESH})
@JoinTable(
name = "users_roles",
joinColumns = { @JoinColumn(name = "user_id") },
inverseJoinColumns = { @JoinColumn(name = "role_id") }
)
@NotNull
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
@ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.REFRESH})
@JoinTable(
name = "checkout",
joinColumns = { @JoinColumn(name = "user_id") },
inverseJoinColumns = { @JoinColumn(name = "product_id") }
)
public Set<Product> getProducts() {
return products;
}
public void setProducts(Set<Product> products) {
this.products = products;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return id.equals(user.id) && username.equals(user.username) && password.equals(user.password) && firstName.equals(user.firstName) && lastName.equals(user.lastName);
}
@Override
public int hashCode() {
return Objects.hash(id, username, password, firstName, lastName);
}
}
用戶.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>
<!DOCTYPE html>
<html>
<head>
<c:import url="${contextPath}/WEB-INF/jsp/header.jsp"/>
</head>
<body>
<c:import url="${contextPath}/WEB-INF/jsp/navibar.jsp"/>
<div class="container">
<div class="row">
<security:authorize access="hasRole('ROLE_ADMIN')">
<div class="btn-toolbar" role="toolbar" aria-label="Toolbar with button groups">
<div class="btn-group me-2" role="group" aria-label="Second group">
<a href="/users" type="button" class="btn btn-success">Back to users</a>
</div>
</div>
</security:authorize>
</div><br>
<form:form action="/users/save" method="post" modelAttribute="user">
<div class="form-group">
<div class="row">
<form:label path="id">User ID:</form:label><br>
<form:input path="id" type="UUID" readonly="true" class="form-control" id="id" placeholder="User ID" name="id" value="${user.id}"/>
<br>
<form:label path="username">username (email address):</form:label><br>
<form:input path="username" type="text" class="form-control" id="username" placeholder="Enter username" name="username" value="${user.username}"/>
<form:errors path="username" cssClass="error"/>
<br>
<form:label path="password">Password:</form:label><br>
<form:input path="password" type="text" class="form-control" id="password" placeholder="Enter password" name="password" value="${user.password}"/>
<form:errors path="password" cssClass="error"/>
<br>
<form:label path="firstName">First name:</form:label><br>
<form:input path="firstName" type="text" class="form-control" id="firstName" placeholder="Enter first name" name="firstName" value="${user.firstName}"/>
<form:errors path="firstName" cssClass="error"/>
<br>
<form:label path="lastName">First name:</form:label><br>
<form:input path="lastName" type="text" class="form-control" id="lastName" placeholder="Enter last name" name="lastName" value="${user.lastName}"/>
<form:errors path="lastName" cssClass="error"/>
<br>
<form:label path="money">Money:</form:label><br>
<form:input path="money" type="number" class="form-control" id="money" placeholder="Enter money" name="money" value="${user.money}"/>
<form:errors path="money" cssClass="error"/>
<br>
<security:authorize access="hasRole('ROLE_ADMIN')">
<form:label path="roles">Roles:</form:label><br>
<c:forEach items="${roles}" var="role">
<form:checkbox path="roles" id="${roles}" label="${role.name}" value="${role}"/></td>
</c:forEach>
<br>
<form:errors path="roles" cssClass="error"/><br><br>
</security:authorize>
</div>
<div class="row">
<div class="btn-toolbar" role="toolbar" aria-label="Toolbar with button groups">
<div class="btn-group me-2" role="group" aria-label="Second group">
<form:button type="submit" value="Submit" class="btn btn-primary">Save</form:button>
</div>
</div>
</div>
</div>
</form:form>
</div>
</body>
</html>
uj5u.com熱心網友回復:
如果您從除錯器中查看類名,您com.intellias.testmarketplace.controller.UserController$$EnhancerBySpringCGLIB$$309171b7會看到$$EnhancerBySpringCGLIB. 那部分表明Spring正在生成一個代理。可以出于各種原因生成代理,但控制器最常見的一個是安全性,我@PreAuthorize想到了注釋。另一種選擇是@Timed在控制器上進行監控的注釋。
由于您的initBinder方法是,private這將直接在代理上呼叫,而不是在實際的 bean 實體上。這是由于創建了一個類代理,它通過擴展你的類來作業。正在生成具有相同簽名的方法并呼叫攔截器,然后將呼叫傳遞給實際方法。但是,這僅適用于publicandprotected方法,不適用于privateorfinal方法。這些將直接在空代理上呼叫(這些欄位null不需要依賴項)。
正如您的方法一樣,private它是在代理上呼叫的。代理沒有設定欄位,所以userValidator是null. 因此有效地沒有Validator設定WebDataBinder,因此沒有驗證將并且可以完成。
要修復,請創建方法public(或protected),以便在實際 bean 而不是代理上正確呼叫它。
對于未來,根據經驗,您可能希望控制器中帶有注釋的方法public無論如何都不是“私有的”。
uj5u.com熱心網友回復:
問題在于方法的私有訪問修飾符與某些方法的注釋initBinder相結合。非常感謝@M.Deinum 的耐心和幫助。@PreAuthorizeUserController
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/504201.html
