tutorial mvc examples example java spring jsp spring-mvc

java - examples - spring mvc jsp



¿Qué causa "java.lang.IllegalStateException: ni BindingResult ni el objeto de destino simple para el nombre de comando ''comando'' disponible como atributo de solicitud"? (6)

Esto está destinado a ser una extensa publicación de preguntas y respuestas canónicas para este tipo de preguntas.

Estoy tratando de escribir una aplicación web Spring MVC donde los usuarios pueden agregar nombres de películas a una colección en memoria. Está configurado así

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[] { "/" }; } }

y

@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; } }

Hay una sola clase @Controller en el paquete 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 contiene

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

La aplicación está configurada con la ruta de contexto /Example . Cuando envío una solicitud GET a

http://localhost:8080/Example/movies

la solicitud falla, Spring MVC responde con un código de estado 500 e informa la siguiente excepción y el seguimiento de la pila

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)

Esperaba que el JSP generara un HTML <form> con una sola entrada de texto, para un nombre de Movie , y un botón de enviar, que puedo usar para enviar una solicitud POST con una nueva Movie . ¿Por qué el servlet JSP no puede representar la etiqueta Spring <form:form> ?


Agregue el atributo de modelo (nombre del modelo que especificó en el controlador) para relacionar su formulario. Debe agregar los siguientes atributos a su formulario

<form:form modelAttribute="movies" method="post">


En mi caso, funcionó agregando modelAttribute="movie" a la etiqueta del formulario y anteponiendo el nombre del modelo al atributo, algo así como <form:input path="filmName" type="text" id="movie.name" />


Estás tratando de usar la etiqueta de formulario Spring MVC .

Esta etiqueta representa una etiqueta de form HTML y expone una ruta de enlace a las etiquetas internas para el enlace. Coloca el objeto de comando en el PageContext para que las etiquetas internas puedan acceder al objeto de comando. [..]

Supongamos que tenemos un objeto de dominio llamado User . Es un JavaBean con propiedades como firstName y lastName . Lo usaremos como el objeto de respaldo de formulario de nuestro controlador de formulario que devuelve form.jsp .

En otras palabras, Spring MVC extraerá un objeto de comando y usará su tipo como modelo para vincular expresiones de path para etiquetas internas de form , como input o checkbox , para representar un elemento de form HTML.

Este objeto de comando también se denomina atributo modelo y su nombre se especifica en los atributos modelAttribute o modelAttribute la etiqueta de modelAttribute . Lo has omitido en tu JSP

<form:form>

Podrías haber especificado un nombre explícitamente. Ambos son equivalentes.

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

El nombre de atributo predeterminado es command (lo que ve en el mensaje de error). Un atributo modelo es un objeto, generalmente un POJO o una colección de POJO, que su aplicación suministra a la pila Spring MVC y que la pila Spring MVC expone a su vista (es decir, la M a la V en MVC).

Spring MVC recopila todos los atributos del modelo en un ModelMap (todos tienen nombres) y, en el caso de los JSP, los transfiere a los atributos HttpServletRequest , donde las etiquetas JSP y las expresiones EL tienen acceso a ellos.

En su ejemplo, su método de controlador @Controller que maneja un GET a la ruta /movies agrega un atributo de modelo único

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

y luego reenviar a index.jsp . Este JSP luego intenta renderizar

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

Al procesar esto, FormTag (en realidad, InputTag ) intenta encontrar un atributo modelo llamado command (el nombre de atributo predeterminado) para que pueda producir un elemento HTML <input> con un atributo de name construido a partir de la expresión de path y la propiedad correspondiente valor, es decir. El resultado de la Movie#getFilmName() .

Como no puede encontrarlo, arroja la excepción que ves

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

El motor JSP lo atrapa y responde con un código de estado 500. Si desea aprovechar un POJO de Movie para simplemente construir su formulario correctamente, puede agregar un atributo de modelo explícitamente con

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

o haga que Spring MVC cree y agregue uno para usted (debe tener un constructor sin parámetros accesible)

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

Alternativamente, incluya un método anotado @ModelAttribute en su clase @Controller

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

Tenga en cuenta que Spring MVC llamará a este método y agregará implícitamente el objeto devuelto a sus atributos de modelo para cada solicitud manejada por el @Controller .

