Warum antwortet Spring MVC mit einem 404 und meldet "Keine Zuordnung für HTTP-Anforderung mit URI […] in DispatcherServlet gefunden"?

90

Ich schreibe eine Spring MVC-Anwendung, die auf Tomcat bereitgestellt wird. Siehe das folgende minimale, vollständige und überprüfbare Beispiel

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

Wo SpringServletConfigist

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

Endlich habe ich eine @Controllerim Paketcom.example.controllers

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

Der Kontextname meiner Anwendung lautet Example. Wenn ich eine Anfrage an sende

http://localhost:8080/Example/home

Die Anwendung antwortet mit einem HTTP-Status 404 und protokolliert Folgendes

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'

Ich habe eine JSP-Ressource bei /WEB-INF/jsps/index.jspIch habe erwartet, dass Spring MVC meinen Controller verwendet, um die Anforderung zu verarbeiten und an die JSP weiterzuleiten. Warum antwortet sie also mit einem 404?


Dies ist ein kanonischer Beitrag für Fragen zu dieser Warnmeldung.

Sotirios Delimanolis
quelle

Antworten:

99

Ihre Standard-Spring-MVC-Anwendung bedient alle Anforderungen über eine DispatcherServlet, die Sie bei Ihrem Servlet-Container registriert haben.

Das DispatcherServletprüft seine ApplicationContextund, falls verfügbar, die ApplicationContextmit einem ContextLoaderListenerfür spezielle Beans registrierten , die es benötigt, um seine Anforderungs-Serving-Logik einzurichten. Diese Beans sind in der Dokumentation beschrieben .

Die wohl wichtigsten Bohnen vom Typ HandlerMappingMap

Eingehende Anforderungen an Handler und eine Liste von Vor- und Nachprozessoren (Handler-Interceptors) basierend auf einigen Kriterien, deren Details je nach HandlerMappingImplementierung variieren . Die beliebteste Implementierung unterstützt kommentierte Controller, es gibt jedoch auch andere Implementierungen.

Das Javadoc vonHandlerMapping beschreibt weiter, wie sich Implementierungen verhalten müssen.

Das DispatcherServletfindet alle Beans dieses Typs und registriert sie in einer bestimmten Reihenfolge (kann angepasst werden). Während der Bearbeitung einer Anforderung DispatcherServletwerden diese HandlerMappingObjekte durchlaufen und jedes von ihnen getestet getHandler, um eines zu finden, das die eingehende Anforderung verarbeiten kann, die als Standard dargestellt wird HttpServletRequest. Ab 4.3.x, wenn es irgendwelche nicht finden , es protokolliert die Warnung , dass Sie sehen ,

Keine Zuordnung gefunden für HTTP - Anforderung mit URI [/some/path]in DispatcherServletmit dem Namen Somenamen

und löst entweder a aus NoHandlerFoundExceptionoder schreibt die Antwort sofort mit einem 404 Not Found-Statuscode fest.

Warum hat das nicht DispatcherServleteine finden , HandlerMappingdie meine Anfrage umgehen konnte?

Die häufigste HandlerMappingImplementierung ist RequestMappingHandlerMappingdie Registrierung von @ControllerBeans als Handler (eigentlich ihre mit @RequestMappingAnmerkungen versehenen Methoden). Sie können eine Bean dieses Typs entweder selbst (mit @Beanoder mit einem <bean>anderen Mechanismus) deklarieren oder die integrierten Optionen verwenden . Diese sind:

  1. Kommentieren Sie Ihre @ConfigurationKlasse mit @EnableWebMvc.
  2. Deklarieren Sie ein <mvc:annotation-driven />Mitglied in Ihrer XML-Konfiguration.

Wie der obige Link beschreibt, registrieren beide eine RequestMappingHandlerMappingBean (und eine Reihe anderer Dinge). Allerdings ist eine HandlerMappingnicht sehr nützlich ist, ohne einen Handler. RequestMappingHandlerMappingerwartet einige @ControllerBeans, daher müssen Sie diese auch deklarieren, durch @BeanMethoden in einer Java-Konfiguration oder <bean>Deklarationen in einer XML-Konfiguration oder durch Komponentenscannen von @Controllerannotierten Klassen in beiden. Stellen Sie sicher, dass diese Bohnen vorhanden sind.

