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

90

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 String[] getServletMappings() {
        return new String[] { "/*" };
    }
}

SpringServletConfigest

@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 @Controllerdans le packagecom.example.controllers

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

Le nom de contexte de mon application est Example. Lorsque j'envoie une demande à

http://localhost:8080/Example/home

l'application répond avec un état HTTP 404 et enregistre les éléments suivants

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 chez /WEB-INF/jsps/index.jspJe m'attendais à ce que Spring MVC utilise mon contrôleur pour gérer la demande et la transmettre à la JSP, alors pourquoi répond-elle avec un 404?


Ceci est destiné à être un message canonique pour les questions sur ce message d'avertissement.

Sotirios Delimanolis
la source

Réponses:

99

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

Le DispatcherServletregarde son ApplicationContextet, si disponible, le ApplicationContextenregistré avec un ContextLoaderListenerpour les beans spéciaux dont il a besoin pour configurer sa logique de traitement de demande. Ces beans sont décrits dans la documentation .

Sans doute le type de HandlerMappingcarte le plus important

les demandes entrantes aux gestionnaires et une liste de pré et post-processeurs (intercepteurs de gestionnaires) basée sur certains critères dont les détails varient selon l' HandlerMappingimplémentation. L'implémentation la plus populaire prend en charge les contrôleurs annotés, mais d'autres implémentations existent également.

Le javadoc deHandlerMapping décrit plus en détail comment les implémentations doivent se comporter.

Le DispatcherServletrecherche tous les beans de ce type et les enregistre dans un certain ordre (peut être personnalisé). Tout en traitant une requête, le DispatcherServletparcourt ces HandlerMappingobjets et teste chacun d'eux avec getHandlerpour trouver celui qui peut gérer la requête entrante, représentée comme le standard HttpServletRequest. À partir de 4.3.x, s'il n'en trouve aucun , il enregistre l'avertissement que vous voyez

Aucune correspondance trouvée pour la requête HTTP avec l' URI [/some/path]en DispatcherServletavec le nom UnNom

et soit lancers francs un NoHandlerFoundExceptionou engage immédiatement la réponse avec un code d'état 404 Not Found.

Pourquoi la DispatcherServletrecherche n'a-t-elle pas HandlerMappingpu traiter ma demande?

L' HandlerMappingimplémentation la plus courante est RequestMappingHandlerMapping, qui gère l'enregistrement des @Controllerbeans en tant que gestionnaires (en fait leurs @RequestMappingméthodes annotées). Vous pouvez soit déclarer vous-même un bean de ce type (avec @Beanou <bean>ou un autre mécanisme), soit utiliser les options intégrées . Ceux-ci sont:

  1. Annotez votre @Configurationclasse avec @EnableWebMvc.
  2. Déclarez un <mvc:annotation-driven />membre dans votre configuration XML.

Comme le lien ci-dessus le décrit, les deux enregistreront un RequestMappingHandlerMappingbean (et un tas d'autres choses). Cependant, a HandlerMappingn'est pas très utile sans gestionnaire. RequestMappingHandlerMappingattend des @Controllerbeans, vous devez donc les déclarer également, via des @Beanméthodes dans une configuration Java ou des <bean>déclarations dans une configuration XML ou via l'analyse des composants des @Controllerclasses annotées dans l'un ou l'autre. Assurez-vous que ces haricots sont présents.

Si vous recevez le message d'avertissement et un 404 et que vous avez configuré tout ce qui précède correctement, vous envoyez votre demande au mauvais URI , qui n'est pas géré par une @RequestMappingméthode de gestionnaire annotée détectée .

La spring-webmvcbibliothèque propose d'autres HandlerMappingimplémentations intégrées . Par exemple, des BeanNameUrlHandlerMappingcartes

des URL aux beans dont les noms commencent 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 un des HandlerMappinggestionnaires de l' objet enregistré .

Si vous n'enregistrez pas implicitement ou explicitement de HandlerMappingbeans (ou si detectAllHandlerMappingsc'est le cas true), les valeurs par défaut sontDispatcherServlet enregistrées . Ceux-ci sont définis dans le même package que la classe. Ils sont et (qui est similaire mais obsolète).DispatcherServlet.propertiesDispatcherServletBeanNameUrlHandlerMappingDefaultAnnotationHandlerMappingRequestMappingHandlerMapping

