Perché Spring MVC risponde con un 404 e segnala "Nessuna mappatura trovata per la richiesta HTTP con URI […] in DispatcherServlet"?


91

Sto scrivendo un'applicazione Spring MVC distribuita su Tomcat. Vedi il seguente esempio minimo, 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 SpringServletConfigsi trova

@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, ne ho una @Controllernella confezionecom.example.controllers

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

Il nome del 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  o.s.web.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 in /WEB-INF/jsps/index.jspMi aspettavo che Spring MVC usasse il mio controller per gestire la richiesta e inoltrarla al JSP, quindi perché risponde con un 404?


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

Risposte:


100

La tua applicazione Spring MVC standard servirà tutte le richieste tramite un DispatcherServletche hai registrato con il tuo contenitore Servlet.

Le DispatcherServletguarda il suo ApplicationContexte, se disponibile, il ApplicationContextregistrati con una ContextLoaderListenerper i fagioli speciali di cui ha bisogno per impostare la sua richiesta logica di servire. Questi bean sono descritti nella documentazione .

Probabilmente il più importante, i fagioli della HandlerMappingmappa dei tipi

richieste in arrivo ai gestori e un elenco di pre e post processori (handler interceptor) in base ad alcuni criteri i cui dettagli variano a seconda HandlerMappingdell'implementazione. L'implementazione più popolare supporta controller annotati ma esistono anche altre implementazioni.

Il javadoc diHandlerMapping descrive ulteriormente come devono comportarsi le implementazioni.

La DispatcherServlettrova tutti i fagioli di questo tipo e li registra in un ordine (personalizzabile). Mentre serve una richiesta, il DispatcherServletloop passa attraverso questi HandlerMappingoggetti e testa ciascuno di essi getHandlerper trovarne uno in grado di gestire la richiesta in arrivo, rappresentata come standard HttpServletRequest. A partire da 4.3.x, se non ne trova , registra l'avviso che vedi

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

e sia getta una NoHandlerFoundExceptiono immediatamente impegna la risposta con un codice di stato 404 Not Found.

Perché non ho DispatcherServlettrovato un in HandlerMappinggrado di gestire la mia richiesta?

L' HandlerMappingimplementazione più comune è RequestMappingHandlerMapping, che gestisce la registrazione dei @Controllerbean come gestori (in realtà i loro @RequestMappingmetodi annotati). Puoi dichiarare tu stesso un bean di questo tipo (con @Beano <bean>o un altro meccanismo) oppure puoi usare le opzioni integrate . Questi sono:

  1. Annota la tua @Configurationclasse con @EnableWebMvc.
  2. Dichiara un <mvc:annotation-driven />membro nella tua configurazione XML.

Come descritto nel collegamento sopra, entrambi registreranno un RequestMappingHandlerMappingbean (e un mucchio di altre cose). Tuttavia, a HandlerMappingnon è molto utile senza un gestore. RequestMappingHandlerMappingsi aspetta alcuni @Controllerbean quindi è necessario dichiararli anche tramite @Beanmetodi in una configurazione Java o <bean>dichiarazioni in una configurazione XML o tramite la scansione dei componenti delle @Controllerclassi annotate in entrambe. Assicurati che questi fagioli siano presenti.

Se ricevi il messaggio di avviso e un 404 e hai configurato tutto quanto sopra correttamente, stai inviando la tua richiesta all'URI sbagliato , uno che non è gestito da un @RequestMappingmetodo del gestore annotato rilevato .

La spring-webmvclibreria offre altre HandlerMappingimplementazioni integrate . Ad esempio, BeanNameUrlHandlerMappingmappe

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

e puoi sempre scrivere il tuo. Ovviamente, dovrai assicurarti che la richiesta che stai inviando corrisponda ad almeno uno dei HandlerMappinggestori dell'oggetto registrato .

Se non registri implicitamente o esplicitamente alcun HandlerMappingbean (o se lo detectAllHandlerMappingsè true), DispatcherServletregistra alcuni valori predefiniti . Questi sono definiti nello DispatcherServlet.propertiesstesso pacchetto della DispatcherServletclasse. Sono BeanNameUrlHandlerMappinge DefaultAnnotationHandlerMapping(che è simile a RequestMappingHandlerMappingma deprecato).

Debug

Spring MVC registrerà i gestori registrati tramite RequestMappingHandlerMapping. Ad esempio, un @Controllerlike

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

registrerà quanto segue a livello di INFO

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

Descrive la mappatura registrata. Quando viene visualizzato l'avviso che non è stato trovato alcun gestore, confronta l'URI nel messaggio con la mappatura elencata qui. Tutte le restrizioni specificate in @RequestMappingdevono corrispondere affinché Spring MVC selezioni il gestore.

Altre HandlerMappingimplementazioni registrano le proprie istruzioni che dovrebbero suggerire le loro mappature e i loro gestori corrispondenti.

Allo stesso modo, abilitare la registrazione Spring a livello DEBUG per vedere quali bean Spring registra. Dovrebbe riportare quali classi annotate trova, quali pacchetti analizza e quali bean inizializza. Se quelli che ti aspettavi non sono presenti, controlla la tua ApplicationContextconfigurazione.

Altri errori comuni

A DispatcherServletè solo un tipico Java EE Servlet. Lo registri con la tua tipica dichiarazione <web.xml> <servlet-class>e <servlet-mapping>, o direttamente attraverso ServletContext#addServletin a WebApplicationInitializer, o con qualsiasi meccanismo che usi Spring boot. Pertanto, è necessario fare affidamento sulla logica di mappatura URL specificata nella specifica Servlet , vedere il Capitolo 12. Vedere anche

