¿Por qué Spring MVC responde con un 404 e informa "No se encontró ninguna asignación para la solicitud HTTP con URI […] en DispatcherServlet"?

91

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 SpringServletConfigesta

@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 @Controllerpaquetecom.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 y /WEB-INF/jsps/index.jspesperaba que Spring MVC usara mi controlador para manejar la solicitud y reenviar a JSP, entonces, ¿por qué responde con un 404?


Esta es una publicación canónica para preguntas sobre este mensaje de advertencia.

Sotirios Delimanolis
fuente

Respuestas:

100

Su aplicación Spring MVC estándar atenderá todas las solicitudes a través de una DispatcherServletque haya registrado con su contenedor Servlet.

El DispatcherServletmira su ApplicationContexty, si está disponible, el ApplicationContextregistrado con un ContextLoaderListenerpara beans especiales que necesita para configurar su lógica de servicio de solicitudes. Estos beans se describen en la documentación .

Posiblemente el HandlerMappingmapa de beans de tipo más importante

solicitudes entrantes a los manejadores y una lista de pre y posprocesadores (interceptores de manejadores) basada en algunos criterios cuyos detalles varían según la HandlerMappingimplementación. La implementación más popular admite controladores anotados, pero también existen otras implementaciones.

El javadoc deHandlerMapping describe además cómo deben comportarse las implementaciones.

El DispatcherServletbusca todos los beans de este tipo y los registra en algún orden (se puede personalizar). Mientras atiende una solicitud, el DispatcherServletbucle recorre estos HandlerMappingobjetos y prueba cada uno de ellos getHandlerpara encontrar uno que pueda manejar la solicitud entrante, representada como estándar HttpServletRequest. A partir de 4.3.x, si no encuentra ninguno , registra la advertencia que ve

No se encontró el mapeo para la petición HTTP con URI [/some/path]en DispatcherServletcon el nombre somename

y ya sea tiros una NoHandlerFoundExceptiono inmediatamente compromete la respuesta con un código de estado 404 no encontrado.

¿Por qué no DispatcherServletencontré un HandlerMappingque pudiera manejar mi solicitud?

La HandlerMappingimplementación más común es RequestMappingHandlerMapping, que maneja el registro de @Controllerbeans como manejadores (realmente sus @RequestMappingmétodos anotados). Se puede declarar ya sea un grano de este tipo a sí mismo (con @Beano <bean>u otro mecanismo) o puede utilizar el incorporado en las opciones . Estos son:

  1. Anote su @Configurationclase con @EnableWebMvc.
  2. Declare un <mvc:annotation-driven />miembro en su configuración XML.

Como se describe en el enlace anterior, ambos registrarán un RequestMappingHandlerMappingbean (y muchas otras cosas). Sin embargo, a HandlerMappingno es muy útil sin un controlador. RequestMappingHandlerMappingespera algunos @Controllerbeans, por lo que debe declararlos también, a través de @Beanmétodos en una configuración Java o <bean>declaraciones en una configuración XML o mediante el escaneo de componentes de @Controllerclases anotadas en cualquiera de los dos. 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 @RequestMappingmétodo de controlador anotado detectado .

La spring-webmvcbiblioteca ofrece otras HandlerMappingimplementaciones integradas . Por ejemplo, BeanNameUrlHandlerMappingmapas

de URL a beans con nombres que comienzan con una barra ("/")

y siempre puedes escribir el tuyo. Obviamente, tendrá que asegurarse de que la solicitud que envía coincida con al menos uno de los HandlerMappingcontroladores del objeto registrado .

Si no registra implícita o explícitamente ningún HandlerMappingbean (o si lo detectAllHandlerMappingses true), el DispatcherServletregistra algunos valores predeterminados . Estos se definen en DispatcherServlet.propertiesel mismo paquete que la DispatcherServletclase. Son BeanNameUrlHandlerMappingy DefaultAnnotationHandlerMapping(que es similar RequestMappingHandlerMappingpero obsoleto).

