【问题标题】:Which bean shall I put in the model?我应该在模型中放入哪个 bean?
【发布时间】:2015-06-06 16:14:24
【问题描述】:

我有一个从不同表单接收数据的控制器。 我手动将此数据绑定到相应的 Java 对象。 我使用额外的路径变量获得了正确的类。 我还验证了提交的数据。现在我想看看错误 在我的 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 sn-p 显示它们,我得到了异常:

<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)

我做错了什么?

【问题讨论】:

  • 只是为了确定,你能展示你的 getter 和 setter 吗?另外,你用的是什么版本的spring。
  • 它们是由 STS 生成的,我已经检查过它们的签名是否正确。

标签: java spring spring-mvc thymeleaf


【解决方案1】:

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

@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;
}

【讨论】:

    【解决方案2】:

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

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

    Spring 会自动绑定 bindingResult 对象和你验证过的对象,如果出现任何错误,你应该返回到同一个视图,你会从 BindingResult 中得到错误。

    【讨论】:

    • 当然可以,但是控制器必须处理不同的表单支持 bean。因此我无法使用 Spring 的自动绑定。
    猜你喜欢
    • 2021-07-14
    • 1970-01-01
    • 2013-02-14
    • 2011-03-22
    • 2016-04-29
    • 1970-01-01
    • 1970-01-01
    • 2013-06-24
    • 1970-01-01
    相关资源
    最近更新 更多