对于这些类型的问题,这将是一个广泛的规范问题解答。
我正在尝试编写一个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[] { "/" }; } }
和
@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; } }
套餐中只有一个@Controller班级com.example
@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 包含
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>
该应用程序配置了上下文路径/Example。当我发送GET请求到
/Example
http://localhost:8080/Example/movies
请求失败,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)
我希望JSP生成<form>带有单个文本输入(用于Movie名称)和提交按钮的HTML ,可用于发送带有new的POST请求Movie。为什么JSP Servlet却无法呈现Spring的<form:form>标签?
<form>
Movie
<form:form>
您正在尝试使用Spring MVC的form标签。
此标签呈现HTML form标签,并向内部标签公开绑定路径以进行绑定。它将命令对象放入中,PageContext 以便内部标签可以访问该命令对象。[..] 假设我们有一个名为的域对象User。它是具有诸如firstName和属性的JavaBean lastName。我们将其用作返回的表单控制器的 表单支持对象form.jsp。
此标签呈现HTML form标签,并向内部标签公开绑定路径以进行绑定。它将命令对象放入中,PageContext 以便内部标签可以访问该命令对象。[..]
form
PageContext
假设我们有一个名为的域对象User。它是具有诸如firstName和属性的JavaBean lastName。我们将其用作返回的表单控制器的 表单支持对象form.jsp。
User
firstName
lastName
form.jsp
换句话说,Spring MVC将提取 命令对象 并将其类型用作绑定内部标记(如或)的path表达式的蓝图,以呈现HTML 元素。forminputcheckboxform
path
input
checkbox
该 命令对象 也称为模型属性,其名称在form标签modelAttribute或commandName属性中指定。您已在JSP中省略了它
modelAttribute
commandName
您可以明确指定一个名称。这两个都是等效的。
<form:form modelAttribute="some-example-name"> <form:form commandName="some-example-name">
在默认属性名称是command(你在错误信息看)。模型属性是对象,通常是POJO或POJO的集合,您的应用程序将其提供给Spring MVC堆栈,并且Spring MVC堆栈将其公开给您的视图(即MVC中的M到V)。
command
Spring MVC将所有模型属性收集ModelMap在一起(它们都有名称),对于JSP,将它们转移到HttpServletRequest属性中,在此JSP标签和EL表达式可以访问它们。
ModelMap
HttpServletRequest
在您的示例中,@Controller用于处理GET路径的处理程序方法/movies添加了一个模型属性
GET
/movies
model.addAttribute("movies", movies); // not named 'command'
然后转发到index.jsp。然后,此JSP尝试渲染
index.jsp
<form:form> ... <form:input path="name" type="text" id="name" /> ... </form:form>
在呈现此代码时,FormTag(实际上是InputTag)会尝试查找名为command(默认属性名称)的模型属性,以便它可以生成具有由表达式和相应属性值(即)构成<input>的name属性的HTML 元素path。的结果Movie#getFilmName()。
FormTag
InputTag
<input>
name
Movie#getFilmName()
由于找不到它,它会引发您看到的异常
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
JSP引擎将其捕获并以500状态代码响应。如果您想利用MoviePOJO简单地正确构造表单,则可以使用以下命令显式添加模型属性
model.addAttribute("movie", new Movie());
或让Spring MVC为您创建并添加一个(必须具有可访问的无参数构造函数)
@RequestMapping(path = "/movies", method = RequestMethod.GET) public String homePage(@ModelAttribute("command") Movie movie, Model model) {...}
或者,@ModelAttribute在您的@Controller班级中添加带注释的方法
@ModelAttribute
@ModelAttribute("command") public Movie defaultInstance() { Movie movie = new Movie(); movie.setFilmName("Rocky II"); return movie; }
请注意,Spring MVC将调用此方法,并针对由封装处理的每个请求,将返回的对象隐式添加到其模型属性中@Controller。
您可能从此描述中猜测到,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>
在接收方,您的POST处理程序方法仍将能够提取filmName输入值并将其用于初始化Movie对象。
POST
filmName
正如我们所看到的,FormTag查找名为一个模型属性command默认或名称在任一指定modelAttribute或commandName。确保使用正确的名称。
ModelMap有一个addAttribute(Object)增加
addAttribute(Object)
Map使用生成的名称为其提供的属性。
Map
一般惯例是
返回[属性的]的小写形式的短名称Class,根据JavaBeans属性的命名规则:那么,com.myapp.Product变得 product; com.myapp.MyProduct成为myProduct; com.myapp.UKProduct 变成UKProduct
Class
com.myapp.Product
product
com.myapp.MyProduct
myProduct
com.myapp.UKProduct
UKProduct
如果使用的是这种(或类似的)方法,或者使用的是代表模型属性的@RequestMapping 受支持的返回类型之一,请确保生成的名称符合您的期望。
@RequestMapping
另一个常见的错误是@Controller完全绕过您的方法。典型的Spring MVC应用程序遵循以下模式:
DispatcherServlet
如果由于某种错误配置而@RequestMapping完全跳过了该方法,则将不会添加属性。这可能发生
WEB-INF
welcome-list
web.xml
您希望@Controller以一种或另一种方式调用自己,以便适当地添加模型属性。
BindingResult
A BindingResult是用于初始化或验证模型属性的容器。在Spring MVC的文档状态
该Errors或BindingResult参数必须遵循被立即绑定方法签名可能有不止一个模型对象和Spring将创建一个单独的模型对象 BindingResult为他们每个人的情况下[…]
Errors
换句话说,如果要使用BindingResult,则必须在@RequestMapping方法中遵循相应的model属性参数
@RequestMapping(path = "/movies", method = RequestMethod.POST) public String upload(@ModelAttribute("movie") Movie movie, BindingResult errors) {
BindingResult对象也被视为模型属性。Spring MVC使用简单的命名约定来管理它们,从而可以轻松地找到相应的常规模型属性。由于BindingResult包含有关模型属性的更多数据(例如,验证错误),FormTag因此首先尝试绑定到该属性。但是,由于它们并存,因此没有一个就不可能存在。