首页 > 解决方案 > 春季启动 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 列

标签: spring-bootspring-mvcspring-data-jpathymeleaf

解决方案


我解决了我的问题。代码和映射控制器视图没有错误。问题是由于我使用 admin/users/addUser 而不是 admin/users/addUser/ 进行控制器测试。谢谢@Simon Martinelli。


推荐阅读