是什么导致“java.lang.illegalstateexception:bindingresult和bean name'command'的普通目标对象都不能作为请求属性使用”?

czfnxgou  于 2021-06-29  发布在  Java
关注(0)|答案(5)|浏览(398)

这是一篇关于这类问题的广泛的规范问答文章。
我正在尝试编写一个springmvcweb应用程序,用户可以在其中向内存中的集合添加电影名称。是这样配置的

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

eqqqjvef

eqqqjvef1#

在我的例子中,它通过添加 modelAttribute="movie" 并在属性前面加上模型名,类似于 <form:input path="filmName" type="text" id="movie.name" />

hs1ihplo

hs1ihplo2#

从springversion3更新到springversion5会产生相同的错误。我的代码中已经给出了所有的答案。添加注解 @ControllerAdvice 帮我解决了这个问题。

r6hnlfcb

r6hnlfcb3#

为了简化form标签的使用,只需添加一个“commandname”,这是一个可怕的名称,它实际上是在寻找什么…它想要你在mdelattribute注解中命名的对象。所以在本例中commandname=“movie”。
这样你就不用读冗长的解释了,朋友。

368yc8dk

368yc8dk4#

我在一个有多个表单进行搜索的屏幕上遇到了这个错误。每个窗体都将结果发布到自己的控制器方法,结果显示在同一屏幕上。
问题:我没有在每个控制器方法中添加另外两个窗体作为模型属性,这导致在屏幕呈现结果时出现错误。

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";
}
n1bvdmb6

n1bvdmb65#

您正在尝试使用SpringMVC的表单标记。
此标记呈现html form 标记并向内部标记公开绑定路径以进行绑定。它将命令对象放入 PageContext 以便内部标记可以访问命令对象。[..]
假设我们有一个名为 User . 它是一个具有如下属性的javabean firstName 以及 lastName . 我们将使用它作为返回 form.jsp .
换句话说,springmvc将提取一个command对象,并将其类型用作绑定的蓝图 path 的表达式 form 的内部标记,如 input 或者 checkbox ,以呈现html form 元素。
此命令对象也称为模型属性,其名称在 form 标签 modelAttribute 或者 commandName 属性。您在jsp中省略了它

<form:form>

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

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

默认属性名为 command (您在错误消息中看到的内容)。模型属性是一个对象,通常是一个pojo或pojo的集合,应用程序将其提供给springmvc堆栈,springmvc堆栈将其公开给您的视图(即mvc中的m到v)。
springmvc在一个数据库中收集所有模型属性 ModelMap (它们都有名字)并且,在jsp的情况下,将它们转移到 HttpServletRequest 属性,其中jsp标记和el表达式可以访问它们。
在你的例子中,你的 @Controller 处理 GET 通往小路 /movies 添加单个模型属性

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

然后转到 index.jsp . 然后,这个jsp尝试呈现

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

渲染时, FormTag (实际上 InputTag )尝试查找名为 command (默认属性名),以便它可以生成html <input> 带有 namepath 表达式和相应的属性值,即 Movie#getFilmName() .
因为它找不到它,所以抛出您看到的异常

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

jsp引擎捕捉到它并用500状态码响应。如果你想利用 Movie 为了简单地正确构造表单,可以使用

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

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

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

或者,包括 @ModelAttribute 在您的 @Controller

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

请注意,springmvc将调用此方法,并隐式地将返回的对象添加到由封闭 @Controller .
你可能已经从这个描述中猜到了Spring form 标记更适合呈现html <form> 从现有对象中,使用实际值。如果您只想创建一个空白 <form> ,可能更适合自己构造,而不依赖于任何模型属性。

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

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

常见错误

如我们所见, FormTag 查找名为 command 默认情况下,或使用 modelAttribute 或者 commandName . 确保你用的是正确的名字。 ModelMap 有一个 addAttribute(Object) 添加
为此提供的属性 Map 使用生成的名称。
大会的目的地
返回[属性的]的未大写短名称 Class ,根据javabeans属性命名规则:所以, com.myapp.Product 变成 product ; com.myapp.MyProduct 变成 myProduct ; com.myapp.UKProduct 变成 UKProduct 如果您正在使用这个(或类似的)方法,或者如果您正在使用 @RequestMapping 表示模型属性的受支持的返回类型,请确保生成的名称是预期的名称。
另一个常见的错误是绕过 @Controller 方法。典型的spring mvc应用程序遵循以下模式:
发送http get请求 DispatcherServlet 选择 @RequestMapping 处理请求的方法
handler方法生成一些模型属性并返回视图名称 DispatcherServlet 将模型属性添加到 HttpServletRequest 并将请求转发到视图名称对应的jsp
jsp呈现响应
如果由于某些配置错误,您跳过了 @RequestMapping 方法,则不会添加属性。这是可能发生的
如果您的http请求uri直接访问您的jsp资源,例如,因为它们是可访问的,即在外部 WEB-INF ,或
如果 welcome-list 你的 web.xml 包含您的jsp资源,servlet容器将直接呈现它,完全绕过springmvc堆栈
不管怎样,你都想 @Controller 以便适当地添加模型属性。

这和bindingresult有什么关系?

BindingResult 是用于初始化或验证模型属性的容器。springmvc文档说明
这个 Errors 或者 BindingResult 参数必须跟随立即绑定的模型对象,因为方法签名可能有多个模型对象,spring将创建一个单独的 BindingResult 每个示例[…]
换句话说,如果你想用 BindingResult ,它必须遵循 @RequestMapping 方法

@RequestMapping(path = "/movies", method = RequestMethod.POST)
public String upload(@ModelAttribute("movie") Movie movie, BindingResult errors) {
``` `BindingResult` 对象也被视为模型属性。springmvc使用一个简单的命名约定来管理它们,这样就很容易找到相应的常规模型属性。自从 `BindingResult` 包含有关模型属性的更多数据(例如,验证错误) `FormTag` 尝试先绑定到它。然而,由于它们是齐头并进的,一个不可能没有另一个而存在。

相关问题