Perché Spring MVC risponde con un 404 e riporta “Nessuna mapping trovata per richiesta HTTP con URI in DispatcherServlet”?

Sto scrivendo un’applicazione Spring MVC, distribuita su Tomcat. Vedi il seguente esempio minimale, completo e verificabile :

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

Dove SpringServletConfig è

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

Infine, ho un @Controller nel pacchetto com.example.controllers

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

Il nome di contesto della mia applicazione è Example . Quando invio una richiesta a

 http://localhost:8080/Example/home 

l’applicazione risponde con uno stato HTTP 404 e registra quanto segue

 WARN osweb.servlet.PageNotFound - No mapping found for HTTP request with URI `[/Example/WEB-INF/jsps/index.jsp]` in `DispatcherServlet` with name 'dispatcher' 

Ho una risorsa JSP su /WEB-INF/jsps/index.jsp Mi aspettavo che Spring MVC usasse il mio controller per gestire la richiesta e inoltrare al JSP, quindi perché sta rispondendo con un 404?


Questo è pensato per essere un post canonico per domande su questo messaggio di avviso.

L’applicazione MVC Spring standard servirà tutte le richieste attraverso un DispatcherServlet che hai registrato con il tuo contenitore Servlet.

DispatcherServlet esamina ApplicationContext e, se disponibile, ApplicationContext registrato con ContextLoaderListener per i bean speciali, deve impostare la logica di serving della richiesta. Questi fagioli sono descritti nella documentazione .

Probabilmente il più importante, i fagioli del tipo di mappa HandlerMapping

richieste in arrivo ai gestori e un elenco di pre e post-processori (interceptor del gestore) basati su alcuni criteri i cui dettagli variano a HandlerMapping dell’implementazione di HandlerMapping . L’implementazione più popolare supporta i controller annotati ma esistono anche altre implementazioni.

HandlerMapping di HandlerMapping descrive ulteriormente come devono comportarsi le implementazioni.

DispatcherServlet trova tutti i bean di questo tipo e li registra in qualche ordine (può essere personalizzato). Durante la pubblicazione di una richiesta, DispatcherServlet HandlerMapping loop di questi oggetti HandlerMapping e verifica ciascuno di essi con getHandler per trovarne uno in grado di gestire la richiesta in entrata, rappresentata come HttpServletRequest standard. A partire dal 4.3.x, se non ne trova , registra l’avviso che vedi

Nessun mapping trovato per la richiesta HTTP con URI [/some/path] in DispatcherServlet con nome SomeName

e lancia una NoHandlerFoundException o commette immediatamente la risposta con un codice di stato 404 non trovato.

Perché il DispatcherServlet non ha trovato un HandlerMapping grado di gestire la mia richiesta?

L’implementazione di HandlerMapping più comune è RequestMappingHandlerMapping , che gestisce la registrazione @Controller bean @Controller come gestori (in realtà i loro metodi annotati @RequestMapping ). Puoi dichiarare tu stesso un bean di questo tipo (con @Bean o o altro meccanismo) oppure puoi utilizzare le opzioni integrate . Questi sono:

  1. Annota la tua class @EnableWebMvc con @EnableWebMvc .
  2. Dichiarare un membro nella configurazione XML.

Come descritto nel link precedente, entrambi registreranno un bean RequestMappingHandlerMapping (e un mucchio di altre cose). Tuttavia, un HandlerMapping non è molto utile senza un gestore. RequestMappingHandlerMapping prevede alcuni bean @Controller quindi è necessario dichiararli anche, mediante i metodi @Bean in una configurazione Java o dichiarazioni in una configurazione XML o tramite la scansione dei componenti delle classi annotate @Controller in entrambi. Assicurati che questi fagioli siano presenti.

Se ricevi il messaggio di avviso e un 404 e hai configurato correttamente tutto quanto sopra correttamente, @RequestMapping tua richiesta all’URI errato , uno che non è gestito da un metodo di gestore annotato @RequestMapping rilevato.

La libreria spring-webmvc offre altre implementazioni di HandlerMapping . Ad esempio, BeanNameUrlHandlerMapping mappe BeanNameUrlHandlerMapping

dagli URL ai bean con nomi che iniziano con una barra (“/”)

