java - example - spring mvc vs spring boot
¿Por qué Spring MVC responde con un 404 e informa "No se encontró asignación para la solicitud HTTP con URI[...] en DispatcherServlet"? (6)
En mi caso, estaba siguiendo la
documentación de Interceptors Spring para la versión 5.1.2
(mientras usaba
Spring Boot v2.0.4.RELEASE
) y la clase
WebConfig
tenía la anotación
@EnableWebMvc
, que parecía estar en conflicto con algo más en mi aplicación que era evitar que mis activos estáticos se resuelvan correctamente (es decir, no se devolvieron archivos CSS o JS al cliente).
Después de probar muchas cosas diferentes, intenté
eliminar
@EnableWebMvc
y funcionó.
Editar:
Aquí está la documentación de referencia
que dice que debe eliminar la anotación
@EnableWebMvc
Aparentemente, en mi caso, al menos, ya estoy configurando mi aplicación Spring (aunque no usando
web.xml
o cualquier otro archivo estático, definitivamente es programáticamente), por lo que fue un conflicto allí.
Estoy escribiendo una aplicación Spring MVC implementada en Tomcat. Vea el siguiente ejemplo mínimo, completo y verificable
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[] { "/*" };
}
}
Donde está
SpringServletConfig
@Configuration
@ComponentScan("com.example.controllers")
@EnableWebMvc
public class SpringServletConfig {
@Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
}
Finalmente, tengo un
@Controller
en el paquete
com.example.controllers
@Controller
public class ExampleController {
@RequestMapping(path = "/home", method = RequestMethod.GET)
public String example() {
return "index";
}
}
El nombre de contexto de mi aplicación es
Example
.
Cuando envío una solicitud a
http://localhost:8080/Example/home
la aplicación responde con un estado HTTP 404 y registra lo siguiente
WARN o.s.web.servlet.PageNotFound - No mapping found for HTTP request with URI `[/Example/WEB-INF/jsps/index.jsp]` in `DispatcherServlet` with name ''dispatcher''
Tengo un recurso JSP en
/WEB-INF/jsps/index.jsp
Esperaba que Spring MVC usara mi controlador para manejar la solicitud y reenviarla a JSP, entonces, ¿por qué responde con un 404?
Esto está destinado a ser una publicación canónica para preguntas sobre este mensaje de advertencia.
Encontré otra razón para el mismo error. Esto también podría deberse a los archivos de clase no generados para su archivo controller.java. Como resultado, el servlet del despachador mencionado en web.xml no puede asignarlo al método apropiado en la clase de controlador.
@Controller
Class Controller{
@RequestMapping(value="/abc.html")//abc is the requesting page
public void method()
{.....}
}
En eclipse en Proyecto-> seleccione limpiar -> Proyecto de compilación. Compruebe si el archivo de clase se ha generado para el archivo del controlador en compilaciones en su espacio de trabajo.
Limpia tu servidor. Tal vez elimine el servidor y agregue el proyecto una vez más y Ejecute.
-
Detenga el servidor Tomcat
-
Haga clic derecho en el servidor y seleccione "Limpiar"
-
Haga clic derecho en el servidor nuevamente y seleccione "Limpiar directorio de trabajo de Tomcat"
Para mí, descubrí que mis clases de destino se generaron en un patrón de carpeta no igual al de origen. Esto es posiblemente en eclipse. Agrego carpetas para contener mis controladores y no los agrego como paquetes. Así que terminé definiendo una ruta incorrecta en la configuración de primavera.
Mi clase objetivo estaba generando clases en la aplicación y me refería a com.happy.app
<context:annotation-config />
<context:component-scan
base-package="com.happy.app"></context:component-scan>
Agregué paquetes (no carpetas) para com.happy.app y moví los archivos de carpetas a paquetes en eclipse y resolvió el problema.
Resolví mi problema cuando, además de lo descrito anteriormente: `
@Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
added tomcat-embed-jasper:
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
`from: el archivo JSP no se procesa en la aplicación web Spring Boot
Su aplicación Spring MVC estándar atenderá todas las solicitudes a través de un
DispatcherServlet
que haya registrado con su contenedor de Servlet.
DispatcherServlet
analiza su
ApplicationContext
y, si está disponible, el
ApplicationContext
registrado con un
ContextLoaderListener
para beans especiales que necesita para configurar su lógica de servicio de solicitudes.
Estos beans se describen en la documentación
.
Posiblemente el más importante, frijoles de tipo
HandlerMapping
map
solicitudes entrantes a manejadores y una lista de preprocesadores y postprocesadores (interceptores de manejador) basados en algunos criterios cuyos detalles varían según la implementación de
HandlerMapping
. La implementación más popular admite controladores anotados, pero también existen otras implementaciones.
El
javadoc de
HandlerMapping
describe además cómo deben comportarse las implementaciones.
DispatcherServlet
encuentra todos los beans de este tipo y los registra en algún orden (se puede personalizar).
Mientras se atiende una solicitud,
DispatcherServlet
recorre estos objetos
HandlerMapping
y prueba cada uno de ellos con
getHandler
para encontrar uno que pueda manejar la solicitud entrante, representada como la
HttpServletRequest
estándar.
A partir de 4.3.x,
si no encuentra ninguno
,
registra la advertencia
que ve
No se encontró ninguna asignación para la solicitud HTTP con URI
[/some/path]
enDispatcherServlet
con el nombre SomeName
e
NoHandlerFoundException
una
NoHandlerFoundException
o confirma inmediatamente la respuesta con un código de estado 404 No encontrado.
¿Por qué el
DispatcherServlet
encontró un
HandlerMapping
que pudiera manejar mi solicitud?
La implementación más común de
HandlerMapping
es
RequestMappingHandlerMapping
, que maneja el registro de beans
@Controller
como manejadores (realmente sus métodos anotados
@RequestMapping
).
Puede declarar un bean de este tipo usted mismo (con
@Bean
o
<bean>
u otro mecanismo) o puede usar
las opciones integradas
.
Estos son:
-
@Configuration
su clase de@EnableWebMvc
con@EnableWebMvc
. -
Declare un miembro
<mvc:annotation-driven />
en su configuración XML.
Como se describe en el enlace anterior, ambos registrarán un bean
RequestMappingHandlerMapping
(y un montón de otras cosas).
Sin embargo, un
HandlerMapping
no es muy útil sin un controlador.
RequestMappingHandlerMapping
espera algunos beans
@Controller
por lo que debe declararlos también, a través de los métodos
@Bean
en una configuración Java o declaraciones
<bean>
en una configuración XML o mediante el escaneo de componentes de las clases anotadas
@Controller
.
Asegúrese de que estos frijoles estén presentes.
Si recibe el mensaje de advertencia y un 404 y ha configurado todo lo anterior correctamente,
entonces está enviando su solicitud al URI incorrecto
, uno que no es manejado por un método de controlador anotado
@RequestMapping
detectado.
La biblioteca
spring-webmvc
ofrece otras implementaciones de
HandlerMapping
.
Por ejemplo,
BeanNameUrlHandlerMapping
maps
desde URL a beans con nombres que comienzan con una barra diagonal ("/")
y siempre puedes escribir el tuyo.
Obviamente,
tendrá que asegurarse de que la solicitud que está enviando coincide con al menos uno de los
HandlerMapping
objeto
HandlerMapping
registrado.
Si no registra implícita o explícitamente ningún
HandlerMapping
(o si
detectAllHandlerMappings
es
true
),
DispatcherServlet
registra algunos
defaults
.
Estos se definen en
DispatcherServlet.properties
en el mismo paquete que la clase
DispatcherServlet
.
Son
BeanNameUrlHandlerMapping
y
DefaultAnnotationHandlerMapping
(que es similar a
RequestMappingHandlerMapping
pero en desuso).
Depuración
Spring MVC registrará los controladores registrados a través de
RequestMappingHandlerMapping
.
Por ejemplo, un
@Controller
como
@Controller
public class ExampleController {
@RequestMapping(path = "/example", method = RequestMethod.GET, headers = "X-Custom")
public String example() {
return "example-view-name";
}
}
registrará lo siguiente en el nivel INFO
Mapped "{[/example],methods=[GET],headers=[X-Custom]}" onto public java.lang.String com.spring.servlet.ExampleController.example()
Esto describe el mapeo registrado.
Cuando vea la advertencia de que no se encontró ningún controlador, compare el URI en el mensaje con la asignación que se muestra aquí.
Todas las restricciones especificadas en
@RequestMapping
deben coincidir para que Spring MVC seleccione el controlador.
Otras implementaciones de
HandlerMapping
registran sus propias declaraciones que deberían insinuar sus asignaciones y sus controladores correspondientes.
Del mismo modo, habilite el registro de Spring en el nivel DEBUG para ver qué beans registra Spring.
Debe informar qué clases anotadas encuentra, qué paquetes analiza y qué beans inicializa.
Si los que esperaba no están presentes, revise la configuración de
ApplicationContext
.
Otros errores comunes
Un
DispatcherServlet
es solo un
Servlet
Java EE típico.
Lo registra con su típica declaración
<web.xml>
<servlet-class>
y
<servlet-mapping>
, o directamente a través de
ServletContext#addServlet
en un
WebApplicationInitializer
, o con cualquier mecanismo que utilice Spring boot.
Como tal, debe confiar en la lógica de
mapeo de url
especificada en la
especificación de Servlet
, consulte el Capítulo 12. Consulte también
Con eso en mente, un error común es registrar el
DispatcherServlet
con una asignación de URL de
/*
, devolviendo un nombre de vista desde un método de controlador
@RequestMapping
y esperando que se represente un JSP.
Por ejemplo, considere un método de controlador como
@RequestMapping(path = "/example", method = RequestMethod.GET)
public String example() {
return "example-view-name";
}
con un
InternalResourceViewResolver
@Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
puede esperar que la solicitud se
forwarded
a un recurso JSP en la ruta
/WEB-INF/jsps/example-view-name.jsp
.
Esto no va a suceder.
En cambio, suponiendo un nombre de contexto de
Example
,
DisaptcherServlet
informará
No se encontró ninguna asignación para la solicitud HTTP con URI
[/Example/WEB-INF/jsps/example-view-name.jsp]
enDispatcherServlet
con el nombre ''dispatcher''
Debido a que
DispatcherServlet
se asigna a
/*
y
/*
coincide con todo (excepto las coincidencias exactas, que tienen mayor prioridad),
DispatcherServlet
se elegiría para manejar el
forward
desde
JstlView
(devuelto por
InternalResourceViewResolver
).
En casi todos los casos,
DispatcherServlet
no se configurará para manejar dicha solicitud
.
En su lugar, en este caso simplista, debe registrar el
DispatcherServlet
en
/
, marcándolo como el servlet predeterminado.
El servlet predeterminado es la última coincidencia para una solicitud.
Esto permitirá que su contenedor de servlet típico elija una implementación de Servlet interna, asignada a
*.jsp
, para manejar el recurso JSP (por ejemplo, Tomcat tiene
JspServlet
), antes de intentar con el servlet predeterminado.
Eso es lo que estás viendo en tu ejemplo.