Wenn Sie die Warnmeldung und einen 404 erhalten und alle oben genannten Punkte korrekt konfiguriert haben, senden Sie Ihre Anfrage an den falschen URI , der nicht von einer erkannten @RequestMappingMethode für kommentierte Handler verarbeitet wird.

Die spring-webmvcBibliothek bietet weitere integrierte HandlerMappingImplementierungen. Zum Beispiel BeanNameUrlHandlerMappingKarten

von URLs zu Beans mit Namen, die mit einem Schrägstrich beginnen ("/")

und du kannst immer deine eigenen schreiben. Natürlich müssen Sie sicherstellen, dass die Anforderung, die Sie senden, mindestens einem der HandlerMappingHandler des registrierten Objekts entspricht.

Wenn Sie keine HandlerMappingBeans implizit oder explizit registrieren (oder wenn dies der FalldetectAllHandlerMappings ist true), DispatcherServletwerden einige Standardeinstellungen registriert . Diese werden im DispatcherServlet.propertiesselben Paket wie die DispatcherServletKlasse definiert. Sie sind BeanNameUrlHandlerMappingund DefaultAnnotationHandlerMapping(ähnlich, RequestMappingHandlerMappingaber veraltet).

Debuggen

Spring MVC protokolliert registrierte Handler RequestMappingHandlerMapping. Zum Beispiel ein @ControllerLike

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

protokolliert Folgendes auf INFO-Ebene

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

Dies beschreibt die registrierte Zuordnung. Wenn Sie die Warnung sehen, dass kein Handler gefunden wurde, vergleichen Sie den URI in der Nachricht mit der hier aufgeführten Zuordnung. Alle im Abschnitt angegebenen Einschränkungen @RequestMappingmüssen übereinstimmen, damit Spring MVC den Handler auswählt.

Andere HandlerMappingImplementierungen protokollieren ihre eigenen Anweisungen, die auf ihre Zuordnungen und die entsprechenden Handler verweisen sollen.

Aktivieren Sie auf ähnliche Weise die Spring-Protokollierung auf DEBUG-Ebene, um zu sehen, welche Beans Spring registriert. Es sollte angeben, welche mit Anmerkungen versehenen Klassen gefunden werden, welche Pakete gescannt und welche Beans initialisiert werden. Wenn die erwarteten nicht vorhanden sind, überprüfen Sie Ihre ApplicationContextKonfiguration.

Andere häufige Fehler

A DispatcherServletist nur ein typischer Java EE Servlet. Sie registrieren es mit Ihrer typischen <web.xml> <servlet-class>und <servlet-mapping>Deklaration oder direkt ServletContext#addServletin einem WebApplicationInitializeroder mit einem beliebigen Mechanismus, den Spring Boot verwendet. Daher müssen Sie sich auf die in der Servlet-Spezifikation angegebene URL-Zuordnungslogik verlassen , siehe Kapitel 12. Siehe auch

