简体   繁体   中英

Java, Spring, JSP, <form>. Neither BindingResult nor plain target object for bean name 'command' available as request attribute

Hey, everyone!

I can't understand why it happens when I try to open webapp:

Neither BindingResult nor plain target object for bean name 'command' available as request attribute

and

org.apache.jasper.JasperException: An exception occurred processing JSP page /index.jsp at line 53

Here is form from index.jsp

51 <c:url var="addAction" value="/email/add.form"/>
52 <form:form action="${addAction}" commandName="command">
53    <form:input path="email" type="email" placeholder="Enter email" class="cell"/>
54    <input type="submit" class="button" value="<spring:message text="Send"/>"/>
55 </form:form>

Here is Controller

@Controller
public class EmailController {
    private EmailService emailService;

    @Autowired
    @Qualifier("emailService")
    public void setEmailService(EmailService emailService) {
        this.emailService = emailService;
    }

    @RequestMapping(value = "/email/add", method = RequestMethod.POST)
    public String addEmail(@ModelAttribute("email") EmailEntity emailEntity) {
        this.emailService.addEmail(emailEntity);
        return "redirect:/email.form";
    }
}

The following is Stacktrace and Root Cause

Stacktrace:
    org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:584)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:476)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
Root Cause

java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
    org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:144)
    org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:168)
    org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:188)
    org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:154)
    org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.autogenerateId(AbstractDataBoundFormElementTag.java:141)
    org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.resolveId(AbstractDataBoundFormElementTag.java:132)
    org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:116)
    org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.java:422)
    org.springframework.web.servlet.tags.form.InputTag.writeTagContent(InputTag.java:142)
    org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java:84)
    org.springframework.web.servlet.tags.RequestContextAwareTag.doStartTag(RequestContextAwareTag.java:80)
    org.apache.jsp.index_jsp._jspx_meth_form_005finput_005f0(index_jsp.java:323)
    org.apache.jsp.index_jsp._jspx_meth_form_005fform_005f0(index_jsp.java:271)
    org.apache.jsp.index_jsp._jspService(index_jsp.java:184)
    org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:443)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:385)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:329)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

Sorry, if something has been missed. Thanks!

Why does this happen?

The complete answer is here . This is just my easier explanation for Spring beginners.

The short answer is, that the Spring form does not work without a "form backing object" or "command object", as it is also called in the Spring docs.

The index.jsp should be returned by a controller method, that makes a " bean name 'command' available to the form as request attribute " as stated in the exception.

--- Long answer ---

The Spring form documentation reads:

"...form:form tag renders an HTML 'form' tag and exposes a binding path to inner tags for binding. It puts the command object in the PageContext so that the command object can be accessed by inner tags ..."

This means, that the inner tags (like form:input) need a 'command object' because they want to bind to it.

So, what is this "command object" (also known as the form backing object) and where does it come from?

Read this a couple of times:

  • The form backing object is the bean/entity that the form is dealing with - like for example the form addItem.jsp is dealing with an entity "Item". The entity/command object comes from the the method in the form controller which returns the form .

  • The form backing object/bean/entity is going to have its properties set in the form , but it needs to be available already when we show the empty form.

We will solve our problem in two steps:

  1. add the form backing object to the model in controller method that shows the form, to make it accessible for the form - (for example) addItem.jsp

      @RequestMapping("/add-item") public String showAddItemForm(Model model) { // adding the form backing object/entity - here for example named item // to the model "as request attribute" (see the exception in the question) model.addAttribute("item", new Item()); return "item/addItem"; } 
  2. add the name of the form backing object to the form:form tag in the addItem.jsp like this:

    <form:form modelAttribute="item">

To understand what is happening in the step 2., read the explanation below:

If we run the app after step 1, normally we should still get the same exception: IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute

(It is possible, that after adding an entity named 'command' to the code in the question above, it starts to work, because the commandName="command" works the same way as the modelAttribute="command" - see explanation below.)

What is this - bean named 'command' ? Read the docs or the excerpt:

...docs.spring.io/spring/docs/current/spring-framework-reference/html/view.html#view-jsp-formtaglib-formtag

...

<form:form>
  ...
</form:form>

... The preceding JSP assumes that the variable name of the form backing object is 'command'. If you have put the form backing object into the model under another name (definitely a best practice), then you can bind the form to the named variable like so:

<form:form modelAttribute="user">

...

This means:

A: a form:form will try to find a form backing object named 'command' in the model, even if it is not explicitly writen in the form:form tag

<form:form> really means <form:form modelAttribute="command">

In this case, the form will not work if the form backing object/entity named 'command' is not available in the model.

B: When sending the form backing object/bean/entity to the form (packaged in the model), we must either:

  • name it 'command'

    or - (definitely a best practice) -

  • name it whatewer we want, like in the sample code above model.addAttribute("item", new Item()); but then we must explicitly write the name of the form backing object in the form:form tag like this:

<form:form modelAttribute="item">

The reason for the "Neither BindingResult nor plain target object..." exception may be:

  • we did not put our form backing object named 'command' into the model in the controller method that shows the form

  • we did not name our form backing object 'command' when we put it into the model and we did not tell the form to look in the model for the form backing object named 'item', 'user', or whatever 'myFormBackingObjectName' we used, when we put the form backing object/bean/entity into the model.

That happens in the step 2:

  1. add the name of our form backing object to the form:form tag like this:

<form:form modelAttribute="myFormBackingObjectName">

This means, tell the form, what name we used for our entity/form backing object.


Your index.jsp is probably not being returned by a method in the controller. At least, the code is not showing it. But that alone would still not be enough, that method would also have to make available a form backing object/entity, named 'command', in other words, save an object named 'command' in the model.

Since you have commandName="command" in your form:form tag (that is the older way of saying modelAttribute="command") the form is trying to find the 'command' object in the model as a request attribute. But the calling method in controller must first save this entity in the model, to make it available for the form.

If this is clear, further reading is this answer to the same question.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM