我应该将哪个 bean 放入模型中?

Which bean shall I put in the model?

我有一个控制器可以接收来自不同表单的数据。 我手动将此数据绑定到相应的 Java 对象。 我使用附加路径变量获得了正确的 class。 我还验证了提交的数据。现在我想看看错误 在我的 Thymeleaf 模板中,但我不知道必须是哪个对象 放在模型中作为表单支持 bean。据我了解 它应该是 BindingResult,但这给了我 NotReadablePropertyException.

以下是我的控制器:

@Controller
public class MyController {

    @Autowired
    private BeanRegistry beanRegistry;

    @Autowired
    private ValidatorRegistry validatorRegistry;

    @RequestMapping(value = "/{name}", method = RequestMethod.POST)
    public String submitForm(@PathVariable("name") final String name, final WebRequest request, final Model model) {
        final Object bean = beanRegistry.lookup(name);
        final Validator validator = validatorRegistry.lookup(name);
        final WebRequestDataBinder binder = new WebRequestDataBinder(bean);

        binder.setValidator(validator);
        binder.validate();
        binder.bind(request);

        final BindingResult result = binder.getBindingResult();

        if (result.hasErrors()) {
            model.addAttribute("name", name);
            model.addAttribute("bean", result);

            return "form";
        }

        return "redirect:/" + name;
    }

}

绑定和验证工作正常。但是当我的表格有错误时 并尝试使用以下 Thymeleaf 片段显示它们,我得到异常:

<ul th:if="${#fields.hasErrors('*')}">
    <li th:each="err : ${#fields.errors('*')}" th:text="${err}" />
</ul>

假设有以下 bean:

public class NameBean {

    private String firstName;

    private String lastName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(final String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(final String lastName) {
        this.lastName = lastName;
    }

}

和一个检查两个字段是否包含数据的验证器。当我提交一个空表格时, 我得到以下堆栈跟踪:

org.springframework.beans.NotReadablePropertyException: Invalid property 'firstName' of bean class [org.springframework.validation.BeanPropertyBindingResult]: Bean property 'firstName' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
    at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:731)
    at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:722)
    at org.springframework.validation.AbstractPropertyBindingResult.getActualFieldValue(AbstractPropertyBindingResult.java:99)
    at org.springframework.validation.AbstractBindingResult.getFieldValue(AbstractBindingResult.java:229)
    at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:120)
    at org.thymeleaf.spring4.util.FieldUtils.getBindStatusFromParsedExpression(FieldUtils.java:396)
    at org.thymeleaf.spring4.util.FieldUtils.getBindStatus(FieldUtils.java:323)
    at org.thymeleaf.spring4.util.FieldUtils.getBindStatus(FieldUtils.java:289)
    at org.thymeleaf.spring4.processor.attr.AbstractSpringFieldAttrProcessor.processAttribute(AbstractSpringFieldAttrProcessor.java:98)
    at org.thymeleaf.processor.attr.AbstractAttrProcessor.doProcess(AbstractAttrProcessor.java:87)
    at org.thymeleaf.processor.AbstractProcessor.process(AbstractProcessor.java:212)
    at org.thymeleaf.dom.Node.applyNextProcessor(Node.java:1017)
    at org.thymeleaf.dom.Node.processNode(Node.java:972)
    at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:695)
    at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:668)
    at org.thymeleaf.dom.Node.processNode(Node.java:990)
    at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:695)
    at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:668)
    at org.thymeleaf.dom.Node.processNode(Node.java:990)
    at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:695)
    at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:668)
    at org.thymeleaf.dom.Node.processNode(Node.java:990)
    at org.thymeleaf.dom.NestableNode.computeNextChild(NestableNode.java:695)
    at org.thymeleaf.dom.NestableNode.doAdditionalProcess(NestableNode.java:668)
    at org.thymeleaf.dom.Node.processNode(Node.java:990)
    at org.thymeleaf.dom.Document.process(Document.java:93)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1155)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1060)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1011)
    at org.thymeleaf.spring4.view.ThymeleafView.renderFragment(ThymeleafView.java:335)
    at org.thymeleaf.spring4.view.ThymeleafView.render(ThymeleafView.java:190)
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1244)
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1027)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:971)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:85)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1521)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1478)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

我做错了什么?

据我所知,BindingResult 是一个Springclass 验证过程。您的表单支持 bean 是您的 class NameBean。在 POST 请求处理方法中你应该声明参数(方法参数):

 @RequestMapping(value = "/{name}", method = RequestMethod.POST)
public String submitForm(@Valid @ModelAttribute("yourBeanName") NameBean nameBean,  final BindingResult result, ...) {...}

Spring 将自动绑定 bindingResult 对象和您验证的对象,如果出现任何错误,您应该 return 到同一视图,您将从 BindingResult 中得到错误。

我还以为 BindingResult 也是 BeanWrapper。它不是,因此我得到了错误。我使用 BindingResult::getModel() 解决了这个问题。此方法 returns 一个包含 bean 和 BindingResult 的模型 Map。现在我的控制器看起来像这样:

@RequestMapping(value = "/{name}", method = RequestMethod.POST)
public String submitFormAction(@PathVariable("name") final String name, final WebRequest request, final Model model) {
    final Object bean = registry.lookup(name);
    final Validator validator = validatorRegistry.lookup(name);
    final WebRequestDataBinder binder = new WebRequestDataBinder(bean, "bean");

    binder.setValidator(validator);
    binder.validate();
    binder.bind(request);

    final BindingResult result = binder.getBindingResult();

    if (result.hasErrors()) {
        model.addAttribute("name", name);
        model.addAllAttributes(result.getModel());

        return "form";
    }

    return "redirect:/" + name;
}