Con questo in mente, un errore comune è registrare DispatcherServletcon una mappatura URL /*, restituendo un nome di visualizzazione da un @RequestMappingmetodo gestore e aspettandosi che venga eseguito il rendering di un JSP. 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. Non succederà. Invece, assumendo un nome di contesto di Example, DisaptcherServletriporterà

Nessun mapping trovato per la richiesta HTTP con URI [/Example/WEB-INF/jsps/example-view-name.jsp]in DispatcherServletcon il nome 'dispatcher'

Poiché DispatcherServletè mappato a /*e /*corrisponde a tutto (tranne le corrispondenze esatte, che hanno una priorità più alta), DispatcherServletverrebbe scelto di gestire il forwardda JstlView(restituito da InternalResourceViewResolver). In quasi tutti i casi, DispatcherServletnon sarà configurato per gestire tale richiesta .

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

Questo è quello che vedi nel tuo esempio.


Con @EnableWebMvc il dispatcherServlet è già registrato in /. "potresti aspettarti che la richiesta venga inoltrata a una risorsa JSP nel percorso /WEB-INF/jsps/example-view-name.jsp. Questo non accadrà." Come si fa a farlo funzionare in modo che inoltri a una risorsa JSP in quel percorso? Questa è fondamentalmente la domanda posta.
Tor

@Tor Da solo, @EnableWebMvcsu una @Configurationclasse annotata non lo fa. Tutto ciò che fa è aggiungere un numero di bean di gestione / adattatori Spring MVC predefiniti al contesto dell'applicazione. La registrazione di un DispatcherServletservizio /è un processo completamente separato che viene eseguito in diversi modi che descrivo nella sezione Altri errori comuni . Rispondo alla domanda posta due paragrafi sotto quello che hai citato.
Sotirios Delimanolis

5

Ho risolto il mio problema oltre a quanto descritto prima: `

@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: file JSP non visualizzato nell'applicazione web Spring Boot


2

Nel mio caso, stavo seguendo la documentazione di Interceptors Spring per la versione 5.1.2 (durante l'utilizzo di Spring Boot v2.0.4.RELEASE ) e la WebConfigclasse aveva l'annotazione @EnableWebMvc, che sembrava essere in conflitto con qualcos'altro nella mia applicazione che impediva la mia elettricità statica le risorse venivano risolte correttamente (cioè nessun file CSS o JS veniva restituito al client).

Dopo aver provato un sacco di cose diverse, ho cercato di rimuovere la @EnableWebMvce ha funzionato!

Modifica: ecco la documentazione di riferimento che dice che dovresti rimuovere l' @EnableWebMvcannotazione

Apparentemente, almeno nel mio caso, sto già configurando la mia applicazione Spring (anche se non utilizzando web.xmlo qualsiasi altro file statico, è decisamente programmatico), quindi c'era un conflitto lì.


1

Prova a modificare il tuo codice con la seguente modifica sul tuo file di configurazione. Java config viene utilizzato al posto di application.properties. Non dimenticare di abilitare la configurazione nel configureDefaultServletHandlingmetodo.

WebMvcConfigurerAdapterclass è deprecata, quindi usiamo 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();
    }
}

Io uso gradle, dovresti avere le seguenti dipendenze 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'
}

0

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

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

In eclipse sotto Project-> seleziona clean -> Build Project.Da controllare se il file di classe è stato generato per il file controller sotto builds nel tuo spazio di lavoro.


0

Per me, ho scoperto che le mie classi di destinazione sono state generate in un modello di cartella diverso da quello di origine. Questo è forse in eclipse aggiungo cartelle per contenere i miei controller e non li aggiungo come pacchetti. Quindi ho finito per definire un percorso errato nella configurazione di primavera.

La mia classe di destinazione stava generando classi in app e mi riferivo a com.happy.app

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

Ho aggiunto pacchetti (non cartelle) per com.happy.app e spostato i file dalle cartelle ai pacchetti in eclipse e ha risolto il problema.


0

Pulisci il tuo server. Forse elimina il server e aggiungi di nuovo il progetto ed esegui.

  1. Arresta il server Tomcat

  2. Fai clic con il pulsante destro del mouse sul server e seleziona "Pulisci"

  3. Fare nuovamente clic con il pulsante destro del mouse sul server e selezionare "Pulisci directory di lavoro Tomcat"


0

Nel mio caso, stavo giocando con l'importazione di file di configurazione java secondari in un file di configurazione java principale. Durante la creazione dei file di configurazione secondari, avevo cambiato il nome della classe di configurazione principale, ma non ero riuscito ad aggiornare il nome in web.xml. Quindi, ogni volta che avevo riavviato il mio server Tomcat, non vedevo i gestori di mappatura annotati nella console IDE di Eclipse e quando ho provato a navigare nella mia home page vedevo questo errore:

1 nov 2019 23:00:01 org.springframework.web.servlet.PageNotFound noHandlerFound AVVISO: nessuna mappatura trovata per la richiesta HTTP con URI [/ webapp / home / index] in DispatcherServlet con il nome "dispatcher"

La correzione era aggiornare il file web.xml in modo che il vecchio nome "WebConfig" fosse invece "MainConfig", rinominandolo semplicemente per riflettere l'ultimo nome del file java config principale (dove "MainConfig" è arbitrario e le parole " Web "e" Main "utilizzati qui non sono un requisito di sintassi). MainConfig era importante, perché era il file che faceva la scansione del componente per "WebController", la mia classe di controller Spring mvc che gestisce le mie richieste web.

@ComponentScan(basePackageClasses={WebController.class})

web.xml aveva questo:

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

Il file web.xml ora ha:

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

Ora vedo la mappatura nella finestra della console:

INFORMAZIONI: mappato "{[/ home / index], methods = [GET]}" su org.springframework.web.servlet.ModelAndView com.lionheart.fourthed.controller.WebController.gotoIndex ()

E la mia pagina web si sta caricando di nuovo.


-1

Ho avuto lo stesso problema di **No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName**

Dopo aver analizzato per 2 o 4 giorni ho scoperto la causa principale. I file di classe non sono stati generati dopo aver eseguito il progetto. Ho fatto clic sulla scheda del progetto.

Progetto -> CloseProject -> OpenProject -> Pulisci -> Crea progetto

Sono stati generati file di classe per il codice sorgente. Ha risolto il mio problema. Per verificare se i file di classe sono stati generati o meno, controlla la cartella Build nella cartella del progetto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.