it-swarm.com.de

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

Ich schreibe eine Spring-MVC-Anwendung, die auf Tomcat bereitgestellt wird. Siehe folgendes minimales, vollständiges und überprüfbares 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 SpringServletConfig ist 

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

Schließlich habe ich einen @Controller im Paket com.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 unter /WEB-INF/jsps/index.jsp. Ich habe erwartet, dass Spring MVC meinen Controller verwendet, um die Anforderung zu verarbeiten und an die JSP weiterzuleiten. Warum antwortet er mit einer 404?


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

68

Ihre Standard-Spring-MVC-Anwendung wird alle Anforderungen über ein DispatcherServlet bedienen, das Sie bei Ihrem Servlet-Container registriert haben.

Das DispatcherServlet betrachtet sein ApplicationContext und, falls verfügbar, das ApplicationContext, das mit einem ContextLoaderListener für spezielle Beans registriert ist, die es zum Einrichten seiner Anforderungsversorgungslogik benötigt. Diese Beans sind in der Dokumentation beschrieben .

Die wohl wichtigsten Bohnen vom Typ HandlerMapping map

eingehende Anforderungen an Handler und eine Liste von Pre- und Postprozessoren (Handler-Interceptors) basierend auf einigen Kriterien, deren Details je nach HandlerMapping-Implementierung variieren. Die beliebteste Implementierung unterstützt mit Anmerkungen versehene Controller, es gibt jedoch auch andere Implementierungen.

Das javadoc von HandlerMapping beschreibt weiter, wie sich Implementierungen verhalten müssen.

Der DispatcherServlet findet alle Beans dieses Typs und registriert sie in einer bestimmten Reihenfolge (kann angepasst werden). Während der Bearbeitung einer Anfrage durchläuft der DispatcherServlet diese HandlerMapping -Objekte und testet jedes mit getHandler , um ein Objekt zu finden, das die eingehende Anfrage verarbeiten kann, die als Standard HttpServletRequest dargestellt wird. Ab 4.3.x wird , wenn keine gefunden werden, die Warnung protokolliert ​​angezeigt

Keine Zuordnung für HTTP-Anforderung mit URI [/some/path] In DispatcherServlet mit dem Namen SomeName gefunden

und entweder wirft ein NoHandlerFoundException oder schreibt die Antwort sofort mit dem Statuscode 404 Not Found fest.

Warum hat der DispatcherServlet kein HandlerMapping gefunden, das meine Anfrage bearbeiten kann?

Die gebräuchlichste HandlerMapping-Implementierung ist RequestMappingHandlerMapping , die die Registrierung von @Controller - Beans als Handler behandelt (wirklich ihre mit @RequestMapping - Annotationen versehenen Methoden). Sie können eine Bean dieses Typs entweder selbst deklarieren (mit @Bean Oder <bean> Oder einem anderen Mechanismus) oder die eingebauten Optionen verwenden. Diese sind:

  1. Kommentieren Sie Ihre Klasse @Configuration Mit @EnableWebMvc.
  2. Deklarieren Sie ein Mitglied <mvc:annotation-driven /> In Ihrer XML-Konfiguration.

Wie der obige Link beschreibt, registrieren beide eine RequestMappingHandlerMapping-Bean (und eine Reihe anderer Dinge). Ein HandlerMapping ist jedoch ohne einen Handler nicht sehr nützlich. RequestMappingHandlerMapping erwartet einige @Controller - Beans, daher müssen Sie diese auch mit @Bean - Methoden in einer Java Konfiguration oder <bean> - Deklaration in einer XML deklarieren Konfiguration oder durch Überprüfung von @Controller - Komponenten in einer der beiden Klassen. Vergewissern Sie sich, dass diese Beans vorhanden sind.

Wenn Sie die Warnmeldung und eine 404 erhalten und alle oben genannten Einstellungen korrekt vorgenommen haben , senden Sie Ihre Anfrage an die falsche URI , eine, die nicht von einer mit @RequestMapping - Annotationen versehenen Handlermethode behandelt wird.

Die Bibliothek spring-webmvc Bietet weitere integrierte HandlerMapping-Implementierungen. Zum Beispiel BeanNameUrlHandlerMapping maps

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

und du kannst immer deine eigenen schreiben. Offensichtlich müssen Sie sicherstellen, dass die Anforderung, die Sie senden, mit mindestens einem der Handler des registrierten HandlerMapping-Objekts übereinstimmt.

Wenn Sie keine HandlerMapping-Beans implizit oder explizit registrieren (oder wenn detectAllHandlerMappingstrue ist), registriert die DispatcherServlet einige Standardeinstellungen . Diese sind in DispatcherServlet.properties im selben Paket wie die Klasse DispatcherServlet definiert. Sie sind BeanNameUrlHandlerMapping und DefaultAnnotationHandlerMapping (ähnlich zu RequestMappingHandlerMapping, aber veraltet).

Debuggen

Spring MVC protokolliert über RequestMappingHandlerMapping registrierte Handler. Zum Beispiel ein @Controller Wie

