102 votes

Pourquoi Spring MVC répond-il par un 404 et signale-t-il "No mapping found for HTTP request with URI [...] in DispatcherServlet" ?

J'écris une application Spring MVC déployée sur Tomcat. Voici ce que je vois exemple minimal, complet et vérifiable

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

SpringServletConfig est

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

Enfin, j'ai un @Controller dans le paquet com.example.controllers

@Controller
public class ExampleController {
    @RequestMapping(path = "/home", method = RequestMethod.GET)
    public String example() {
        return "index";
    }
}

Le nom du contexte de mon application est Example . Lorsque j'envoie une requête à

http://localhost:8080/Example/home

l'application répond par un statut HTTP 404 et enregistre les données suivantes

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'

J'ai une ressource JSP à /WEB-INF/jsps/index.jsp Je m'attendais à ce que Spring MVC utilise mon contrôleur pour traiter la requête et la transmettre à la JSP, alors pourquoi répond-il avec un 404 ?


Il s'agit d'un message canonique pour les questions relatives à ce message d'avertissement.

106voto

Sotirios Delimanolis Points 77933

Votre application Spring MVC standard servira toutes les requêtes par l'intermédiaire d'un fichier de type DispatcherServlet que vous avez enregistré auprès de votre conteneur de servlets.

Le site DispatcherServlet examine son ApplicationContext et, si elle est disponible, la ApplicationContext enregistré auprès d'un ContextLoaderListener pour les beans spéciaux dont il a besoin pour configurer sa logique de service de requête. Ces beans sont décrits dans la documentation .

Sans doute le plus important, les haricots de type HandlerMapping carte

les requêtes entrantes vers les gestionnaires et une liste de pré et post-processeurs (intercepteurs de gestionnaires) en fonction de certains critères dont les détails varient selon HandlerMapping mise en œuvre. L'implémentation la plus populaire supporte les contrôleurs annotés mais d'autres implémentations existent aussi également.

Le site javadoc de HandlerMapping décrit en outre comment les implémentations doivent se comporter.

Le site DispatcherServlet trouve tous les beans de ce type et les enregistre dans un certain ordre (peut être personnalisé). Pendant qu'elle sert une requête, la méthode DispatcherServlet des boucles à travers ces HandlerMapping et teste chacun d'entre eux avec getHandler pour en trouver un qui puisse traiter la demande entrante, représentée par la norme HttpServletRequest . A partir de 4.3.x, s'il ne trouve pas de il enregistre l'avertissement que vous voyez

Aucune correspondance trouvée pour la requête HTTP avec URI [/some/path] sur DispatcherServlet avec le nom SomeName

et soit lance un NoHandlerFoundException ou commet immédiatement la réponse avec un code d'état 404 Not Found.

Pourquoi le DispatcherServlet trouver un HandlerMapping qui pourrait traiter ma demande ?

