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