Что вызывает "java.lang.IllegalStateException: ни BindingResult, ни обычный целевой объект для bean name" command ", доступный как атрибут запроса"?
Это должно быть обширным каноническим вопросом и ответом на эти вопросы.
Я пытаюсь написать веб-приложение Spring MVC, где пользователи могут добавлять имена фильмов в коллекцию в памяти. Он сконфигурирован так
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.example
есть один класс
@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>
Приложение настроено на путь контекста /Example
. Когда я отправляю запрос GET на
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 <form>
с одним текстовым вводом для имени Movie
и кнопки отправки, которую я могу использовать для отправки запроса POST с помощью нового Movie
. Почему вместо этого сервлет JSP не может отображать тег Spring <form:form>
?
Ответы
Ответ 1
Вы пытаетесь использовать тег формы Spring MVC.
Этот тег отображает тег form
HTML и предоставляет путь привязки к внутренним тегам для привязки. Он помещает объект команды в PageContext
так, чтобы к объекту команды можно было обращаться по внутренним тегам. [..]
Допустим, у нас есть объект домена, называемый User
. Это JavaBean со свойствами, такими как firstName
и lastName
. Мы будем использовать его как объект поддержки формы нашего контроллера формы, который возвращает form.jsp
.
Другими словами, Spring MVC будет извлекать объект команды и использовать его тип в качестве плана для связывания выражений path
для внутренних тегов form
, таких как input
или checkbox
, для визуализации элемента form
HTML.
Этот объект команды также называется атрибутом модели и ее имя указано в form
тега modelAttribute
или commandName
атрибутов. Вы опустили его в своем JSP
<form:form>
Вы могли бы указать имя явно. Оба они эквивалентны.
<form:form modelAttribute="some-example-name">
<form:form commandName="some-example-name">
Имя атрибута по умолчанию - это command
(что вы видите в сообщении об ошибке). Атрибут model - это объект, обычно POJO или набор POJO, который ваше приложение поставляет в стек Spring MVC и который стек Spring MVC предоставляет вашему представлению (т.е. M к V в MVC).
Spring MVC собирает все атрибуты модели в ModelMap
(все они имеют имена), а в случае JSP передает их атрибутам HttpServletRequest
, где к ним имеют доступ теги JSP и выражения EL.
В вашем примере ваш @Controller
обработчика @Controller
который обрабатывает GET
для пути /movies
добавляет один атрибут модели
model.addAttribute("movies", movies); // not named 'command'
а затем index.jsp
к index.jsp
. Затем JSP пытается отобразить
<form:form>
...
<form:input path="name" type="text" id="name" />
...
</form:form>
Во время рендеринга FormTag
(в действительности, InputTag
) пытается найти атрибут model с именем command
(имя атрибута по умолчанию), чтобы он мог создать элемент HTML <input>
с атрибутом name
построенным из выражения path
и соответствующим свойством значение, т.е. результат Movie#getFilmName()
.
Поскольку он не может найти его, он выдает исключение, которое вы видите
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
Двигатель JSP ловит его и отвечает кодом состояния 500. Если вы хотите использовать Movie
POJO, чтобы просто правильно построить свою форму, вы можете явно добавить атрибут модели с помощью
model.addAttribute("movie", new Movie());
или создать Spring Spring MVC и добавить его для вас (должен иметь доступный конструктор без параметров)
@RequestMapping(path = "/movies", method = RequestMethod.GET)
public String homePage(@ModelAttribute("command") Movie movie, Model model) {...}
Альтернативно, @ModelAttribute
аннотированный метод @Controller
в свой класс @Controller
@ModelAttribute("command")
public Movie defaultInstance() {
Movie movie = new Movie();
movie.setFilmName("Rocky II");
return movie;
}
Обратите внимание, что Spring MVC вызовет этот метод и неявно добавит объект, возвращенный его атрибутам модели, для каждого запроса, обрабатываемого @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
конфигурации вы вообще пропустите метод @RequestMapping
, атрибуты не будут добавлены. Это может случиться
- если ваш URI-запрос HTTP напрямую обращается к вашим ресурсам JSP, например. потому что они доступны, т.е. вне
WEB-INF
или - если
welcome-list
вашего web.xml
содержит ваш ресурс JSP, контейнер Servlet будет отображать его напрямую, полностью обходя стек Spring MVC
Так или иначе, вы хотите, чтобы ваш @Controller
был вызван, чтобы атрибуты модели были добавлены соответствующим образом.
Что BindingResult
имеет к этому отношение?
BindingResult
- это контейнер для инициализации или проверки атрибутов модели. В документации Spring MVC
Параметры Errors
или BindingResult
должны следовать объекту модели, который привязан сразу, поскольку подпись метода может иметь более одного объекта модели, а Spring создаст отдельный экземпляр BindingResult
для каждого из них [...]
Другими словами, если вы хотите использовать BindingResult
, он должен следовать соответствующему параметру атрибута модели в методе @RequestMapping
@RequestMapping(path = "/movies", method = RequestMethod.POST)
public String upload(@ModelAttribute("movie") Movie movie, BindingResult errors) {
Объекты BindingResult
также считаются атрибутами модели. Spring MVC использует простое соглашение об именах для управления ими, что позволяет легко найти соответствующий регулярный атрибут модели. Поскольку BindingResult
содержит больше данных об атрибуте модели (например, ошибки проверки), FormTag
пытается связать с ним сначала. Однако, поскольку они идут рука об руку, маловероятно, что один будет существовать без другого.
Ответ 2
Чтобы сделать вещи простыми с тегом формы, просто добавьте "commandName", которое является ужасным именем для того, что он действительно ищет... он хочет объект, который вы назвали в аннотации MdelAttribute. Итак, в этом случае commandName = "movie".
Это спасет вас, читая долгое объяснение друзей.
Ответ 3
В моем случае это сработало путем добавления modelAttribute="movie"
в тег формы и добавления имени модели к атрибуту, что-то вроде <form:input path="filmName" type="text" id="movie.name"/>
Ответ 4
Ни BindingResult, ни простой целевой объект для имени компонента "команда" не доступны в качестве атрибута запроса
В этой статье мы обсуждаем ни Spring, ни BindingResult, ни простой целевой объект для имени компонента "команда", доступный в качестве атрибута запроса. Это частое исключение в любом веб-проекте Spring mvc.
Давайте посмотрим, когда это исключение происходит с примером:
Класс EmployeeController:
@Controller
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
EmployeeRepository employeeRepository;
@GetMapping(value="/add")
public String getAddEmployeePage(Model model) {
model.addAttribute("employee", new Employee());
return "employees/add";
}
}
Страница add.jsp:
<form:form action="${updateUrl}" method="post">
Теперь внимательно прочитайте Exception StackTrace.
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:153)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:173)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:193)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:159)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:118)
at org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.java:422)
at org.springframework.web.servlet.tags.form.InputTag.writeTagContent(InputTag.java:345)
at org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java:86)
at org.springframework.web.servlet.tags.RequestContextAwareTag.doStartTag(RequestContextAwareTag.java:83)
at org.apache.jsp.WEB_002dINF.views.employees.add_jsp._jspx_meth_form_005finput_005f0(add_jsp.java:420)
at org.apache.jsp.WEB_002dINF.views.employees.add_jsp._jspService(add_jsp.java:240)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:444)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:386)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:330)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:712)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:459)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:384)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:312)
at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:170)
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:314)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1325)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1069)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1008)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:634)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:494)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:651)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:407)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:754)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1376)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
Мы проиллюстрируем возможные причины этой проблемы и доступные решения.
Причина исключения: java.lang.IllegalStateException: ни BindingResult, ни простой целевой объект для имени компонента "команда" не доступны в качестве атрибута запроса.
Самая частая причина этого исключения заключается в том, что мы не связывали наш объект имени команды в jsp с тегом form: form.
Как мы видим из приведенного выше класса EmployeeController, у нас есть один метод getAddEmployeePage (Model model), в котором мы связали данные employee в объекте модели с ключевым именем employee, а затем вызываем страницу add.jsp. на java.lang.Thread.run (неизвестный источник)
model.addAttribute("employee", new Employee());
return "employees/add";
<form:form action="${updateUrl}" method="post">
В приведенном выше теге "form: form" мы не взяли ни одного атрибута "commandName" или "modelAttribute", и именно об этом говорится в трассировке стека исключений, что он не может найти какой-либо атрибут команды на странице add.jsp.
Решение: Если мы изменим тег form: form с помощью приведенного ниже кода, он будет работать правильно.
<form:form action="${updateUrl}" method="post" modelAttribute="employee">
Если мы запустим этот код после внесения определенных изменений, тогда наш проект будет работать без каких-либо исключений. Нажмите на ссылку, чтобы получить дополнительную информацию: https://www.solutionfactory.in/posts/Neither-BindingResult-nor-plain-target-object-for-bean-name-command-available-as-request-attribute
Ответ 5
У меня была эта ошибка на экране с несколькими формами, которые выполняют поиск. Каждая форма отправляет свой собственный метод контроллера с результатами, отображаемыми на том же экране.
Проблема: я пропустил добавление двух других форм в качестве атрибутов модели в каждом методе контроллера, что вызывало эту ошибку при отображении экрана с результатами.
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";
}
Ответ 6
Добавьте атрибут модели (название модели, которую вы указали в контроллере), чтобы связать вашу форму. Вы должны добавить следующие атрибуты в свою форму
<form:form modelAttribute="movies" method="post">
Ответ 7
Обновление с Spring версии 3 до версии Spring версии 5 приводит к той же ошибке. Все ответы были удовлетворены уже в моем коде. Добавление аннотации @ControllerAdvice
решило проблему для меня.