Débogage

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

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

enregistrera ce qui suit au niveau INFO

Mapped "{[/example],methods=[GET],headers=[X-Custom]}" onto public java.lang.String 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 dans le message au mappage répertorié ici. Toutes les restrictions spécifiées dans @RequestMappingdoivent correspondre pour que Spring MVC sélectionne le gestionnaire.

D'autres HandlerMappingimplémentations consignent leurs propres instructions qui devraient faire allusion à leurs mappages et à leurs gestionnaires correspondants.

De même, activez la journalisation Spring au niveau DEBUG pour voir quels beans Spring enregistre. Il doit indiquer les classes annotées qu'il trouve, les packages qu'il analyse et les beans qu'il initialise. Si ceux que vous attendiez ne sont pas présents, vérifiez votre ApplicationContextconfiguration.

Autres erreurs courantes

A DispatcherServletest juste un Java EE typique Servlet. Vous l'enregistrez avec votre déclaration typique <web.xml> <servlet-class>et <servlet-mapping>, ou directement via ServletContext#addServletun WebApplicationInitializer, ou avec n'importe quel mécanisme utilisé par Spring boot. 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

Dans cet esprit, une erreur courante consiste à enregistrer le DispatcherServletavec un mappage d'URL de /*, à renvoyer un nom de vue à partir d'une @RequestMappingméthode de gestionnaire et à s'attendre à ce qu'une JSP soit rendue. Par exemple, considérons une méthode de gestionnaire 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;
}

vous pouvez vous attendre à ce que la demande soit transmise à une ressource JSP sur le 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 DisaptcherServletrapportera

Aucun mappage trouvé pour la requête HTTP avec l' URI [/Example/WEB-INF/jsps/example-view-name.jsp]en DispatcherServletavec le nom « dispatcher »

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

Au lieu de cela, dans ce cas simpliste, vous devez enregistrer le DispatcherServletto /, 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 de 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.

Sotirios Delimanolis
la source
Avec @EnableWebMvc le dispatcherServlet il a déjà enregistré dans /. "vous pouvez vous attendre à ce que la demande soit transmise à une ressource JSP au chemin /WEB-INF/jsps/example-view-name.jsp. Cela ne se produira pas." Comment le faire fonctionner pour qu'il soit redirigé vers une ressource JSP sur ce chemin? C'est essentiellement la question posée.
Tor
@Tor Seul, @EnableWebMvcsur une @Configurationclasse annotée ne fait pas cela. Tout ce qu'il fait, c'est ajouter un certain nombre de gestionnaires / adaptateurs Spring MVC par défaut au contexte de l'application. L'enregistrement d'un DispatcherServletservice à servir /est un processus complètement distinct qui se fait de plusieurs manières que je décris dans la section Autres erreurs courantes . Je réponds à la question posée deux paragraphes ci-dessous ce que vous avez cité.
Sotirios Delimanolis
5

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:

<dependency>
       <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
       <scope>provided</scope>
</dependency>