In diesem Sinne besteht ein häufiger Fehler darin, das DispatcherServletmit einer URL-Zuordnung von zu registrieren /*, einen Ansichtsnamen von einer @RequestMappingHandler-Methode zurückzugeben und zu erwarten, dass eine JSP gerendert wird. Betrachten Sie beispielsweise eine Handlermethode wie

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

mit einem InternalResourceViewResolver

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

Sie könnte die Anforderung erwarten werden weitergeleitet auf dem Weg zu einer JSP Ressource /WEB-INF/jsps/example-view-name.jsp. Das wird nicht passieren. Unter der Annahme eines Kontextnamens von wird stattdessen Exampleder DisaptcherServletBericht erstellt

Keine Zuordnung gefunden für HTTP - Anforderung mit URI [/Example/WEB-INF/jsps/example-view-name.jsp]in DispatcherServletmit dem Namen ‚Dispatcher‘

Da das DispatcherServletauf alles abgebildet ist /*und /*mit allem übereinstimmt (mit Ausnahme von exakten Übereinstimmungen, die eine höhere Priorität haben), wird das DispatcherServletausgewählt, um das forwardvon dem JstlView(von dem zurückgegebenen InternalResourceViewResolver) zu verarbeiten. In fast allen Fällen wird der DispatcherServletnicht für die Verarbeitung einer solchen Anforderung konfiguriert .

In diesem vereinfachten Fall sollten Sie stattdessen das DispatcherServletto registrieren /und es als Standardservlet markieren. Das Standardservlet ist die letzte Übereinstimmung für eine Anforderung. Auf diese Weise kann Ihr typischer Servlet-Container eine interne Servlet-Implementierung auswählen *.jsp, die der JSP-Ressource zugeordnet ist (z. B. Tomcat JspServlet), bevor Sie es mit dem Standardservlet versuchen.

Das sehen Sie in Ihrem Beispiel.

Sotirios Delimanolis
quelle
Mit @EnableWebMvc hat sich das dispatcherServlet bereits bei / registriert. "Sie können erwarten, dass die Anforderung an eine JSP-Ressource unter dem Pfad /WEB-INF/jsps/example-view-name.jsp weitergeleitet wird. Dies wird nicht passieren." Wie funktioniert es, damit es an eine JSP-Ressource unter diesem Pfad weitergeleitet wird? Das ist im Grunde die gestellte Frage.
Tor
@Tor Allein macht @EnableWebMvceine @Configurationkommentierte Klasse das nicht. Es werden lediglich einige Standard-Spring-MVC-Handler / -Adapter-Beans zum Anwendungskontext hinzugefügt. Das Registrieren eines DispatcherServletDienstes /ist ein völlig separater Prozess, der auf verschiedene Arten durchgeführt wird, die ich im Abschnitt Andere häufige Fehler beschreibe . Ich beantworte die Frage zwei Absätze unter dem, was Sie zitiert haben.
Sotirios Delimanolis
4

Ich habe mein Problem behoben, als zusätzlich zu den zuvor beschriebenen: `

@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: JSP-Datei wird in der Spring Boot-Webanwendung nicht gerendert

RoutesMaps.com
quelle
2

In meinem Fall folgte ich der Interceptors Spring-Dokumentation für Version 5.1.2 (während ich Spring Boot v2.0.4.RELEASE verwendete ) und die WebConfigKlasse hatte die Annotation @EnableWebMvc, die mit etwas anderem in meiner Anwendung in Konflikt zu stehen schien, das meine statische Aufladung verhinderte Assets wurden nicht korrekt aufgelöst (dh es wurden keine CSS- oder JS-Dateien an den Client zurückgegeben).

Nachdem viele verschiedene Dinge auszuprobieren, habe ich versucht , das Entfernen der @EnableWebMvcund es funktionierte!

Bearbeiten: Hier ist die Referenzdokumentation , die besagt, dass Sie die @EnableWebMvcAnmerkung entfernen sollten

Zumindest in meinem Fall konfiguriere ich meine Spring-Anwendung anscheinend bereits (obwohl sie nicht mithilfe web.xmleiner anderen statischen Datei verwendet wird, sondern definitiv programmgesteuert), sodass es dort zu einem Konflikt kam.

Acapulco
quelle
1

Versuchen Sie, Ihren Code mit der folgenden Änderung in Ihrer Konfigurationsdatei zu ändern. Java-Konfiguration wird anstelle von verwendet application.properties. Vergessen Sie nicht, die Konfiguration in der configureDefaultServletHandlingMethode zu aktivieren .

WebMvcConfigurerAdapterKlasse ist veraltet, daher verwenden wir die WebMvcConfigurerSchnittstelle.

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

Ich benutze gradle, du solltest folgende Abhängigkeiten haben in 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'
}
Kennzeichen
quelle
0

Ich bin auf einen anderen Grund für denselben Fehler gestoßen. Dies kann auch an den Klassendateien liegen, die nicht für Ihre Datei controller.java generiert wurden. Infolgedessen kann das in web.xml erwähnte Dispatcher-Servlet es nicht der entsprechenden Methode in der Controller-Klasse zuordnen.

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

In Eclipse unter Projekt-> Wählen Sie Bereinigen -> Projekt erstellen. Überprüfen Sie, ob die Klassendatei für die Controller-Datei unter Builds in Ihrem Arbeitsbereich generiert wurde.

Anil NP
quelle
0

Für mich stellte ich fest, dass meine Zielklassen in einem Ordnermuster generiert wurden, das nicht mit dem Quellmuster übereinstimmt. Dies ist möglicherweise in Eclipse. Ich füge Ordner hinzu, um meine Controller zu enthalten, und füge sie nicht als Pakete hinzu. Also habe ich in der Frühjahrskonfiguration einen falschen Pfad definiert.

Meine Zielklasse generierte Klassen unter App und ich bezog mich auf com.happy.app

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

Ich habe Pakete (keine Ordner) für com.happy.app hinzugefügt und die Dateien aus Ordnern in Pakete in Eclipse verschoben, um das Problem zu beheben.

Roy
quelle
0

Reinigen Sie Ihren Server. Löschen Sie möglicherweise den Server und fügen Sie das Projekt erneut hinzu und führen Sie es aus.

  1. Stoppen Sie den Tomcat-Server

  2. Klicken Sie mit der rechten Maustaste auf den Server und wählen Sie "Reinigen".

  3. Klicken Sie erneut mit der rechten Maustaste auf den Server und wählen Sie "Tomcat-Arbeitsverzeichnis bereinigen".

2rahulsk
quelle
0

In meinem Fall habe ich mit dem Import von sekundären Java-Konfigurationsdateien in eine Java-Hauptkonfigurationsdatei herumgespielt. Beim Erstellen von sekundären Konfigurationsdateien hatte ich den Namen der Hauptkonfigurationsklasse geändert, aber den Namen in web.xml nicht aktualisiert. Bei jedem Neustart meines Tomcat-Servers wurden keine Mapping-Handler in der Eclipse IDE-Konsole angezeigt. Beim Versuch, zu meiner Startseite zu navigieren, wurde der folgende Fehler angezeigt:

1. November 2019 23:00:01 org.springframework.web.servlet.PageNotFound noHandlerFound WARNUNG: Keine Zuordnung für HTTP-Anforderung mit URI [/ webapp / home / index] in DispatcherServlet mit dem Namen 'dispatcher' gefunden

Das Update bestand darin, die Datei web.xml so zu aktualisieren, dass der alte Name "WebConfig" stattdessen "MainConfig" lautet, und sie einfach umzubenennen, um den neuesten Namen der Java-Hauptkonfigurationsdatei wiederzugeben (wobei "MainConfig" willkürlich ist und die Wörter " Web "und" Main ", die hier verwendet werden, sind keine Syntaxanforderungen. MainConfig war wichtig, da die Datei nach "WebController" gesucht hat, meiner Spring-MVC-Controller-Klasse, die meine Webanforderungen verarbeitet.

@ComponentScan(basePackageClasses={WebController.class})

web.xml hatte folgendes:

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

Die Datei web.xml hat jetzt:

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

Jetzt sehe ich die Zuordnung im Konsolenfenster:

INFO: "{[/ home / index], Methods = [GET]}" auf öffentliches org.springframework.web.servlet.ModelAndView com.lionheart.fourthed.controller.WebController.gotoIndex () abgebildet

Und meine Webseite wird wieder geladen.

Steve T.
quelle
-1

Ich hatte das gleiche Problem wie **No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName**

Nachdem ich 2 bis 4 Tage analysiert hatte, fand ich die Grundursache heraus. Klassendateien wurden nicht generiert, nachdem ich das Projekt ausgeführt habe. Ich habe auf die Registerkarte Projekt geklickt.

Projekt -> CloseProject -> OpenProject -> Clean -> Projekt erstellen

Klassendateien für den Quellcode wurden generiert. Es hat mein Problem gelöst. Um zu überprüfen, ob Klassendateien generiert wurden oder nicht, überprüfen Sie den Build-Ordner in Ihrem Projektordner.

Thanis Albert
quelle