Puede haber adivinado a partir de esta descripción que la etiqueta de form de Spring es más adecuada para representar un <form> HTML desde un objeto existente, con valores reales. Si desea simplemente crear un <form> blanco, puede ser más apropiado construirlo usted mismo y no confiar en ningún atributo del modelo.

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

En el lado de recepción, su método de controlador POST aún podrá extraer el valor de entrada de filmName y usarlo para inicializar un objeto Movie .

Errores comunes

Como hemos visto, FormTag busca un atributo de modelo llamado command por defecto o con el nombre especificado en modelAttribute o modelAttribute . Asegúrate de estar usando el nombre correcto.

ModelMap tiene un addAttribute(Object) que agrega

El atributo proporcionado a este Map utilizando un nombre generated .

donde la convención general es

devuelve el nombre corto no capitalizado de la Class [atributo], de acuerdo con las reglas de denominación de propiedades JavaBeans: Entonces, com.myapp.Product convierte en product ; com.myapp.MyProduct convierte en myProduct ; com.myapp.UKProduct convierte en UKProduct

Si está utilizando este método (o uno similar) o si está utilizando uno de los tipos de retorno admitidos de @RequestMapping que representa un atributo de modelo, asegúrese de que el nombre generado sea el que espera.

Otro error común es omitir el método @Controller completo. Una aplicación típica de Spring MVC sigue este patrón:

  1. Enviar solicitud HTTP GET
  2. DispatcherServlet selecciona el método @RequestMapping para manejar la solicitud
  3. El método del controlador genera algunos atributos del modelo y devuelve el nombre de la vista
  4. DispatcherServlet agrega atributos de modelo a HttpServletRequest y reenvía la solicitud a JSP correspondiente al nombre de la vista
  5. JSP rinde respuesta

Si, por alguna mala configuración, omite el método @RequestMapping completo, los atributos no se habrán agregado. Esto puede suceder

  • si su URI de solicitud HTTP accede a sus recursos JSP directamente, por ejemplo. porque son accesibles, es decir. fuera de WEB-INF , o
  • Si la welcome-list de welcome-list de su web.xml contiene su recurso JSP, el contenedor de Servlet lo representará directamente, omitiendo por completo la pila Spring MVC

De una forma u otra, desea que se invoque su @Controller para que los atributos del modelo se agreguen adecuadamente.

¿ BindingResult tiene que ver BindingResult con esto?

Un BindingResult es un contenedor para la inicialización o validación de los atributos del modelo. La documentación de Spring MVC dice

Los parámetros Errors o BindingResult deben seguir el objeto modelo que se está vinculando de inmediato, ya que la firma del método puede tener más de un objeto modelo y Spring creará una instancia BindingResult separada para cada uno de ellos [...]

En otras palabras, si desea utilizar BindingResult , debe seguir el parámetro de atributo del modelo correspondiente en un método @RequestMapping

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

BindingResult objetos BindingResult también se consideran atributos del modelo. Spring MVC utiliza una convención de nomenclatura simple para administrarlos, lo que facilita encontrar un atributo de modelo regular correspondiente. Dado que BindingResult contiene más datos sobre el atributo del modelo (p. Ej., Errores de validación), el FormTag intenta FormTag primero. Sin embargo, dado que van de la mano, es poco probable que exista uno sin el otro.


La actualización de Spring versión 3 a Spring versión 5 produce el mismo error. Todas las respuestas fueron satisfechas ya en mi código. Agregar la anotación @ControllerAdvice resolvió el problema para mí.


Para simplificar las cosas con la etiqueta de formulario, simplemente agregue un "commandName" que es un nombre horrible para lo que realmente está buscando ... quiere el objeto que nombró en la anotación MdelAttribute. Entonces en este caso commandName = "movie".

Eso te ahorrará leer largas explicaciones sin aliento amigo.


Tuve este error en una pantalla con múltiples formas que hacen una búsqueda. Cada formulario se publica en su propio método de controlador con resultados que se muestran en la misma pantalla.

Problema: omití agregar las otras dos formas como atributos de modelo en cada método de controlador, lo que causó ese error cuando la pantalla se muestra con resultados.

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