This is meant to be an extensive canonical question & answer post for these types of questions.
I'm trying to write a Spring MVC web application where users can add movie names to an in-memory collection. It's configured like so
public class Application extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {};
}
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] { SpringServletConfig.class };
}
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
and
@Configuration
@ComponentScan("com.example")
public class SpringServletConfig extends WebMvcConfigurationSupport {
@Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
}
There's a single @Controller
class in the com.example
package
@Controller
public class MovieController {
private final CopyOnWriteArrayList<Movie> movies = new CopyOnWriteArrayList<>();
@RequestMapping(path = "/movies", method = RequestMethod.GET)
public String homePage(Model model) {
model.addAttribute("movies", movies);
return "index";
}
@RequestMapping(path = "/movies", method = RequestMethod.POST)
public String upload(@ModelAttribute("movie") Movie movie, BindingResult errors) {
if (!errors.hasErrors()) {
movies.add(movie);
}
return "redirect:/movies";
}
public static class Movie {
private String filmName;
public String getFilmName() {
return filmName;
}
public void setFilmName(String filmName) {
this.filmName = filmName;
}
}
}
WEB-INF/jsps/index.jsp
contains
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Movies</title>
</head>
<body>
Current Movies:
<c:forEach items="${movies}" var="movieItem">
<ul>
<li>${movieItem.filmName}</li>
</ul>
</c:forEach>
<form:form>
<div>Movie name:</div>
<form:input path="filmName" type="text" id="name" />
<input type="submit" value="Upload">
</form:form>
</body>
</html>
The application is configured with context path /Example
. When I send a GET request to
http://localhost:8080/Example/movies
the request fails, Spring MVC responds with a 500 status code, and reports the following exception and stack trace
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.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:117)
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.WEB_002dINF.jsps.index_jsp._jspx_meth_form_005finput_005f0(index_jsp.java:267)
org.apache.jsp.WEB_002dINF.jsps.index_jsp._jspx_meth_form_005fform_005f0(index_jsp.java:227)
org.apache.jsp.WEB_002dINF.jsps.index_jsp._jspService(index_jsp.java:142)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:438)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:396)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:340)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:168)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303)
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1257)
org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
I expected the JSP to generate an HTML <form>
with a single text input, for a Movie
name, and a submit button, that I can use to send a POST request with a new Movie
. Why does the JSP servlet instead fail to render Spring's <form:form>
tag?
You're trying to use Spring MVC's form tag.
In other words, Spring MVC will extract a command object and use its type as a blueprint for binding
path
expressions forform
's inner tags, likeinput
orcheckbox
, to render an HTMLform
element.This command object is also called a model attribute and its name is specified in the
form
tag'smodelAttribute
orcommandName
attributes. You've omitted it in your JSPYou could've specified a name explicitly. Both of these are equivalent.
The default attribute name is
command
(what you see in error message). A model attribute is an object, typically a POJO or collection of POJOs, that your application supplies to the Spring MVC stack and which the Spring MVC stack exposes to your view (ie. the M to the V in MVC).Spring MVC collects all model attributes in a
ModelMap
(they all have names) and, in the case of JSPs, transfers them to theHttpServletRequest
attributes, where JSP tags and EL expressions have access to them.In your example, your
@Controller
handler method which handles aGET
to the path/movies
adds a single model attributeand then forwards to the
index.jsp
. This JSP then tries to renderWhile rendering this,
FormTag
(in reality, theInputTag
) tries to find a model attribute namedcommand
(the default attribute name) so that it can produce an HTML<input>
element with aname
attribute constructed from thepath
expression and the corresponding property value, ie. the result ofMovie#getFilmName()
.Since it cannot find it, it throws the exception you see
The JSP engine catches it and responds with a 500 status code. If you want to take advantage of a
Movie
POJO to simply construct your form correctly, you can add a model attribute explicitly withor have Spring MVC create and add one for you (must have an accessible parameterless constructor)
Alternatively, include a
@ModelAttribute
annotated method in your@Controller
classNote that Spring MVC will call this method and implicitly add the object returned to its model attributes for each request handled by the enclosing
@Controller
.You may have guessed from this description that Spring's
form
tag is more suited for rendering an HTML<form>
from an existing object, with actual values. If you want to simply create a blank<form>
, it may be more appropriate to construct it yourself and not rely on any model attributes.On the receiving side, your
POST
handler method, will still be able to extract thefilmName
input value and use it to initialize aMovie
object.Common Errors
As we've seen,
FormTag
looks for a model attribute namedcommand
by default or with the name specified in eithermodelAttribute
orcommandName
. Make sure you're using the right name.ModelMap
has aaddAttribute(Object)
method which addswhere the general convention is to
If you're using this (or a similar) method or if you're using one of the
@RequestMapping
supported return types that represents a model attribute, make sure the generated name is what you expect.Another common error is to bypass your
@Controller
method altogether. A typical Spring MVC application follows this pattern:DispatcherServlet
selects@RequestMapping
method to handle requestDispatcherServlet
adds model attributes toHttpServletRequest
and forwards request to JSP corresponding to view nameIf, by some misconfiguration, you skip the
@RequestMapping
method altogether, the attributes will not have been added. This can happenWEB-INF
, orwelcome-list
of yourweb.xml
contains your JSP resource, the Servlet container will render it directly, bypassing the Spring MVC stack entirelyOne way or another, you want your
@Controller
to be invoked so that the model attributes are added appropriately.What does
BindingResult
have to do with this?A
BindingResult
is a container for initialization or validation of model attributes. The Spring MVC documentation statesIn other words, if you want to use
BindingResult
, it has to follow the corresponding model attribute parameter in a@RequestMapping
methodBindingResult
objects are also considered model attributes. Spring MVC uses a simple naming convention to manage them, making it easy to find a corresponding regular model attribute. Since theBindingResult
contains more data about the model attribute (eg. validation errors), theFormTag
attempts to bind to it first. However, since they go hand in hand, it's unlikely one will exist without the other.In my case, it worked by adding
modelAttribute="movie"
to the form tag, and prepending the model name to the attribute, something like<form:input path="filmName" type="text" id="movie.name" />
Add Model Attribute (name of model you specified in controller) to relate your form. You must add following attributes to your form
To make things simple with the form tag just add a "commandName" which is a horrible name for what it is actually looking for...it wants the object you named in the MdelAttribute annotation. So in this case commandName="movie".
That'll save you reading long winded explanations friend.
Updating from Spring version 3 to Spring version 5, produces the same error. All answers were satisfied already in my code. Adding the annotation
@ControllerAdvice
solved the problem for me.