`from: le fichier JSP ne s'affiche pas dans l'application Web Spring Boot

RoutesMaps.com
la source
2

Dans mon cas, je suivais la documentation Interceptors Spring pour la version 5.1.2 (tout en utilisant Spring Boot v2.0.4.RELEASE ) et la WebConfigclasse avait l'annotation @EnableWebMvc, qui semblait être en conflit avec quelque chose d'autre dans mon application qui empêchait mon statique avoir été résolus correctement (c'est-à-dire qu'aucun fichier CSS ou JS n'a été renvoyé au client).

Après avoir essayé beaucoup de choses différentes, j'ai essayé de supprimer le @EnableWebMvcet cela a fonctionné!

Modifier: voici la documentation de référence qui dit que vous devez supprimer l' @EnableWebMvcannotation

Apparemment, dans mon cas du moins, je configure déjà mon application Spring (bien que ce ne soit pas en utilisant web.xmlou en utilisant un autre fichier statique, c'est définitivement par programme), donc c'était un conflit là-bas.

Acapulco
la source
1

Essayez de modifier votre code avec la modification suivante sur votre fichier de configuration. La configuration Java est utilisée à la place de application.properties. N'oubliez pas d'activer la configuration dans configureDefaultServletHandlingmethod.

WebMvcConfigurerAdapterclass est obsolète, nous utilisons donc 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();
    }
}

J'utilise gradle, vous devriez avoir les dépendances suivantes dans 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'
}
marque
la source
0

Je suis tombé sur une autre raison pour la même erreur. Cela peut é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 de contrôleur.

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

Dans eclipse sous Project-> sélectionnez clean -> Build Project. Vérifiez si le fichier de classe a été généré pour le fichier contrôleur sous builds dans votre espace de travail.

Anil NP
la source
0

Pour moi, j'ai trouvé que mes classes cibles étaient générées dans un modèle de dossier différent de la source. C'est peut-être dans eclipse que j'ajoute des dossiers pour contenir mes contrôleurs et ne les ajoute pas en tant que packages. J'ai donc fini par définir un chemin incorrect dans la configuration du printemps.

Ma classe cible générait 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 packages (pas des dossiers) pour com.happy.app et déplacé les fichiers des dossiers vers les packages dans eclipse et cela a résolu le problème.

Roy
la source
0

Nettoyez votre serveur. Supprimez peut-être le serveur et ajoutez à nouveau le projet et exécutez.

  1. Arrêtez le serveur Tomcat

  2. Faites un clic droit sur le serveur et sélectionnez "Nettoyer"

  3. Cliquez à nouveau avec le bouton droit sur le serveur et sélectionnez "Nettoyer le répertoire de travail Tomcat"

2rahulsk
la source
0

Dans mon cas, je jouais avec l'importation de fichiers de configuration java secondaires dans un fichier de configuration java principal. Lors de la création de fichiers de configuration secondaires, j'avais changé le nom de la classe de configuration principale, mais je n'avais pas réussi à mettre à jour le nom dans web.xml. Ainsi, chaque fois que j'avais redémarré mon serveur tomcat, je ne voyais pas les gestionnaires de mappage notés dans la console Eclipse IDE, et lorsque j'ai essayé de naviguer vers ma page d'accueil, je voyais cette erreur:

1er novembre 2019 23:00:01 org.springframework.web.servlet.PageNotFound noHandlerFound AVERTISSEMENT: aucun mappage trouvé pour la requête HTTP avec l'URI [/ webapp / home / index] dans DispatcherServlet avec le nom 'dispatcher'

Le correctif consistait à mettre à jour le fichier web.xml afin que l'ancien nom «WebConfig» soit à la place «MainConfig», en le renommant simplement pour refléter le dernier nom du fichier de configuration Java principal (où «MainConfig» est arbitraire et les mots « Web "et" Main "utilisés ici ne sont pas une condition de syntaxe) MainConfig était important, car c'était le fichier qui effectuait l'analyse des composants pour "WebController", ma classe de contrôleur mvc de printemps qui gère mes requêtes Web.

@ComponentScan(basePackageClasses={WebController.class})

web.xml avait ceci:

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

Le fichier web.xml a maintenant:

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

Maintenant, je vois le mappage dans la fenêtre de la console:

INFO: mappé "{[/ home / index], methods = [GET]}" sur org.springframework.web.servlet.ModelAndView com.lionheart.fourthed.controller.WebController.gotoIndex ()

Et ma page Web se charge à nouveau.

Steve T
la source
-1

J'ai eu le même problème que **No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName**

Après avoir analysé pendant 2 à 4 jours, j'ai découvert la cause profonde. Les fichiers de classe n'ont pas été générés après l'exécution du projet. J'ai cliqué sur l'onglet projet.

Projet -> CloseProject -> OpenProject -> Clean -> Build project

Des fichiers de classe pour le code source ont été générés. Cela a résolu mon problème. Pour vérifier si les fichiers de classe ont été générés ou non, veuillez vérifier le dossier Build dans votre dossier de projet.

Thanis Albert
la source