Les plus courants HandlerMapping La mise en œuvre est RequestMappingHandlerMapping qui s'occupe de l'enregistrement @Controller en tant que gestionnaires (en réalité, leurs @RequestMapping méthodes annotées). Vous pouvez soit déclarer vous-même un bean de ce type (avec l'option @Bean ou <bean> ou un autre mécanisme) ou vous pouvez utiliser les options intégrées . Ce sont :

  1. Annotez votre @Configuration classe avec @EnableWebMvc .
  2. Déclarer un <mvc:annotation-driven /> dans votre configuration XML.

Comme le décrit le lien ci-dessus, ces deux éléments enregistreront une RequestMappingHandlerMapping (et un tas d'autres choses). Cependant, un HandlerMapping n'est pas très utile sans un gestionnaire. RequestMappingHandlerMapping s'attend à ce que @Controller vous devez donc les déclarer également, par l'intermédiaire de @Bean dans une configuration Java ou <bean> dans une configuration XML ou par le biais d'un balayage de composant de @Controller classes annotées dans l'un ou l'autre. Assurez-vous que ces haricots sont présents.

Si vous obtenez le message d'avertissement et un 404 et que vous avez configuré tous les éléments ci-dessus correctement, alors vous envoyez votre requête à la mauvaise URI qui n'est pas gérée par un système de détection. @RequestMapping méthode du gestionnaire annoté.

Le site spring-webmvc La bibliothèque offre d'autres services intégrés HandlerMapping mises en œuvre. Par exemple, BeanNameUrlHandlerMapping cartes

des URLs aux beans dont les noms commencent par une barre oblique ("/")

et vous pouvez toujours écrire le vôtre. Évidemment, vous devez vous assurer que la requête que vous envoyez correspond à au moins un des éléments enregistrés. HandlerMapping les gestionnaires de l'objet.

Si vous n'enregistrez pas implicitement ou explicitement de HandlerMapping haricots (ou si detectAllHandlerMappings est true ), le DispatcherServlet enregistre certains Valeurs par défaut . Ceux-ci sont définis dans DispatcherServlet.properties dans le même paquet que le DispatcherServlet classe. Ils sont BeanNameUrlHandlerMapping et DefaultAnnotationHandlerMapping (qui est similaire à RequestMappingHandlerMapping mais déprécié).

Débogage

Spring MVC enregistrera les gestionnaires enregistrés via RequestMappingHandlerMapping . Par exemple, un @Controller comme

@Controller
public class ExampleController {
    @RequestMapping(path = "/example", method = RequestMethod.GET, headers = "X-Custom")
    public String example() {
        return "example-view-name";
    }
}

enregistrera le message suivant au niveau INFO

Mapped "{[/example],methods=[GET],headers=[X-Custom]}" onto public java.lang.String com.spring.servlet.ExampleController.example()

Ceci décrit la cartographie enregistrée. Lorsque vous voyez l'avertissement indiquant qu'aucun gestionnaire n'a été trouvé, comparez l'URI dans le message au mappage répertorié ici. Toutes les restrictions spécifiées dans la section @RequestMapping doit correspondre pour que Spring MVC puisse sélectionner le gestionnaire.

Autre HandlerMapping Les implémentations enregistrent leurs propres déclarations qui devraient donner des indications sur leurs mappings et leurs gestionnaires correspondants.

De même, activez la journalisation de Spring au niveau DEBUG pour voir quels beans Spring enregistre. Il devrait signaler les classes annotées qu'il trouve, les paquets qu'il scanne et les beans qu'il initialise. Si les éléments que vous attendiez ne sont pas présents, revoyez votre code source. ApplicationContext configuration.

Autres erreurs courantes

A DispatcherServlet est juste un exemple typique de Java EE Servlet . Vous l'enregistrez auprès de votre <web.xml> <servlet-class> et <servlet-mapping> ou directement par la déclaration ServletContext#addServlet dans un WebApplicationInitializer ou avec tout autre mécanisme utilisé par Spring boot. En tant que tel, vous devez vous appuyer sur le cartographie des urls logique spécifiée dans le Spécification des servlets voir le chapitre 12. Voir aussi

Dans cette optique, une erreur fréquente consiste à enregistrer l'élément DispatcherServlet avec un mapping url de /* en renvoyant le nom d'une vue à partir d'un @RequestMapping et s'attendre à ce qu'une JSP soit rendue. Par exemple, considérons une méthode de gestion comme

@RequestMapping(path = "/example", method = RequestMethod.GET)
public String example() {
    return "example-view-name";
}

avec un InternalResourceViewResolver

@Bean
public InternalResourceViewResolver resolver() {
    InternalResourceViewResolver vr = new InternalResourceViewResolver();
    vr.setPrefix("/WEB-INF/jsps/");
    vr.setSuffix(".jsp");
    return vr;
}

on peut s'attendre à ce que la demande soit transmis à vers une ressource JSP au chemin /WEB-INF/jsps/example-view-name.jsp . Cela ne se produira pas. Au lieu de cela, en supposant que le nom du contexte est Example le DisaptcherServlet fera un rapport

Aucune correspondance trouvée pour la requête HTTP avec URI [/Example/WEB-INF/jsps/example-view-name.jsp] sur DispatcherServlet avec le nom "dispatcher".

Parce que le DispatcherServlet est mis en correspondance avec /* et /* correspond à tout (sauf aux concordances exactes, qui ont une priorité plus élevée), l'option DispatcherServlet serait choisi pour gérer le forward de la JstlView (renvoyée par la InternalResourceViewResolver ). Dans presque tous les cas, le DispatcherServlet ne sera pas configuré pour traiter une telle demande .

Au lieu de cela, dans ce cas simpliste, il faut enregistrer le fichier DispatcherServlet à / en le marquant comme le servlet par défaut. Le servlet par défaut est le dernier à correspondre à une requête. Cela permettra à votre conteneur de servlets typique de choisir une implémentation de servlet interne, mappée à *.jsp pour gérer la ressource JSP (par exemple, Tomcat dispose de JspServlet ), avant d'essayer avec le servlet par défaut.

C'est ce que vous voyez dans votre exemple.

0 votes

Avec @EnableWebMvc le dispatcherServlet il est déjà enregistré à /. "vous pourriez vous attendre à ce que la demande soit transmise à une ressource JSP au chemin /WEB-INF/jsps/exemple-view-name.jsp. Cela ne se produira pas." Comment faire pour que la requête soit transmise à une ressource JSP située dans ce chemin ? C'est en gros la question posée.

1 votes

@Tor En soi, @EnableWebMvc sur un @Configuration La classe annotée ne fait pas cela. Tout ce qu'elle fait, c'est ajouter un certain nombre de handlers/adaptateurs Spring MVC par défaut au contexte de l'application. L'enregistrement d'un DispatcherServlet pour servir / est un processus complètement séparé qui se fait de plusieurs manières que je décris dans le document Autres erreurs courantes section. Je réponds la question posée deux paragraphes en dessous de ce que vous avez cité.

7voto

RoutesMaps.com Points 888

J'ai résolu mon problème lorsqu'en plus de ce qui a été décrit précédemment.

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

` de : Le fichier JSP n'est pas rendu dans l'application web Spring Boot

2voto

Acapulco Points 391

Dans mon cas, je suivais le Documentation d'Interceptors Spring pour la version 5.1.2 (tout en utilisant Spring Boot v2.0.4.RELEASE ) et le WebConfig avait l'annotation @EnableWebMvc Ce dernier semblait entrer en conflit avec un autre élément de mon application qui empêchait la résolution correcte de mes ressources statiques (c'est-à-dire qu'aucun fichier CSS ou JS n'était renvoyé au client).

Après avoir essayé beaucoup de choses différentes, j'ai essayé enlever le site @EnableWebMvc et ça a marché !

Edit : Voici la documentation de référence qui dit que vous devez retirer le @EnableWebMvc annotation

Apparemment, dans mon cas du moins, je configure déjà mon application Spring (bien que ce ne soit pas en utilisant web.xml ou tout autre fichier statique, c'est définitivement par programme), il y avait donc un conflit à ce niveau.

0voto

Anil N.P Points 35

Je suis tombé sur une autre raison pour la même erreur. Cela pourrait également être dû au fait que les fichiers de classe n'ont pas été générés pour votre fichier controller.java. En conséquence, le servlet dispatcher mentionné dans le web.xml est incapable de le faire correspondre à la méthode appropriée dans la classe du contrôleur.

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

Dans eclipse sous Project->select clean ->Build Project.Do donner une vérification si le fichier de classe a été généré pour le fichier contrôleur sous builds dans votre workspace.

0voto

Roy Points 1

Pour moi, j'ai constaté que mes classes cibles étaient générées dans un modèle de dossier différent de celui de la source. Il est possible que dans Eclipse, j'ajoute des dossiers pour contenir mes contrôleurs et que je ne les ajoute pas en tant que paquets. J'ai donc fini par définir un chemin incorrect dans la configuration de Spring.

Ma classe cible génère des classes sous app et je faisais référence à com.happy.app.

<context:annotation-config />
<context:component-scan
    base-package="com.happy.app"></context:component-scan> 

J'ai ajouté des paquets (pas des dossiers) pour com.happy.app et j'ai déplacé les fichiers des dossiers vers les paquets dans eclipse et cela a résolu le problème.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X