e puoi sempre scrivere il tuo. Ovviamente, dovrai assicurarti che la richiesta di invio delle corrispondenze sia almeno uno dei gestori di oggetti HandlerMapping registrati.

Se non si registra in modo implicito o esplicito alcun bean di HandlerMapping (o se detectAllHandlerMappings è true ), DispatcherServlet registra alcuni valori predefiniti . Questi sono definiti in DispatcherServlet.properties nello stesso pacchetto della class DispatcherServlet . Sono BeanNameUrlHandlerMapping e DefaultAnnotationHandlerMapping (che è simile a RequestMappingHandlerMapping ma deprecato).

Debug

Spring MVC registra i gestori registrati tramite RequestMappingHandlerMapping . Ad esempio, un @Controller piace

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

registrerà quanto segue al livello INFO

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

Questo descrive la mapping registrata. Quando viene visualizzato l’avviso che non è stato trovato alcun gestore, confrontare l’URI nel messaggio con la mapping elencata qui. Tutte le restrizioni specificate in @RequestMapping devono corrispondere per Spring MVC per selezionare il gestore.

Altre implementazioni di HandlerMapping registrano le proprie dichiarazioni che dovrebbero suggerire i loro mapping e i relativi gestori.

Allo stesso modo, abilitare la registrazione Spring a livello DEBUG per vedere quali bean Spring registrano. Dovrebbe riportare quali classi annotate trova, quali pacchetti esegue la scansione e quali bean inizializza. Se quelli che ci si aspetta non sono presenti, quindi rivedere la configurazione di ApplicationContext .

Altri errori comuni

Un DispatcherServlet è solo un tipico Servlet EE Java. La si registra con la tipica dichiarazione e , oppure direttamente tramite ServletContext#addServlet in un object WebApplicationInitializer o con qualsiasi altro meccanismo utilizzato da Boot. Pertanto, è necessario fare affidamento sulla logica di mapping url specificata nella specifica Servlet , vedere il Capitolo 12. Vedere anche

  • Come vengono utilizzati i mapping degli URL di Servlet in web.xml?

Con questo in mente, un errore comune è registrare DispatcherServlet con una mapping URL di /* , restituendo un nome vista da un metodo handler @RequestMapping e aspettandosi che un JSP sia reso. Ad esempio, considera un metodo di gestione come

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

con un InternalResourceViewResolver

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

potresti aspettarti che la richiesta venga inoltrata a una risorsa JSP nel percorso /WEB-INF/jsps/example-view-name.jsp . Questo non succederà. Invece, assumendo un nome di contesto di Example , il DisaptcherServlet riporterà

Nessuna mapping trovata per richiesta HTTP con URI [/Example/WEB-INF/jsps/example-view-name.jsp] in DispatcherServlet con nome ‘dispatcher’

Poiché DispatcherServlet è mappato su /* e /* corrisponde a tutto (tranne le corrispondenze esatte, che hanno priorità più alta), il DispatcherServlet verrà scelto per gestire l’ forward da JstlView (restituito da InternalResourceViewResolver ). In quasi tutti i casi, DispatcherServlet non sarà configurato per gestire tale richiesta .

Invece, in questo caso semplicistico, dovresti registrare DispatcherServlet in / , contrassegnandolo come servlet predefinito. Il servlet predefinito è l’ultima corrispondenza per una richiesta. Ciò consentirà al tipico contenitore servlet di scegliere un’implementazione interna del Servlet, associata a *.jsp , per gestire la risorsa JSP (ad esempio, Tomcat ha JspServlet ), prima di provare con il servlet predefinito.

Questo è quello che stai vedendo nel tuo esempio.

Ho risolto il mio problema quando in aggiunta a quanto descritto in precedenza: `

 @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  

`da: il file JSP non viene visualizzato nell’applicazione Web Spring Boot

Mi sono imbattuto in un’altra ragione per lo stesso errore. Ciò potrebbe anche essere dovuto ai file di class non generati per il file controller.java. Di conseguenza, il servlet del dispatcher menzionato in web.xml non è in grado di associarlo al metodo appropriato nella class controller.

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

In Eclipse sotto Progetto-> selezionare clean -> Build Project.Do dare un controllo se il file di class è stato generato per il file del controller sotto build nell’area di lavoro.