Pourquoi Spring MVC répond-il avec un 404 et signale-t-il «Aucun mappage trouvé pour la requête HTTP avec l’URI dans DispatcherServlet»?

J’écris une application Spring MVC, déployée sur Tomcat. Voir l’exemple minimal, complet et vérifiable suivant :

public class Application extends AbstractAnnotationConfigDispatcherServletInitializer { protected Class[] getRootConfigClasses() { return new Class[] { }; } protected Class[] getServletConfigClasses() { return new Class[] { SpringServletConfig.class }; } protected Ssortingng[] getServletMappings() { return new Ssortingng[] { "/*" }; } } 

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 package com.example.controllers

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

Le nom de contexte de mon application est Example . Quand j’envoie une demande à

 http://localhost:8080/Example/home 

l’application répond avec un statut HTTP 404 et enregistre les éléments suivants

 WARN osweb.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 sur /WEB-INF/jsps/index.jsp Je m’attendais à ce que Spring MVC utilise mon contrôleur pour gérer la demande et le transfert vers le JSP, alors pourquoi répond-il avec un 404?


Ceci est censé être un message canonique pour les questions concernant ce message d’avertissement.

Votre application Spring MVC standard servira toutes les demandes via un DispatcherServlet que vous avez enregistré avec votre conteneur Servlet.

DispatcherServlet examine son ApplicationContext et, le cas échéant, ApplicationContext enregistré avec un ContextLoaderListener pour les beans spéciaux dont il a besoin pour configurer sa logique de ContextLoaderListener de requêtes. Ces grains sont décrits dans la documentation .

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

les requêtes entrantes aux gestionnaires et une liste des pré-processeurs et post-processeurs (intercepteurs de gestionnaires) sur la base de certains critères dont les détails varient HandlerMapping implémentation de HandlerMapping . L’implémentation la plus populaire prend en charge les contrôleurs annotés, mais d’autres implémentations existent également.

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

Le DispatcherServlet trouve tous les beans de ce type et les enregistre dans un certain ordre (peut être personnalisé). Lors de la HandlerMapping une requête, DispatcherServlet parcourt ces objects HandlerMapping et teste chacun d’eux avec getHandler pour en trouver un capable de gérer la demande entrante, représentée sous la forme de HttpServletRequest standard. A partir de 4.3.x, s’il n’en trouve pas , il enregistre l’avertissement que vous voyez

Aucun mappage trouvé pour la requête HTTP avec URI [/some/path] dans DispatcherServlet nommé SomeName

et NoHandlerFoundException une NoHandlerFoundException ou NoHandlerFoundException immédiatement la réponse avec un code d’état 404 Not Found.

Pourquoi le DispatcherServlet n’a-t-il pas trouvé un HandlerMapping capable de gérer ma demande?

L’implémentation HandlerMapping plus courante est RequestMappingHandlerMapping , qui gère l’enregistrement des beans @Controller tant que gestionnaires (en réalité leurs méthodes annotées @RequestMapping ). Vous pouvez soit déclarer un bean de ce type vous-même (avec @Bean ou ou un autre mécanisme) ou vous pouvez utiliser les options intégrées . Ceux-ci sont:

  1. @Configuration votre classe @EnableWebMvc avec @EnableWebMvc .
  2. Déclarez un dans votre configuration XML.

Comme le décrit le lien ci-dessus, les deux enregistrent un bean RequestMappingHandlerMapping (et un tas d’autres éléments). Cependant, un HandlerMapping n’est pas très utile sans gestionnaire. RequestMappingHandlerMapping attend certains beans @Controller . Vous devez donc les déclarer également via les méthodes @Bean dans une configuration Java ou dans les déclarations dans une configuration XML ou lors de l’parsing des @Controller des classes annotées @Controller . Assurez-vous que ces grains sont présents.

Si vous recevez le message d’avertissement et un message 404 et que vous avez correctement configuré tous les éléments ci-dessus, vous envoyez votre demande à l’adresse URI incorrecte , qui n’est pas gérée par une méthode de gestionnaire annotée @RequestMapping détectée.

La bibliothèque spring-webmvc offre d’autres implémentations HandlerMapping . Par exemple, BeanNameUrlHandlerMapping maps

des URL aux beans avec des noms commençant par une barre oblique (“/”)

et vous pouvez toujours écrire le vôtre. De toute évidence, vous devrez vous assurer que la requête que vous envoyez correspond au moins à l’un des HandlerMapping object HandlerMapping enregistré.

Si vous n’inscrivez pas implicitement ou explicitement de beans HandlerMapping (ou si detectAllHandlerMappings true ), DispatcherServlet enregistre certains detectAllHandlerMappings défaut . Ceux-ci sont définis dans DispatcherServlet.properties dans le même package que la classe DispatcherServlet . Ce sont BeanNameUrlHandlerMapping et DefaultAnnotationHandlerMapping (similaire à RequestMappingHandlerMapping mais obsolète).

Le 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 Ssortingng example() { return "example-view-name"; } } 

connectera le suivant au niveau INFO

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

Ceci décrit le mappage enregistré. Lorsque vous voyez l’avertissement qu’aucun gestionnaire n’a été trouvé, comparez l’URI du message au mappage répertorié ici. Toutes les ressortingctions spécifiées dans @RequestMapping doivent correspondre à celles de Spring MVC pour sélectionner le gestionnaire.

Les autres implémentations de HandlerMapping consignent leurs propres instructions, qui devraient indiquer leurs mappages et leurs gestionnaires correspondants.

De même, activez la journalisation Spring au niveau DEBUG pour voir quels beans Spring enregistrent. Il doit indiquer quelles classes annotées il trouve, quels paquets il parsing et quels beans il initialise. Si ceux que vous attendiez ne sont pas présents, consultez votre configuration ApplicationContext .

Autres erreurs communes

Un DispatcherServlet est simplement un servlet Java EE classique. Vous l’enregistrez avec votre déclaration et typique, ou directement via ServletContext#addServlet dans un WebApplicationInitializer , ou avec le mécanisme que le démarrage Spring utilise. En tant que tel, vous devez vous fier à la logique de mappage d’URL spécifiée dans la spécification du servlet , voir le chapitre 12. Voir aussi

  • Comment les mappages d’URL Servlet dans web.xml sont-ils utilisés?

Dans cette optique, une erreur courante consiste à enregistrer le DispatcherServlet avec un mappage d’URL de /* , en renvoyant un nom de vue à partir d’une méthode de gestionnaire @RequestMapping et en s’attendant à ce qu’un JSP soit rendu. Par exemple, considérons une méthode de gestionnaire comme

 @RequestMapping(path = "/example", method = RequestMethod.GET) public Ssortingng 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; } 

Vous pouvez vous attendre à ce que la requête soit transmise à une ressource JSP au chemin /WEB-INF/jsps/example-view-name.jsp . Cela n’arrivera pas. Au lieu de cela, en supposant un nom de contexte de Example , le DisaptcherServlet rapportera

Aucun mappage trouvé pour la requête HTTP avec l’URI [/Example/WEB-INF/jsps/example-view-name.jsp] dans DispatcherServlet nommé ‘dispatcher’

Étant donné que DispatcherServlet est mappé sur /* et /* correspond à tout (sauf les correspondances exactes, qui ont une priorité plus élevée), le DispatcherServlet sera choisi pour gérer le JstlView du JstlView (renvoyé par InternalResourceViewResolver ). Dans presque tous les cas, le DispatcherServlet ne sera pas configuré pour gérer une telle demande .

Au lieu de cela, dans ce cas simpliste, vous devez enregistrer le DispatcherServlet sur / , en le marquant comme servlet par défaut. Le servlet par défaut est la dernière correspondance pour une requête. Cela permettra à votre conteneur de servlet typique de choisir une implémentation Servlet interne, mappée sur *.jsp , pour gérer la ressource JSP (par exemple, Tomcat a JspServlet ), avant d’essayer avec le servlet par défaut.

C’est ce que vous voyez dans votre exemple.

J’ai résolu mon problème 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:

  org.apache.tomcat.embed tomcat-embed-jasper provided  

`depuis: fichier JSP non rendu dans l’application web Spring Boot

Je suis tombé sur une autre raison pour la même erreur. Cela pourrait également être dû aux fichiers de classe non générés pour votre fichier controller.java. En conséquence, le servlet du répartiteur mentionné dans web.xml ne peut pas le mapper à 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 Projet-> sélectionnez clean -> Build Project.Vérifiez si le fichier de classe a été généré pour le fichier de contrôleur sous les builds de votre espace de travail.