什么原因导致“java.lang.IllegalStateException:对于 bean 名称‘command’既没有 BindingResult 也没有普通目标对象作为请求属性可用”?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/42198050/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What causes "java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute"?
提问by Sotirios Delimanolis
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
我正在尝试编写一个 Spring MVC Web 应用程序,用户可以在其中将电影名称添加到内存中的集合中。它是这样配置的
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
类在com.example
包
@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
WEB-INF/jsps/index.jsp
包含
<%@ 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
应用程序配置了上下文路径/Example
。当我向
http://localhost:8080/Example/movies
the request fails, Spring MVC responds with a 500 status code, and reports the following exception and stack trace
请求失败,Spring MVC 响应 500 状态码,并报告以下异常和堆栈跟踪
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?
我希望 JSP 生成一个<form>
带有单个文本输入、Movie
名称和提交按钮的 HTML ,我可以用它来发送带有新 .html 文件的 POST 请求Movie
。为什么 JSP servlet 反而无法呈现 Spring 的<form:form>
标记?
回答by Sotirios Delimanolis
You're trying to use Spring MVC's form tag.
您正在尝试使用Spring MVC 的表单标签。
This tag renders an HTML
form
tag and exposes a binding path to inner tags for binding. It puts the command object in thePageContext
so that the command object can be accessed by inner tags. [..]Let's assume we have a domain object called
User
. It is a JavaBean with properties such asfirstName
andlastName
. We will use it as the form backing objectof our form controller which returnsform.jsp
.
该标签呈现一个 HTML
form
标签并公开一个到内部标签的绑定路径以进行绑定。它将命令对象放在 中,PageContext
以便内部标签可以访问命令对象。[..]假设我们有一个名为 的域对象
User
。它与诸如属性一个JavaBeanfirstName
和lastName
。我们将使用它作为我们的表单控制器的 表单支持对象,它返回form.jsp
.
In other words, Spring MVC will extract a command objectand use its type as a blueprint for binding path
expressions for form
's inner tags, like input
or checkbox
, to render an HTML form
element.
换句话说,Spring MVC 将提取一个命令对象,并使用它的类型作为蓝图来绑定内部标记的path
表达式form
,例如input
或checkbox
,以呈现 HTMLform
元素。
This command objectis also called a model attribute and its name is specified in the form
tag's modelAttribute
or commandName
attributes. You've omitted it in your JSP
该命令对象也称为模型属性,其名称在form
标签的modelAttribute
或commandName
属性中指定。你在你的 JSP 中省略了它
<form:form>
You could've specified a name explicitly. Both of these are equivalent.
您可以明确指定一个名称。这两者是等价的。
<form:form modelAttribute="some-example-name">
<form:form commandName="some-example-name">
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).
在默认属性名称是command
(你在错误信息看)。模型属性是一个对象,通常是一个 POJO 或 POJO 的集合,您的应用程序将其提供给 Spring MVC 堆栈,并且 Spring MVC 堆栈将其公开给您的视图(即 MVC 中的 M 到 V)。
Spring MVC collects all model attributes in a ModelMap
(they all have names) and, in the case of JSPs, transfers them to the HttpServletRequest
attributes, where JSP tags and EL expressions have access to them.
Spring MVC 收集所有模型属性ModelMap
(它们都有名称),对于 JSP,将它们传输到HttpServletRequest
属性,在那里 JSP 标记和 EL 表达式可以访问它们。
In your example, your @Controller
handler method which handles a GET
to the path /movies
adds a single model attribute
在您的示例中,@Controller
处理GET
路径的处理程序方法/movies
添加了一个模型属性
model.addAttribute("movies", movies); // not named 'command'
and then forwards to the index.jsp
. This JSP then tries to render
然后转发到index.jsp
. 这个 JSP 然后尝试呈现
<form:form>
...
<form:input path="name" type="text" id="name" />
...
</form:form>
While rendering this, FormTag
(in reality, the InputTag
) tries to find a model attribute named command
(the default attribute name) so that it can produce an HTML <input>
element with a name
attribute constructed from the path
expression and the corresponding property value, ie. the result of Movie#getFilmName()
.
在渲染这个时,FormTag
(实际上,InputTag
)试图找到一个名为command
(默认属性名称)的模型属性,以便它可以生成一个 HTML<input>
元素,该元素具有name
从path
表达式和相应的属性值构造的属性,即。的结果Movie#getFilmName()
。
Since it cannot find it, it throws the exception you see
由于找不到它,它会抛出您看到的异常
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
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 with
JSP 引擎捕获它并以 500 状态代码响应。如果您想利用Movie
POJO 来简单地正确构建您的表单,您可以使用以下命令显式添加模型属性
model.addAttribute("movie", new Movie());
or have Spring MVC create and add one for you (must have an accessible parameterless constructor)
或者让 Spring MVC 为你创建并添加一个(必须有一个可访问的无参数构造函数)
@RequestMapping(path = "/movies", method = RequestMethod.GET)
public String homePage(@ModelAttribute("command") Movie movie, Model model) {...}
Alternatively, include a @ModelAttribute
annotated method in your @Controller
class
或者,@ModelAttribute
在您的@Controller
类中包含一个带注释的方法
@ModelAttribute("command")
public Movie defaultInstance() {
Movie movie = new Movie();
movie.setFilmName("Rocky II");
return movie;
}
Note 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
.
请注意,Spring MVC 将调用此方法,并将返回的对象隐式添加到由封闭@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.
您可能已经从这个描述中猜到,Spring 的form
标签更适合<form>
从现有对象呈现带有实际值的 HTML 。如果你想简单地创建一个 blank <form>
,自己构建它可能更合适,而不依赖于任何模型属性。
<form method="post" action="${pageContext.request.contextPath}/movies">
<input name="filmName" type="text" />
<input type="submit" value="Upload" />
</form>
On the receiving side, your POST
handler method, will still be able to extract the filmName
input value and use it to initialize a Movie
object.
在接收方,您的POST
处理程序方法仍将能够提取filmName
输入值并使用它来初始化Movie
对象。
Common Errors
常见错误
As we've seen, FormTag
looks for a model attribute named command
by default or with the name specified in either modelAttribute
or commandName
. Make sure you're using the right name.
正如我们所见,FormTag
查找command
默认命名的模型属性或在modelAttribute
或 中指定的名称commandName
。确保您使用正确的名称。
ModelMap
has a addAttribute(Object)
method which adds
ModelMap
有一种addAttribute(Object)
方法可以添加
the supplied attribute to this
Map
using a generatedname.
Map
使用生成的名称为此提供的属性。
where the general convention is to
一般约定在哪里
return the uncapitalized short name of the [attribute's]
Class
, according to JavaBeans property naming rules: So,com.myapp.Product
becomesproduct
;com.myapp.MyProduct
becomesmyProduct
;com.myapp.UKProduct
becomesUKProduct
Class
根据 JavaBeans 属性命名规则,返回 [attribute's] 的非大写短名称: 所以,com.myapp.Product
变成product
;com.myapp.MyProduct
变成myProduct
;com.myapp.UKProduct
变成UKProduct
If you're using this (or a similar) method or if you're using one of the @RequestMapping
supported return typesthat represents a model attribute, make sure the generated name is what you expect.
如果您正在使用此(或类似)方法,或者您正在使用代表模型属性的@RequestMapping
受支持返回类型之一,请确保生成的名称是您所期望的。
Another common error is to bypass your @Controller
method altogether. A typical Spring MVC application follows this pattern:
另一个常见错误是@Controller
完全绕过您的方法。典型的 Spring MVC 应用程序遵循以下模式:
- Send HTTP GET request
DispatcherServlet
selects@RequestMapping
method to handle request- Handler method generates some model attributes and returns view name
DispatcherServlet
adds model attributes toHttpServletRequest
and forwards request to JSP corresponding to view name- JSP renders response
- 发送 HTTP GET 请求
DispatcherServlet
选择@RequestMapping
处理请求的方法- Handler 方法生成一些模型属性并返回视图名称
DispatcherServlet
添加模型属性HttpServletRequest
并将请求转发到与视图名称对应的 JSP- JSP 呈现响应
If, by some misconfiguration, you skip the @RequestMapping
method altogether, the attributes will not have been added. This can happen
如果由于某些错误配置,您@RequestMapping
完全跳过该方法,则不会添加属性。这可能发生
- if your HTTP request URI accesses your JSP resources directly, eg. because they are accessible, ie. outside
WEB-INF
, or - if the
welcome-list
of yourweb.xml
contains your JSP resource, the Servlet container will render it directly, bypassing the Spring MVC stack entirely
- 如果您的 HTTP 请求 URI 直接访问您的 JSP 资源,例如。因为它们是可访问的,即。外面
WEB-INF
, 或 - 如果
welcome-list
您的web.xml
包含您的 JSP 资源,则 Servlet 容器将直接呈现它,完全绕过 Spring MVC 堆栈
One way or another, you want your @Controller
to be invoked so that the model attributes are added appropriately.
以一种或另一种方式,您希望您@Controller
被调用,以便适当地添加模型属性。
What does BindingResult
have to do with this?
什么是BindingResult
与此有关?
A BindingResult
is a container for initialization or validation of model attributes. The Spring MVC documentationstates
ABindingResult
是用于初始化或验证模型属性的容器。在Spring MVC的文档状态
The
Errors
orBindingResult
parameters have to follow the model object that is being bound immediately as the method signature might have more than one model object and Spring will create a separateBindingResult
instance for each of them [...]
该
Errors
或BindingResult
参数必须遵循被立即绑定方法签名可能有不止一个模型对象和Spring将创建一个单独的模型对象BindingResult
为他们每个人的情况下[...]
In other words, if you want to use BindingResult
, it has to follow the corresponding model attribute parameter in a @RequestMapping
method
换句话说,如果要使用BindingResult
,必须在@RequestMapping
方法中跟随对应的模型属性参数
@RequestMapping(path = "/movies", method = RequestMethod.POST)
public String upload(@ModelAttribute("movie") Movie movie, BindingResult errors) {
BindingResult
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 the BindingResult
contains more data about the model attribute (eg. validation errors), the FormTag
attempts to bind to it first. However, since they go hand in hand, it's unlikely one will exist without the other.
BindingResult
对象也被视为模型属性。Spring MVC 使用简单的命名约定来管理它们,从而可以轻松找到相应的常规模型属性。由于BindingResult
包含有关模型属性的更多数据(例如验证错误),FormTag
因此首先尝试绑定到它。然而,由于它们齐头并进,一个没有另一个就不太可能存在。
回答by Dotard
I had this error on a screen with multiple forms that do a search. Each form posts to its own controller method with results shown on same screen.
我在具有多个搜索表单的屏幕上出现此错误。每个表单发布到它自己的控制器方法,结果显示在同一屏幕上。
Problem: I missed adding the other two forms as model attributes in each controller method causing that error when screen renders with results.
问题:我错过了在每个控制器方法中添加其他两个表单作为模型属性,当屏幕呈现结果时导致该错误。
Form1 -> bound to Bean1 (bean1) -> Posting to /action1
Form2 -> bound to Bean2 (bean2) -> Posting to /action2
Form3 -> bound to Bean3 (bean2) -> Posting to /action3
@PostMapping
public String blah(@ModelAttribute("bean1") Bean1 bean, Model model){
// do something with bean object
// do not miss adding other 2 beans as model attributes like below.
model.addAttribute("bean2", new Bean2());
model.addAttribute("bean3", new Bean3());
return "screen";
}
@PostMapping
public String blahBlah(@ModelAttribute("bean2") Bean2 bean, Model model){
// do something with bean object
// do not miss adding other 2 beans as model attributes like below.
model.addAttribute("bean1", new Bean1());
model.addAttribute("bean3", new Bean3());
return "screen";
}
@PostMapping
public String blahBlahBlah(@ModelAttribute("bean3") Bean3 bean, Model model){
// do something with bean object
// do not miss adding other 2 beans as model attributes like below.
model.addAttribute("bean1", new Bean1());
model.addAttribute("bean2", new Bean2());
return "screen";
}
回答by Pasha Utt
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".
为了使表单标签变得简单,只需添加一个“commandName”,这对于它实际要查找的内容来说是一个可怕的名称……它需要您在 MdelAttribute 注释中命名的对象。所以在这种情况下,commandName="movie"。
That'll save you reading long winded explanations friend.
这将节省您阅读冗长的解释的朋友。
回答by Pedro Madrid
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" />
在我的情况下,它通过添加modelAttribute="movie"
到表单标签,并将模型名称添加到属性中来工作,例如<form:input path="filmName" type="text" id="movie.name" />
回答by shaqer74
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.
从 Spring 版本 3 更新到 Spring 版本 5,会产生相同的错误。我的代码中已经满足了所有答案。添加注释@ControllerAdvice
为我解决了问题。