What causes “java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'command' available as request attribute”?
(6 个回答)
4年前关闭。
我正在尝试在 MultiActionController 上应用表单验证(我知道 Controller 类现在已被弃用)。
我找到了这个答案(这让我更接近我的目标,但并不完全):
How to perform Spring validation in MultiActionController?
好的,所以根据
javadoc ,异常处理程序方法是带有参数
(HttpServletRequest request, HttpServletResponse response, ExceptionClass exception)
的 Controller 方法。
据我所知(如果我错了,请纠正我),它的工作流程如下:spring dispatcher-servlet 转到 Controller 的请求方法,如果在执行过程中发生异常(例如由于验证失败而导致绑定(bind)异常),它将转到其异常参数与发生的异常匹配的异常处理程序方法(如果有这样的异常处理程序方法)。
但与常规 Controller 的方法不同,此异常处理程序方法没有命令对象参数。
所以我的问题是如何在该方法中访问与发生绑定(bind)异常(由于验证错误)的请求一起发送的命令对象?
例如,当我通过注释使用验证时,我在请求处理程序方法中访问
(作为方法参数) 到 BindingResult 和命令对象,因此如果出现验证错误,我可以使用命令对象加载返回的 ModelAndView数据。
但是,使用我的异常处理程序方法(在我的 MultiActionController 中),以
BindException bindException = (BindException) bindingException.getRootCause();
return new ModelAndView("myFormView").addAllObjects(bindException.getModel());
— 提交无效数据后,我收到异常,因为找不到命令对象,因此无法呈现我的 JSP View (“myFormView”)。
谢谢!
更多信息:
我的 Controller (SearchBookController) 中的实际请求处理程序方法如下所示:
public ModelAndView list(HttpServletRequest request, HttpServletResponse response, Book book) throws Exception {
ModelMap modelMap = new ModelMap();
//getting a list of books according to the propertiest of the command object book...
modelMap.addAttribute("bookList", bookDAO.listBooks(book));
return new ModelAndView("bookForm", modelMap);
}
我还向 Controller 添加了以下异常处理程序方法:
public ModelAndView hanldeBindException(HttpServletRequest request, HttpServletResponse response, ServletRequestBindingException bindingException) {
// do what you want right here
//I WOULD LIKE TO ADD HERE THE SUBMITTED BOOK AND THE FETCHED BOOKLIST TO THE ModelAndView, BUT I DO NOT KNOW HOW TO DO IT
BindException bindException = (BindException) bindingException.getRootCause();
return new ModelAndView("bookForm").addAllObjects(bindException.getModel());
}
这是我在 servlet-dispatcher.xml 中将 validator 添加到 SearchBookController 的方式:
<bean name="/book/search.htm" class="com.books.web.SearchBookController" p:validators-ref="searchBookValidator" >
<property name="bookDAO" ref="myBookDAO" />
</bean>
<bean id="searchBookValidator" class="com.books.validator.SearchBookValidator" />
validator 现在只是确保书籍属性由 ValidationUtils.rejectIfEmptyOrWhitespace 验证。
我的 View (bookForm.jsp) 显示了提交字段和搜索结果(它在呈现结果 View 时重新显示提交的字段)。所以在提交之后, View 应该同时获得 book 命令对象和 bookList 对象。
bookForm.jsp 看起来像这样:
<tr>
<td>Details :</td>
<td><form:input path="details" /></td>
<td><form:errors path="details" cssClass="error"/></td>
</tr>
(details 是 Book 的字段之一)。
这是我尝试加载 bookForm.jsp 时收到的异常消息(甚至在提交之前,只是当我尝试加载页面以便我可以填写表单时):
(*** 当我从 servlet-dispatcher.xml 中的 Controller 定义中删除
p:validators-ref="searchBookValidator"
时,jsp 页面在提交前后正确加载)。
HTTP Status 500 -
--------------------------------------------------------------------------------
type Exception report
message
description The server encountered an internal error () that prevented it from fulfilling this request.
exception
org.apache.jasper.JasperException: An exception occurred processing JSP page /WEB-INF/jsp/bookForm.jsp at line 209
206: --%>
207: <tr>
208: <td>Details :</td>
209: <td><form:input path="details" /></td>
210: <td><form:errors path="details" cssClass="error"/></td>
211: </tr>
212: <tr>
Stacktrace:
org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:510)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:413)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:313)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:260)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:238)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:250)
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1047)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:817)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:549)
javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
root cause
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'book' available as request attribute
org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:141)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:174)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:194)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:160)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.autogenerateId(AbstractDataBoundFormElementTag.java:147)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.resolveId(AbstractDataBoundFormElementTag.java:138)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:122)
org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.java:408)
org.springframework.web.servlet.tags.form.InputTag.writeTagContent(InputTag.java:140)
org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java:102)
org.springframework.web.servlet.tags.RequestContextAwareTag.doStartTag(RequestContextAwareTag.java:79)
org.apache.jsp.WEB_002dINF.jsp.bookForm_jsp._jspx_meth_form_005finput_005f0(bookForm_jsp.java:593)
org.apache.jsp.WEB_002dINF.jsp.bookForm_jsp._jspService(bookForm_jsp.java:326)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:377)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:313)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:260)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:238)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:250)
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1047)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:817)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:549)
javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
更新:
按照下面的答案,我现在可以从异常处理程序方法访问命令对象。但我仍然有一个问题:如果我提交的数据应该触发错误消息,例如通过
<tr>
<td>Details :</td>
<td><form:input path="details" /></td>
<td><form:errors path="details" cssClass="error"/></td>
</tr>
在结果 View JSP 中 - 我没有看到该错误消息。
我的 validator 看起来像这样:
public class SearchBookValidator implements Validator {
//......
@Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "details", "details.required");
}
}
和我的 messages.properties 包含以下行:
details.required=details are required
为了帮助我了解发生了什么,我在 Controller 的 hanldeBindException 中包含了以下代码:
Map mp = bindException.getModel();
for (Object o : mp.entrySet()) {
Map.Entry pairs = (Map.Entry)o;
System.out.println(pairs.getKey() + " = " + pairs.getValue());
}
当我提交带有故意错误数据的表单(即详细信息文本字段为空)时,我在控制台上收到以下输入:
command = com.books.domain.Book@1173447
org.springframework.validation.BindingResult.command = org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'command' on field 'details': rejected value []; codes [details.required.command.details,details.required.details,details.required.java.lang.String,details.required]; arguments []; default message [null]
你能明白为什么我没有通过
<form:errors path="details" cssClass="error"/>
收到任何错误信息吗?
您的 Stacktrace 消息
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'book' available as request attribute
好吧,我想你有一个如下所示的 Spring 表单(注意 commandName 属性)
<form:form commandName="book">
但是,当您的页面呈现时,表单标签会查找任何名为
book 的请求属性。如果 Spring 确实找到了任何名为 book 的请求属性,您将看到这条不错的消息
Neither BindingResult nor plain target object for bean name 'book' available as request attribute
在您的列表方法中,我们可以看到您的命令对象不包含在模型中
public ModelAndView list(HttpServletRequest request, HttpServletResponse response, Book book) throws Exception {
/**
* Book object has not been added to the model
*/
ModelMap modelMap = new ModelMap();
modelMap.addAttribute("bookList", bookDAO.listBooks(book);
return new ModelAndView("bookForm", modelMap);
}
改用
public ModelAndView list(HttpServletRequest request, HttpServletResponse response, Book book) throws Exception {
return new ModelAndView("bookForm")
.addAttribute("bookList", bookDAO.listBooks(book))
.addAttribute(book);
}
几个注意事项:如果抛出的异常与作为参数的异常匹配,则只会调用您的异常处理程序
// It will be just called when some validation or binding Exception occurs
// Otherwise, Spring will bypass it
public ModelAndView hanldeBindException(HttpServletRequest request, HttpServletResponse response, ServletRequestBindingException bindingException) {
BindException bindException = (BindException) bindingException.getRootCause();
BindingResult bindingResult = (BindingResult) bindException.getModel().get(BindingResult.MODEL_KEY_PREFIX + "book");
/**
* bindingResult.getTarget() returns submitted Book object
*/
return new ModelAndView("bookForm")
.addAllObjects(bindException.getModel())
.addAttribute("bookList", bookDAO.listBooks(bindingResult.getTarget()));
}
更新
您是否已注册您的消息来源?
<!--IT MUST BE CALLED messageSource-->
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames" value="ValidationMessages"/>
</bean>
前面的设置在类路径的根目录中使用了 ValidationMessages.properties。根据您的属性文件更新
我是一名优秀的程序员,十分优秀!