Depuración

Spring MVC registrará los controladores registrados a través de RequestMappingHandlerMapping. Por ejemplo, un me @Controllergusta

@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 @RequestMappingdeben coincidir para que Spring MVC seleccione el controlador.

Otras HandlerMappingimplementaciones registran sus propias declaraciones que deberían indicar sus asignaciones y sus correspondientes controladores.

De manera similar, habilite el registro de Spring en el nivel DEBUG para ver qué beans registra Spring. Debería informar qué clases anotadas encuentra, qué paquetes escanea y qué beans inicializa. Si los que esperaba no están presentes, revise su ApplicationContextconfiguración.

Otros errores comunes

A DispatcherServletes solo un Java EE típico Servlet. Lo registra con su declaración típica <web.xml> <servlet-class>y <servlet-mapping>, o directamente a través ServletContext#addServletde a 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

Teniendo esto en cuenta, un error común es registrar el DispatcherServletcon una asignación de URL de /*, devolver un nombre de vista de un @RequestMappingmétodo de controlador y esperar que se procese 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 reenvíe a un recurso JSP en la ruta /WEB-INF/jsps/example-view-name.jsp. Esto no sucederá. En cambio, asumiendo un nombre de contexto de Example, DisaptcherServletinformará

No se encontró el mapeo para la petición HTTP con URI [/Example/WEB-INF/jsps/example-view-name.jsp]en DispatcherServletcon el nombre 'despachador'

Debido a que DispatcherServletse asigna /*y /*coincide con todo (excepto las coincidencias exactas, que tienen una prioridad más alta), DispatcherServletse elegiría para manejar el forwardde JstlView(devuelto por InternalResourceViewResolver). En casi todos los casos, DispatcherServletno se configurará para manejar dicha solicitud .

En cambio, en este caso simplista, debe registrar el DispatcherServletto /, marcándolo como el servlet predeterminado. El servlet predeterminado es la última coincidencia de 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á viendo en su ejemplo.

Sotirios Delimanolis
fuente
Con @EnableWebMvc, el dispatcherServlet ya se registró en /. "podría esperar que la solicitud se reenvíe a un recurso JSP en la ruta /WEB-INF/jsps/example-view-name.jsp. Esto no sucederá". ¿Cómo lo hace funcionar para que se reenvíe a un recurso JSP en esa ruta? Esa es básicamente la pregunta que se hace.
Tor
@Tor Por sí solo, @EnableWebMvcen una @Configurationclase anotada no hace eso. Todo lo que hace es agregar una serie de beans de adaptadores / manejadores Spring MVC predeterminados al contexto de la aplicación. Registrar un DispatcherServletpara servir /es un proceso completamente independiente que se realiza de varias maneras que describo en la sección Otros errores comunes . Respondo a la pregunta formulada dos párrafos más abajo de lo que citó.
Sotirios Delimanolis
5

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 representa en la aplicación web Spring Boot

RoutesMaps.com
fuente
2

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 WebConfigclase tenía la anotación @EnableWebMvc, que parecía estar en conflicto con algo más en mi aplicación que impedía mi estática los activos se resuelvan correctamente (es decir, no se devolvieron archivos CSS o JS al cliente).

Después de probar muchas cosas diferentes, intenté eliminar el @EnableWebMvcy funcionó.

Editar: aquí está la documentación de referencia que dice que debe eliminar la @EnableWebMvcanotación

Aparentemente, al menos en mi caso, ya estoy configurando mi aplicación Spring (aunque no usando web.xmlningún otro archivo estático, definitivamente es programáticamente), así que hubo un conflicto allí.

Acapulco
fuente
1

Intente modificar su código con el siguiente cambio en su archivo de configuración. Se usa la configuración de Java en lugar de application.properties. No olvide habilitar la configuración en el configureDefaultServletHandlingmétodo.

WebMvcConfigurerAdapterLa clase está en desuso, por lo que usamos WebMvcConfigurerinterface.

@Configuration
@EnableWebMvc
@ComponentScan
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.jsp("/WEB-INF/views/", ".jsp");
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

Yo uso gradle, debes tener las siguientes dependencias en pom.xml:

dependencies {

    compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.3.0.RELEASE'
    compile group: 'org.apache.tomcat.embed', name: 'tomcat-embed-jasper', version: '9.0.35'
}
marca
fuente
0

Me encontré con otra razón para el mismo error. Esto también podría deberse a que los archivos de clase no se generaron 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 del controlador.

@Controller
Class Controller{
@RequestMapping(value="/abc.html")//abc is the requesting page
public void method()
{.....}
}

En eclipse, en Proyecto-> seleccione limpiar -> Crear proyecto. Compruebe si el archivo de clase se ha generado para el archivo del controlador en las compilaciones en su espacio de trabajo.

Anil NP
fuente
0

Para mí, descubrí que mis clases de destino se generaron en un patrón de carpeta que no es el mismo que el de origen. Esto posiblemente sea en eclipse. Agrego carpetas para contener mis controladores y no los agrego como paquetes. Así que terminé definiendo la 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 las carpetas a los paquetes en eclipse y resolvió el problema.

Roy
fuente
0

Limpia tu servidor. Tal vez elimine el servidor y agregue el proyecto una vez más y Ejecute.

  1. Detenga el servidor Tomcat

  2. Haga clic derecho en el servidor y seleccione "Limpiar"

  3. Haga clic derecho en el servidor nuevamente y seleccione "Limpiar directorio de trabajo de Tomcat"

2rahulsk
fuente
0

En mi caso, estaba jugando con la importación de archivos de configuración de Java secundarios en un archivo de configuración de Java principal. Mientras creaba archivos de configuración secundarios, había cambiado el nombre de la clase de configuración principal, pero no había podido actualizar el nombre en web.xml. Entonces, cada vez que reiniciaba mi servidor tomcat, no veía los controladores de mapeo anotados en la consola del IDE de Eclipse, y cuando traté de navegar a mi página de inicio, veía este error:

1 de noviembre de 2019 11:00:01 PM org.springframework.web.servlet.PageNotFound noHandlerFound ADVERTENCIA: No se encontró ninguna asignación para la solicitud HTTP con URI [/ webapp / home / index] en DispatcherServlet con el nombre 'dispatcher'

La solución fue actualizar el archivo web.xml para que el nombre anterior "WebConfig" fuera "MainConfig", simplemente renombrándolo para reflejar el último nombre del archivo de configuración de Java principal (donde "MainConfig" es arbitrario y las palabras " Web "y" Main "utilizados aquí no son un requisito de sintaxis). MainConfig era importante, porque era el archivo que escaneaba el componente en busca de "WebController", mi clase de controlador Spring MVC que maneja mis solicitudes web.

@ComponentScan(basePackageClasses={WebController.class})

web.xml tenía esto:

<init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        com.lionheart.fourthed.config.WebConfig
    </param-value>
</init-param>

El archivo web.xml ahora tiene:

<init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        com.lionheart.fourthed.config.MainConfig
    </param-value>
</init-param>

Ahora veo el mapeo en la ventana de la consola:

INFORMACIÓN: Asignado "{[/ home / index], métodos = [GET]}" a una org.springframework.web.servlet.ModelAndView pública com.lionheart.fourthed.controller.WebController.gotoIndex ()

Y mi página web se está cargando de nuevo.

Steve T
fuente
-1

Tuve el mismo problema que **No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName**

Después de analizar durante 2 a 4 días, descubrí la causa raíz. Los archivos de clase no se generaron después de ejecutar el proyecto. Hice clic en la pestaña del proyecto.

Proyecto -> CloseProject -> OpenProject -> Limpiar -> Construir proyecto

Se han generado archivos de clase para el código fuente. Resolvió mi problema. Para verificar si los archivos de clase se han generado o no, verifique la carpeta de compilación en la carpeta de su proyecto.

Thanis Albert
fuente