当前位置: 首页 > 知识库问答 >
问题:

是什么导致"java.lang.IllegalStateException: BindingResult和普通目标对象的bean名称'命令'可作为请求属性"?

农鸿德
2023-03-14

这是一个广泛的经典问题

我正在尝试编写一个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;
    }
}

com中只有一个@Controller类。示例软件包

@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包含

<%@ 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>

应用程序配置了上下文路径/示例。当我向

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生成一个HTML


共有3个答案

厉成仁
2023-03-14

我在一个有多个表单进行搜索的屏幕上出现了这个错误。每个表单都发布到自己的控制器方法,结果显示在同一个屏幕上。

问题:我没有在每个控制器方法中添加其他两个表单作为模型属性,导致在屏幕呈现结果时出现错误。

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";
}
西门磊
2023-03-14

为了简化表单标签的使用,只需添加一个“commandName”,这是一个可怕的名称,它实际上是在寻找什么。。。它需要您在MdelAttribute注释中命名的对象。所以在本例中,commandName=“movie”。

这样你就不用读冗长的解释了,朋友。

端木元青
2023-03-14

您正在尝试使用Spring MVC的表单标记。

这个标签呈现一个HTML表单标签,并公开一个到内部标签的绑定路径以进行绑定。它将命令对象放在PageContext中,这样就可以通过内部标记访问命令对象。[..]

假设我们有一个名为User的域对象。它是一个JavaBean,具有firstNamelastName等属性。我们将使用它作为表单控制器的表单支持对象,它返回表单。jsp

换句话说,Spring MVC将提取一个命令对象,并将其类型用作绑定表单内部标记的路径表达式的蓝图,如输入复选框,以呈现HTML表单元素。

该命令对象也称为模型属性,其名称在表单标记的模型属性命令名属性中指定。您在JSP中忽略了它

<form:form> 

你可以明确地指定一个名字。这两者是等价的。

<form:form modelAttribute="some-example-name">
<form:form commandName="some-example-name">

注意:Spring 5已删除命令名称属性,请参阅此处的升级说明。

默认属性名为命令(您在错误消息中看到的内容)。模型属性是一个对象,通常是一个POJO或POJO集合,应用程序提供给Spring MVC堆栈,Spring MVC堆栈向您的视图公开该对象(即MVC中的M到V)。

Spring MVC收集ModelMap中的所有模型属性(它们都有名称),并且在JSP的情况下,将它们传输到HttpServletRequest属性,其中JSP标签和EL表达式可以访问它们。

在您的示例中,您的@Controller处理路径/电影GET的处理程序方法添加了单个模型属性

model.addAttribute("movies", movies); // not named 'command'

然后转发到索引。jsp。然后,该JSP尝试呈现

<form:form>
    ...
    <form:input path="name" type="text" id="name" />
    ...
</form:form>

在呈现该属性时,FormTag(实际上,InputTag)尝试查找名为command(默认属性名称)的模型属性,以便生成HTML

因为它找不到它,所以会抛出您看到的异常

java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute

JSP引擎捕捉到它并用500状态代码进行响应。如果您想利用电影POJO来简单地正确构造表单,可以使用

model.addAttribute("movie", new Movie());

或者让Spring MVC为您创建并添加一个(必须有一个可访问的无参数构造函数)

@RequestMapping(path = "/movies", method = RequestMethod.GET)
public String homePage(@ModelAttribute("command") Movie movie, Model model) {...}

或者,在您的@Controller类中包含一个@Model属性注释的方法

@ModelAttribute("command")
public Movie defaultInstance() {
    Movie movie = new Movie();
    movie.setFilmName("Rocky II");
    return movie;
}

请注意,Spring MVC将调用此方法并为封闭的@Controller处理的每个请求隐式添加返回到其模型属性的对象。

从这个描述中,您可能已经猜到Spring的表单标记更适合呈现HTML

<form method="post" action="${pageContext.request.contextPath}/movies">
    <input name="filmName" type="text" />
    <input type="submit" value="Upload" />
</form>

在接收端,您的POST处理程序方法仍然能够提取filmName输入值,并使用它初始化电影对象。

如我们所见,FormTag在默认情况下查找名为command的模型属性,或者使用modeldattributecommandName中指定的名称。确保你用的是正确的名字。

ModelMap有一个add属性(Object)方法,它添加了

使用生成的名称为该映射提供的属性。

大会将在哪里举行

根据JavaBeans属性命名规则:So,com,返回[attribute's]类的未大写短名称。我的应用。产品成为产品<代码>com。我的应用。我的产品变成了我的产品<代码>com。我的应用。UKProduct变成UKProduct

如果正在使用此(或类似)方法,或者正在使用表示模型属性的@RequestMapping支持的返回类型之一,请确保生成的名称符合预期。

另一个常见错误是完全绕过您的@Controller方法。典型的Spring MVC应用程序遵循这种模式:

  1. 发送HTTP GET请求
  2. DispatcherServlet选择@RequestMapping方法来处理请求
  3. Handler方法生成一些模型属性并返回视图名称
  4. DispatcherServlet将模型属性添加到HttpServletRequest中,并将请求转发到视图名称对应的JSP
  5. JSP呈现响应

如果由于一些错误配置,您完全跳过了@RequestMapping方法,那么这些属性将不会被添加。这是可能发生的

  • 如果您的HTTP请求URI直接访问您的JSP资源,例如。因为它们是可访问的,即。在WEB-INF之外,或
  • 如果web.xml欢迎列表包含您的JSP资源,Servlet容器将直接呈现它,完全绕过Spring MVC堆栈

无论如何,您希望调用@Controller,以便适当地添加模型属性。

BindingResult是用于初始化或验证模型属性的容器。Spring MVC文档说明

ErrorsBindingResult参数必须跟随立即绑定的模型对象,因为方法签名可能有多个模型对象,Spring将为每个模型对象创建一个单独的BindingResult实例[…]

换句话说,如果你想使用BindingResult,它必须遵循@Request estMap方法中对应的模型属性参数

@RequestMapping(path = "/movies", method = RequestMethod.POST)
public String upload(@ModelAttribute("movie") Movie movie, BindingResult errors) {

BindingResult对象也被视为模型属性。Spring MVC使用一个简单的命名约定来管理它们,从而很容易找到相应的常规模型属性。由于BindingResult包含更多关于模型属性的数据(例如验证错误),FormTag会首先尝试绑定到它。然而,由于它们是齐头并进的,不太可能有一个没有另一个就存在。

 类似资料: