spring-boot - 春季启动 2.0.3。REALESE 和 Thymeleaf 数据绑定
问题描述
我有这个错误,我看不到 BindingResult 或 bean 名称 'utilisateur' 的普通目标对象在哪里可用作请求属性问题。
这是我的实体类:
@Entity
@ToString(exclude = "roles")
@EqualsAndHashCode(exclude = { "id", "roles" })
@Table(name = "users", schema = "ciwara", uniqueConstraints = {
@UniqueConstraint(columnNames = { "id" }) })
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
@EntityListeners(AuditingEntityListener.class)
public class Utilisateur implements Serializable, Principal {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column
private long id;
/*
* @NotNull
*
* @Pattern(regexp = Constants.LOGIN_REGEX)
*
* @Size(min = 1, max = 100)
*
* @Column(length = 100, unique = true, nullable = false) private String login;
*/
@Column
@NotBlank
@NotNull
@Size(min = 2, message = "Please specify a valid first name", max = 60)
private String firstname;
@Column
@NotBlank
@NotNull
@Size(min = 2, message = "Please specify a valid last name", max = 60)
private String lastname;
@Column
@NotBlank
@NotNull
@Size(min = 8, message = "Please specify a valid password", max = 60)
private String password;
@Column(nullable = false, unique = true)
@NotBlank
@NotNull
@Size(max = 60)
@javax.validation.constraints.Email
/*@Pattern(regexp = Constants.LOGIN_REGEX)*/
private String email;
@Column(columnDefinition = "boolean default true")
public Boolean enabled;
@Column(insertable = false, updatable = true)
@CreatedDate
@DateTimeFormat(pattern="dd/MM/yyyy")
private LocalDateTime creatTime;
@Column(insertable = false, updatable = true)
@DateTimeFormat(pattern="dd/MM/yyyy")
@LastModifiedDate
private LocalDateTime modifyTime;
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@JoinTable(name = "ciwara.user_role", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
@Builder.Default
private Set<Role> roles = new HashSet<>();
public Boolean getEnabled() {
return enabled;
}
public void setEbnabled(Boolean enabled) {
this.enabled = enabled;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
@Override
public String getName() {
String fullName = "";
fullName = getFirstname() + " " + getLastname();
return fullName;
}
public LocalDateTime getCreatTime() {
return creatTime;
}
public void setCreatTime(LocalDateTime creatTime) {
this.creatTime = creatTime;
}
public LocalDateTime getModifyTime() {
return modifyTime;
}
public void setModifyTime(LocalDateTime modifyTime) {
/*long modifityToMilliSeconds= modifyTime.getTime();
long createTimeToMilliSeconds= creatTime.getTime();
if ((modifityToMilliSeconds-createTimeToMilliSeconds)<=0) {
throw new IllegalStateException("modification date cannot be null.");
}*/
this.modifyTime = modifyTime;
}
}
和视图模型:
<!DOCTYPE html>
<html lang="fr" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title th:text="#{messages.nouveau.utilisateur}"></title>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="shortcut icon" th:href="@{/resources/favicon.ico}">
<link rel="stylesheet" type="text/css" media="all"
th:href="@{/resources/css/bootstrap.min.css}"></link>
<!-- Bootstrap Core CSS -->
<link rel="stylesheet" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
<link rel="shortcut icon" th:href="@{/resources/favicon.ico}">
<link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}"
rel="stylesheet">
<link
th:href="@{https://fonts.googleapis.com/icon?family=Material+Icons}"
rel="stylesheet">
<!--Bootstrap CSS-->
<link href="resources/ui/assets/bootstrap/css/bootstrap.min.css"
rel="stylesheet">
<link href="resources/ui/assets/params/css/starter-template.css"
rel="stylesheet">
<link th:href="@{/webjars/bootstrap/css/bootstrap.min.css}"
rel="stylesheet">
<!-- font Awesome -->
<!-- Place your kit's code here -->
<script src="https://kit.fontawesome.com/95381e9240.js"
crossorigin="anonymous"></script>
</head>
<body>
<!--Template Header-->
<div th:include="fragments/header :: header"></div>
<div class="card card-container">
<h1 class="indigo-text center"
th:text="#{messages.nouveau.utilisateur}">Créer un utilisateur</h1>
<br> <br>
<th:**form action="admin/users/add-user.html"
th:action="@{/admin/users/addUser}" th:object="${utilisateur}" method="post"**
class="col m8 s12 offset-m2" acceptCharset="UTF-8">
<input type="hidden" **th:field="*{id}"** name="id" />
<div class="control-group">
<div class="controls">
<label class="control-label" for="firstname">Nom</label> <input
class="input-xlarge" type="text" **th:field="*{firstname}"**
id="firstname" placeholder="Nom" required="required"
th:errorclass="invalid" name="firstname" min="2" maxlength="60" />
<span th:if="${#fields.hasErrors('firstname')}"
th:errors="*{firstname}" class="red-text"
th:text="${utilisateur != null} ? ${#strings.toUpperCase(utilisateur.firstname)} : 'utilisateur is null'">
</span> <br> <br>
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="control-label" for="lastname">Prénom</label> <input
class="input-xlarge" type="text" **th:field="*{lastname}"**
id="lastname" placeholder="Prénom" required="required"
th:errorclass="invalid" name="lastname" min="2" maxlength="60" />
<span th:if="${#fields.hasErrors('lastname')}"
th:errors="*{lastname}" class="red-text"></span><br>
</div>
<br>
</div>
<div class="control-group">
<div class="controls">
<br> <label class="control-label" for="email">Email</label> <input
class="input-xlarge" type="email" th:field="*{email}" id="email"
placeholder="Email" required="required" th:errorclass="invalid"
name="email" maxlength="60" /> <span
th:if="${#fields.hasErrors('email')}" th:errors="*{email}"
class="input-group-addon" id="basic-addon1">@</span>
</div>
<br>
<div class="control-group">
<div class="controls">
<label for="password">Password</label> <input class="input-xlarge"
type="password" th:field="*{password}" id="password"
placeholder="password" required="required"
th:errorclass="invalid" name="password" min="8" maxlength="60" />
<span th:if="${#fields.hasErrors('password')}"
th:errors="*{password}" class="red-text"></span><br>
</div>
<br>
<div class="control-group">
<div class="controls">
<label class="control-label" for="dateCreateTime">Date de
saisie</label> <input class="input-xlarge" type="datetime-local"
th:field="*{creatTime}" id="dateCreateTime"
placeholder="date de saisie" required="required"
th:errorclass="invalid" name="creatTime" /> <span
th:if="${#fields.hasErrors('creatTime')}"
th:errors="*{creatTime}" class="red-text"></span><br>
</div>
</div>
<br>
<div class="control-group">
<div class="controls">
<label class="control-label" for="modifyTime">Date de
Modification</label> <input class="input-xlarge" type="datetime-local"
th:field="*{modifyTime}" id="modifyTime"
placeholder="Date de modification" required="required"
th:errorclass="invalid" type="hidden" /> <span
th:if="${#fields.hasErrors('modifyTime')}"
th:errors="*{modifyTime}" class="red-text"></span><br>
</div>
</div>
<br>
<div class="controls">
<label class="form-control-label" for="ROLES">ROLES:</label> <select
class="form-control" id="ROLES" name="roles" multiple="multiple">
<option th:each="role : ${allRoles}" th:value="${{role}}"
th:text="${role.name}">Type de Role</option>
</select>
</div>
<br>
<div class="control-group">
<div class="controls">
<label class="control-label" for="enabled">Active</label> <input class="input-xlarge" type="radio"
th:field="*{enabled}" id="enabled"
placeholder="active" required="required"
th:errorclass="invalid" type="hidden" /> <span
th:if="${#fields.hasErrors('enabled')}"
th:errors="*{enabled}" class="red-text"></span><br>
</div>
</div>
</div>
</div>
<br>
<div class="controls">
<button type="submit" name="submit" value="Ajouter"
class="btn btn-primary" role="alert">
Submit <i class="mdi-content-send right"></i>
</button>
</div>
</div>
<br>
<!--Template footer-->
<!-- <div th:include="/fragments/footer :: footer"></div> -->
</body>
</html>
最后是控制器类:
@Controller
@ControllerAdvice
@RequestMapping(value = "/admin/users")
public class UsersController {
private final static Logger LOGGER = LoggerFactory.getLogger(UsersController.class);
private final UsersService userService;
private final UtilisateurFormValidation userFormValidation;
private final RoleService roleService;
private PasswordEncoder passwordEncoder;
private final int ROW_PER_PAGE = 30;
private String homePageView = "/home/home";
private String updateUserView = "/admin/users/updateUser";
private String addUserView = "/admin/users/add-user";
private String listAllUsersView = "/admin/users/list-user";
@Autowired
public UsersController(UsersService userService, UtilisateurFormValidation userFormValidation,
RoleService roleService, PasswordEncoder passwordEncoder) {
this.userService = userService;
this.userFormValidation = userFormValidation;
this.roleService = roleService;
this.passwordEncoder=passwordEncoder;
}
@RequestMapping(value = {"/addUser"}, method = RequestMethod.GET)
public ModelAndView addUser(Utilisateur utilisateur) {
ModelAndView modelAndView = new ModelAndView(addUserView);
modelAndView.addObject("utilisateur", new Utilisateur());
return getUserModelAndView(modelAndView, utilisateur);
}
@RequestMapping(value = {"/addUser"}, method = RequestMethod.POST)
public ModelAndView addUser(@Valid @ModelAttribute(value="utilisateur") final Utilisateur utilisateur,
final BindingResult result,
ModelAndView modelAndView,
RedirectAttributes redirectAttrs) {
userFormValidation.validate(utilisateur, result);
if (utilisateur.getEmail() == null &&
userService.findByEmail(utilisateur.getEmail()) != null) {
result.rejectValue("login", "exist.user.login");
}
if (result.hasErrors()) {
FieldError fieldError = result.getFieldError();
LOGGER.info("Code:" + fieldError.getCode() + ", object:"
+ fieldError.getObjectName() + ", field:"
+ fieldError.getField());
return getUserModelAndView(modelAndView, utilisateur);
}
//if admin don't select role
if (utilisateur.getRoles() == null) {
Set<Role> role = new HashSet<>();
role.add(roleService.getRoleByName("ROLE_USER"));
utilisateur.setRoles(role);
}
long userId=userService.createUser(utilisateur);
utilisateur.setId(userId);
modelAndView = new ModelAndView(addUserView);//forward mode by default
//ModelAndView mv = new ModelAndView("redirect:/user/save/result");//redirect mode
modelAndView.addObject("utilisateur", utilisateur);
modelAndView.addObject("message","Save user successfully!");
redirectAttrs.addFlashAttribute("success", "Save user successfully:" + utilisateur);
return new ModelAndView("redirect:" + listAllUsersView);
}
/**
* @param pageNumber
* @return
*/
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView listAllUsers(@RequestParam(value = "page", defaultValue = "1") int pageNumber) {
long count = userService.getUtilisateurs().size();
List<Utilisateur> usersList = userService.getUtilisateurs();
usersList.sort(Comparator.comparing(Utilisateur::getId));
LOGGER.debug("Users list: {}", usersList.toArray());
ModelAndView modelAndView = new ModelAndView(listAllUsersView);
boolean hasPrev = pageNumber > 1;
boolean hasNext = (pageNumber * ROW_PER_PAGE) < count;
modelAndView.addObject("hasPrev", hasPrev);
modelAndView.addObject("hasNext", hasNext);
modelAndView.addObject("prev", pageNumber - 1);
modelAndView.addObject("next", pageNumber + 1);
modelAndView.setViewName("list-user");
modelAndView.addObject("userList", usersList);
return new ModelAndView(listAllUsersView, "usersList", usersList);
}
@RequestMapping(value = { "/updateUser/{id}", "/updateUser"}, method = RequestMethod.PUT)
public ModelAndView updateUser(@PathVariable("id") long id,
@ModelAttribute("utilisateur") Utilisateur utilisateur) {
ModelAndView modelAndView = new ModelAndView(updateUserView);
try {
utilisateur.setId(id);
userService.update(id, utilisateur);
modelAndView.addObject("utilisateur", utilisateur);
return getUserModelAndView(modelAndView, utilisateur); //new ModelAndView("redirect:/user/updateUser/" + utilisateur.getId());
} catch (Exception e) {
String errorMessage = e.getMessage();
LOGGER.info("errorMessage", errorMessage);
modelAndView.addObject("errorMessage", errorMessage);
modelAndView.addObject("utilisateur", false);
return modelAndView; //new ModelAndView("redirect:/user/add-user");
}
}
@RequestMapping(value = "/updateUser/{id}", method= RequestMethod.GET)
public ModelAndView editeUser(@PathVariable long id) {
if (id <0) {
throw new ResourceNotFoundException("User with id:{} " + id + "cannot be edit");
}
ModelAndView modelAndView = new ModelAndView();
try {
Optional<Utilisateur> userId = userService.findUserById(id);
modelAndView.addObject("utilisateur", false);
modelAndView.addObject("utilisateur", userId);
} catch (IllegalAccessException e) {
LOGGER.info(e.getMessage());
}
return modelAndView; //new ModelAndView("redirect:/user/add-user");
}
@RequestMapping(value = "/deleteUser/{id}", method = RequestMethod.DELETE)
public String deletUserById(@PathVariable("id") long id, ModelAndView modelAndView, RedirectAttributes redirectAttrs) {
try {
Utilisateur user = userService.findUserById(id)
.orElseThrow(() -> new IllegalAccessException("User" + "" + id));
userService.findUserById(user.getId());
redirectAttrs.addFlashAttribute("success", "Delete user: " + user);
return "redirect:/user/listUser";
} catch (IllegalAccessException e) {
LOGGER.info(e.getMessage());
}
return listAllUsersView;
}
@GetMapping(value = "/deleteUser/{id}")
public ModelAndView showDeletUserById(ModelAndView modelAndView, @PathVariable long id) {
try {
Optional<Utilisateur> utilisateurId = userService.findUserById(id);
modelAndView.addObject("allowDelete", true);
modelAndView.addObject("delteUser", utilisateurId);
modelAndView.setViewName("user");
} catch (IllegalAccessException e) {
modelAndView.addObject("errorMessage", "Cannot found user");
LOGGER.info(e.getMessage());
}
new ModelAndView("redirect:/admin/user/add-user");
return new ModelAndView(listAllUsersView);
}
/**
* @param modelAndView
* @param utilisateur
* @return modelAndView
*/
private ModelAndView getUserModelAndView(ModelAndView modelAndView, Utilisateur utilisateur) {
modelAndView.addObject("currentRoles", utilisateur.getRoles());
modelAndView.addObject("allRoles", roleService.getAllRoles());
modelAndView.addObject("utilisateur", new Utilisateur());
return modelAndView;
}
@InitBinder("utilisateur")
protected void initBinder(WebDataBinder binder) {
StringTrimmerEditor stringTrimmerEditor= new StringTrimmerEditor(true);
binder.registerCustomEditor(String.class, stringTrimmerEditor);
binder.setValidator(userFormValidation);
binder.validate();
}
}
当我运行 addUser 方法时,我得到以下错误 强调文本This application has no explicit mapping for /error,因此您将其视为后备。
25-06-2020 17:39:13.983 [http-nio-8080-exec-1] 调试 osweb.servlet.DispatcherServlet.traceDebug-GET "/admin/users/add-user",参数={} 25-06- 2020 17:39:13.988 [http-nio-8080-exec-1] 调试 oswshSimpleUrlHandlerMapping.getHandler - 映射到 ParameterizableViewController [] 25-06-2020 17:39:14.350 [http-nio-8080-exec-1] 错误组织.thymeleaf.TemplateEngine.process - [THYMELEAF][http-nio-8080-exec-1] 异常处理模板“admin/users/add-user”:模板解析时出错(模板:“ServletContext资源[/WEB- INF/views/admin/users/add-user.html]") org.thymeleaf.exceptions.TemplateInputException: 模板解析时出错(模板:"ServletContext 资源 [/WEB-INF/views/admin/users/add- user.html]") 在 org.thymeleaf.templateparser.markup。AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:241) at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parseStandalone(AbstractMarkupTemplateParser.java:100) at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:666)
原因:org.attoparser.ParseException:在 org.attoparser.MarkupParser 处执行处理器“org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor”(模板:“admin/users/add-user” - 第 52 行,第 25 列)时出错.parseDocument(MarkupParser.java:393) at org.attoparser.MarkupParser.parse(MarkupParser.java:257) at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230) ... 省略了 81 个常用帧原因:org.thymeleaf.exceptions.TemplateProcessingException:在 org.thymeleaf 处执行处理器“org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor”(模板:“admin/users/add-user” - 第 52 行,第 25 列)时出错.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:117) 在 org。thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95)
原因:java.lang.IllegalStateException:在 org.springframework 的 org.springframework.web.servlet.support.BindStatus.(BindStatus.java:153) 中,Bean 名称 'utilisateur' 的 BindingResult 和普通目标对象都不能用作请求属性。 web.servlet.support.RequestContext.getBindStatus(RequestContext.java:903) at org.thymeleaf.spring5.context.webmvc.SpringWebMvcThymeleafRequestContext.getBindStatus(SpringWebMvcThymeleafRequestContext.java:227) at org.thymeleaf.spring5.util.FieldUtils.getBindStatusFromParsedExpression( FieldUtils.java:306) 在 org.thymeleaf.spring5.util.FieldUtils.getBindStatus(FieldUtils.java:253) 在 org.thymeleaf.spring5.util.FieldUtils.getBindStatus(FieldUtils.java:227) 在 org.thymeleaf.spring5 .processor.AbstractSpringFieldTagProcessor.doProcess(AbstractSpringFieldTagProcessor.java:174) 在 org.thymeleaf.processor。element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74) ... 96 个常用帧省略 25-06-2020 17:39:14.350 [http-nio-8080-exec-1] 调试 osweb.servlet.DispatcherServlet.render - 错误渲染视图 [org.thymeleaf.spring5.view.ThymeleafView@2010278a] org.thymeleaf.exceptions.TemplateInputException: 模板解析时出错(模板:“ServletContext 资源 [/WEB-INF/views/admin/users/add-user .html]")ServletContext 资源 [/WEB-INF/views/admin/users/add-user.html]")ServletContext 资源 [/WEB-INF/views/admin/users/add-user.html]")
25-06-2020 17:39:14.352 [http-nio-8080-exec-1] 调试 osweb.servlet.DispatcherServlet.logResult - 无法完成请求:org.thymeleaf.exceptions.TemplateInputException:模板解析期间发生错误(模板:“ServletContext 资源 [/WEB-INF/views/admin/users/add-user.html]”)25-06-2020 17:39:14.355 [http-nio-8080-exec-1] 错误 occC [。 [.[.[dispatcherServlet].log - Servlet.service() 用于路径 [] 上下文中的 servlet [dispatcherServlet] 引发异常 [请求处理失败;嵌套异常是 org.thymeleaf.exceptions.TemplateInputException:模板解析期间发生错误(模板:“ServletContext 资源 [/WEB-INF/views/admin/users/add-user.html]”)],根本原因是 java.lang .IllegalStateException:Bean 名称“utilisateur”的 BindingResult 和普通目标对象都不是
25-06-2020 17:39:14.358 [http-nio-8080-exec-1] 调试 osweb.servlet.DispatcherServlet.traceDebug - GET“/error”的“错误”调度,参数={} 25-06-2020 17:39:14.359 [http-nio-8080-exec-1] 调试 oswsmmaRequestMappingHandlerMapping.getHandler - 映射到 org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#errorHtml(HttpServletRequest, HttpServletResponse) 25-06-2020 17:39:14.381 [http-nio-8080-exec-1] 调试 osweb.servlet.DispatcherServlet.logResult -退出“错误”调度,状态 500
原因:org.attoparser.ParseException:在 org.attoparser.MarkupParser 处执行处理器“org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor”(模板:“admin/users/add-user” - 第 52 行,第 25 列)时出错.parseDocument(MarkupParser.java:393) at org.attoparser.MarkupParser.parse(MarkupParser.java:257) at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230) ... 81 更多:org.thymeleaf.exceptions.TemplateProcessingException:执行处理器'org.thymeleaf.spring5.processor.SpringInputGeneralFieldTagProcessor'时出错(模板:“admin/users/add-user” - 第 52 行,第 25 列)
解决方案
我解决了我的问题。代码和映射控制器视图没有错误。问题是由于我使用 admin/users/addUser 而不是 admin/users/addUser/ 进行控制器测试。谢谢@Simon Martinelli。
推荐阅读
- c# - 使用命令行获取 ProcessId
- c++ - 在 Windows 用户模式驱动程序中使用 C++ 标准库
- scala - 使用预凝胶寻找循环
- javascript - 按字符串从对象中提取数据
- javascript - 带有 QueryString 的 URL 未命中 MVC 控制器方法
- sql - 解析第一个中间名和姓氏
- python - Django-REST-Framework:ForeignKey 实例未传递给 valid_data
- xamarin.android - 为面向 API 28 的 Xamarin.Android 项目构建管道
- bash - 如何创建安装程序 bash 脚本?
- regex - Powershell正则表达式匹配第一次出现字符串后的所有内容