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.
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]
InDispatcherServlet
mit dem Namen SomeName gefunden
und entweder wirft ein NoHandlerFoundException
oder schreibt die Antwort sofort mit dem Statuscode 404 Not Found fest.
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:
@Configuration
Mit @EnableWebMvc
.<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 detectAllHandlerMappings
true
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).
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.
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]
InDispatcherServlet
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.
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
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.
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.
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.