@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 das registrierte Mapping. Wenn die Warnung angezeigt wird, dass kein Handler gefunden wurde, vergleichen Sie den URI in der Nachricht mit der hier aufgeführten Zuordnung. Alle in @RequestMapping angegebenen Einschränkungen müssen übereinstimmen, damit Spring MVC den Handler auswählt.

Andere HandlerMapping-Implementierungen protokollieren ihre eigenen Anweisungen, die auf ihre Zuordnungen und ihre 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 Annotationen versehenen Klassen gefunden, welche Pakete durchsucht und welche Beans initialisiert werden. Wenn die erwarteten nicht vorhanden sind, überprüfen Sie Ihre ApplicationContext-Konfiguration.

Andere häufige Fehler

Ein DispatcherServlet ist nur ein typisches Java EE Servlet . Sie registrieren es mit Ihrem typischen <web.xml><servlet-class> Und <servlet-mapping> Deklaration oder direkt durch ServletContext#addServlet in einem WebApplicationInitializer oder mit einem beliebigen Mechanismus, den Spring Boot verwendet. Als solches müssen Sie sich auf die URL verlassen Zuordnung Logik in der Servlet-Spezifikation angegeben, siehe Kapitel 12. Siehe auch

Aus diesem Grund ist es ein häufiger Fehler, DispatcherServlet mit einer URL-Zuordnung von /* Zu registrieren, einen Ansichtsnamen von einer @RequestMapping - Handlermethode zurückzugeben und zu erwarten, dass eine JSP gerendert wird. Betrachten Sie zum Beispiel 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önnen erwarten, dass die Anforderung weitergeleitet ​​an eine JSP-Ressource unter dem Pfad /WEB-INF/jsps/example-view-name.jsp gesendet wird. Das wird nicht passieren. Angenommen, der Kontextname ist Example, wird DisaptcherServlet gemeldet

Keine Zuordnung für HTTP-Anforderung mit URI [/Example/WEB-INF/jsps/example-view-name.jsp] In DispatcherServlet mit dem Namen 'dispatcher' gefunden.

Da DispatcherServlet auf /* Und /* Abgebildet ist, stimmt alles überein (mit Ausnahme von genauen Übereinstimmungen, die eine höhere Priorität haben), wird DispatcherServlet ausgewählt, um die forward aus JstlView zu behandeln = (von InternalResourceViewResolver zurückgegeben). In fast allen Fällen wird die Variable DispatcherServlet nicht für die Verarbeitung einer solchen Anforderung konfiguriert .

In diesem vereinfachenden Fall sollten Sie stattdessen DispatcherServlet unter / Registrieren und 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, die *.jsp Zugeordnet ist, um die JSP-Ressource zu verarbeiten (z. B. hat Tomcat JspServlet ), bevor Sie das Standardservlet verwenden .

Das sehen Sie in Ihrem Beispiel.

83

Ich habe mein Problem gelöst, als zusätzlich zu den oben 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 Spring Boot-Webanwendung nicht gerendert

2
RoutesMaps.com

Ich habe einen anderen Grund für den gleichen Fehler gefunden. Dies kann auch daran liegen, dass die Klassendateien nicht für Ihre Controller.Java-Datei 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()
{.....}
}

Wählen Sie in Eclipse unter Projekt-> Reinigen -> Projekt erstellen aus. Prüfen Sie, ob die Klassendatei für die Controller-Datei unter Builds in Ihrem Arbeitsbereich generiert wurde.

0
Anil N.P

Für mich habe ich festgestellt, dass meine Zielklassen in einem Ordnermuster generiert wurden, das nicht mit der Quelle identisch ist. Dies ist möglicherweise in Eclipse. Ich füge Ordner hinzu, die meine Controller enthalten, und füge sie nicht als Pakete hinzu. Am Ende habe ich also einen falschen Pfad in der Frühlingskonfiguration definiert.

Meine Zielklasse erzeugte 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 und das Problem behoben.

0
Roy

In meinem Fall verfolgte ich die Interceptors Spring-Dokumentation für Version 5.1.2 (bei Verwendung von Spring Boot v2.0.4.RELEASE), und die WebConfig-Klasse hatte die Annotation @EnableWebMvc, die scheinbar in Konflikt stand etwas anderes in meiner Anwendung, das die korrekte Auflösung meiner statischen Assets verhinderte (dh es wurden keine CSS- oder JS-Dateien an den Client zurückgegeben).

Nachdem ich viele verschiedene Dinge ausprobiert hatte, probierte ich entfernen den @EnableWebMvc und es funktionierte!

Edit: Hier ist die Referenzdokumentation die besagt, dass Sie die Annotation @EnableWebMvc entfernen sollten

Offensichtlich konfiguriere ich in meinem Fall zumindest meine Spring-Anwendung (obwohl nicht web.xml oder eine andere statische Datei verwendet wird, dies ist definitiv programmgesteuert), sodass es dort zu einem Konflikt kam.

0
Acapulco