Dlaczego Spring MVC odpowiada 404 i zgłasza „Nie znaleziono mapowania dla żądania HTTP z URI […] w DispatcherServlet”?

91

Piszę aplikację Spring MVC wdrożoną na Tomcat. Zobacz następujący minimalny, kompletny i weryfikowalny przykład

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

Gdzie SpringServletConfigjest

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

Wreszcie mam @Controllerw paczcecom.example.controllers

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

Nazwa kontekstu mojej aplikacji to Example. Kiedy wysyłam prośbę do

http://localhost:8080/Example/home

aplikacja odpowiada statusem HTTP 404 i rejestruje następujące informacje

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'

Mam zasób JSP na. /WEB-INF/jsps/index.jspSpodziewałem się, że Spring MVC użyje mojego kontrolera do obsługi żądania i przekazania do strony JSP, więc dlaczego odpowiada 404?


To ma być kanoniczny post na pytania dotyczące tego ostrzeżenia.

Sotirios Delimanolis
źródło

Odpowiedzi:

100

Twoja standardowa aplikacja Spring MVC będzie obsługiwać wszystkie żądania za pośrednictwem DispatcherServletkonta zarejestrowanego w kontenerze serwletów.

Te DispatcherServletspojrzenia na jego ApplicationContextoraz, jeśli jest dostępny, ApplicationContextzarejestrowany w ContextLoaderListenerspecjalnych ziaren potrzebnych do konfiguracji jego wniosek służąc logicznego. Te ziarna są opisane w dokumentacji .

Chyba najważniejsza, fasolowa HandlerMappingmapa typu

przychodzące żądania do osób obsługujących oraz lista pre- i postprocesorów (przechwytywaczy obsługi) w oparciu o pewne kryteria, których szczegóły różnią się w zależności od HandlerMappingimplementacji. Najpopularniejsza implementacja obsługuje kontrolery z adnotacjami, ale istnieją również inne implementacje.

Dokument javadoc ofHandlerMapping dalej opisuje, jak muszą zachowywać się implementacje.

DispatcherServletWyszukuje wszystkie ziarna tego typu i rejestruje je w jakimś celu (można dostosować). Obsługując żądanie, DispatcherServletpętle przechodzą przez te HandlerMappingobiekty i testują każdy z nich, getHandleraby znaleźć taki, który może obsłużyć przychodzące żądanie, reprezentowane jako standard HttpServletRequest. Od wersji 4.3.x, jeśli nie znajdzie żadnego , rejestruje ostrzeżenie , które widzisz

Nie znaleziono mapowania dla żądania HTTP z identyfikatorem URI [/some/path]w DispatcherServletnazwie SomeName

i albo zgłasza, NoHandlerFoundExceptionalbo natychmiast zatwierdza odpowiedź z kodem stanu 404 Not Found.

Dlaczego nie DispatcherServletznaleziono elementu, HandlerMappingktóry mógłby obsłużyć moją prośbę?

Najpowszechniejszą HandlerMappingimplementacją jest RequestMappingHandlerMapping, która obsługuje rejestrowanie @Controllerfasoli jako programów obsługi (tak naprawdę ich @RequestMappingmetody z adnotacjami). Możesz samodzielnie zadeklarować ziarno tego typu (za pomocą @Beanlub <bean>lub innego mechanizmu) lub skorzystać z wbudowanych opcji . To są:

  1. Dodaj adnotację do swojej @Configurationklasy za pomocą @EnableWebMvc.
  2. Zadeklaruj <mvc:annotation-driven />członka w konfiguracji XML.

Jak opisuje powyższy link, oba zarejestrują RequestMappingHandlerMappingfasolę (i kilka innych rzeczy). Jednak HandlerMappingnie jest zbyt przydatny bez programu obsługi. RequestMappingHandlerMappingoczekuje niektórych komponentów @Controllerbean, więc musisz je również zadeklarować za pomocą @Beanmetod w konfiguracji Java lub <bean>deklaracji w konfiguracji XML lub przez skanowanie komponentów @Controllerklas z adnotacjami w obu. Upewnij się, że te ziarna są obecne.

Jeśli otrzymujesz komunikat ostrzegawczy i 404, a wszystkie powyższe elementy zostały poprawnie skonfigurowane, to wysyłasz żądanie do niewłaściwego identyfikatora URI , który nie jest obsługiwany przez wykrytą @RequestMappingmetodę obsługi z adnotacjami.

Do spring-webmvcoferty Biblioteka Inne wbudowane w HandlerMappingimplementacji. Na przykład BeanNameUrlHandlerMappingmapy

od adresów URL do fasoli z nazwami zaczynającymi się od ukośnika („/”)

i zawsze możesz napisać własne. Oczywiście musisz się upewnić, że wysyłane żądanie pasuje do co najmniej jednego z HandlerMappingmodułów obsługi zarejestrowanego obiektu.

Jeśli nie zarejestrujesz niejawnie lub jawnie żadnych HandlerMappingziaren (lub jeśli detectAllHandlerMappingstak true), DispatcherServletzarejestruje niektóre wartości domyślne . Są one zdefiniowane w DispatcherServlet.propertiestym samym pakiecie co DispatcherServletklasa. Są BeanNameUrlHandlerMappingi DefaultAnnotationHandlerMapping(co jest podobne do, RequestMappingHandlerMappingale przestarzałe).

Debugowanie

Spring MVC będzie rejestrować programy obsługi zarejestrowane za pośrednictwem RequestMappingHandlerMapping. Na przykład @Controllerpolubienie

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

zarejestruje następujące informacje na poziomie INFO

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

Opisuje zarejestrowane mapowanie. Gdy zobaczysz ostrzeżenie, że nie znaleziono programu obsługi, porównaj identyfikator URI w komunikacie z mapowaniem wymienionym tutaj. Wszystkie ograniczenia określone w pliku @RequestMappingmuszą być zgodne, aby Spring MVC wybrał procedurę obsługi.

Inne HandlerMappingimplementacje rejestrują własne instrukcje, które powinny wskazywać na ich mapowania i odpowiadające im funkcje obsługi.

Podobnie, włącz rejestrowanie Springa na poziomie DEBUG, aby zobaczyć, które ziarna rejestruje Spring. Powinien raportować, które klasy z adnotacjami znajdzie, które pakiety skanuje i które fasole inicjuje. Jeśli nie ma tych, których się spodziewałeś, sprawdź swoją ApplicationContextkonfigurację.

Inne typowe błędy

A DispatcherServletto po prostu typowy Java EE Servlet. Rejestrujesz to za pomocą typowego <web.xml> <servlet-class>i <servlet-mapping>deklaracji, lub bezpośrednio przez ServletContext#addServleta WebApplicationInitializer, lub z jakimkolwiek mechanizmem używanym przez Spring boot. W związku z tym musisz polegać na logice odwzorowywania adresów URL określonej w specyfikacji serwletu , patrz Rozdział 12. Zobacz także

Mając to na uwadze, częstym błędem jest rejestrowanie DispatcherServletadresu URL z mapowaniem adresu /*, zwracanie nazwy widoku z @RequestMappingmetody obsługi i oczekiwanie na renderowanie strony JSP. Na przykład rozważmy metodę obsługi, taką jak

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

z InternalResourceViewResolver

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

można oczekiwać, że żądanie zostanie przekazane do zasobu JSP w ścieżce /WEB-INF/jsps/example-view-name.jsp. To się nie stanie. Zamiast tego, zakładając nazwę kontekstu Example, DisaptcherServletraport zgłosi

Nie znaleziono mapowanie żądania HTTP z URI [/Example/WEB-INF/jsps/example-view-name.jsp]w DispatcherServleto nazwie „dyspozytora”

Ponieważ DispatcherServletjest odwzorowany na /*i /*dopasowuje wszystko (z wyjątkiem dokładnych dopasowań, które mają wyższy priorytet), DispatcherServletzostanie wybrany do obsługi forwardz JstlView(zwróconego przez InternalResourceViewResolver). W prawie każdym przypadku DispatcherServletnie zostanie skonfigurowany do obsługi takiego żądania .

Zamiast tego, w tym uproszczonym przypadku, powinieneś zarejestrować DispatcherServletto /, oznaczając go jako domyślny serwlet. Domyślnym serwletem jest ostatnie dopasowanie dla żądania. Umożliwi to typowemu kontenerowi serwletów wybranie wewnętrznej implementacji serwletu, odwzorowanej na *.jsp, do obsługi zasobu JSP (na przykład Tomcat JspServlet), przed próbą z domyślnym serwletem.

To właśnie widzisz na swoim przykładzie.

Sotirios Delimanolis
źródło
Z @EnableWebMvc dispatcherServlet jest już zarejestrowany w /. „można oczekiwać, że żądanie zostanie przekazane do zasobu JSP w ścieżce /WEB-INF/jsps/example-view-name.jsp. To się nie stanie.” Jak sprawić, by działał, aby przekazywał dalej do zasobu JSP w tej ścieżce? To w zasadzie zadane pytanie.
Tor
@Tor Samo @EnableWebMvcw @Configurationklasie z adnotacjami tego nie robi. Wszystko, co robi, to dodanie kilku domyślnych komponentów bean obsługi / adapterów Spring MVC do kontekstu aplikacji. Rejestracja w DispatcherServletcelu wyświetlenia /to całkowicie oddzielny proces, który można wykonać na kilka sposobów, które opisuję w sekcji Inne typowe błędy . Odpowiadam na pytanie zadane dwa akapity poniżej tego, co zacytowałeś.
Sotirios Delimanolis
5

Rozwiązałem swój problem, gdy oprócz opisanego wcześniej: `

@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: plik JSP nie jest renderowany w aplikacji internetowej Spring Boot

RoutesMaps.com
źródło
2

W moim przypadku postępowałem zgodnie z dokumentacją Interceptors Spring dla wersji 5.1.2 (podczas korzystania ze Spring Boot v2.0.4.RELEASE ) i WebConfigklasa miała adnotację @EnableWebMvc, która wydawała się kolidować z czymś innym w mojej aplikacji, co zapobiegało mojemu statycznemu zasoby nie zostały poprawnie rozwiązane (tj. żadne pliki CSS ani JS nie zostały zwrócone do klienta).

Po wypróbowaniu wielu różnych rzeczy, próbowałem usunięcie@EnableWebMvc i to działa!

Edycja: oto dokumentacja referencyjna, która mówi, że należy usunąć @EnableWebMvcadnotację

Najwyraźniej przynajmniej w moim przypadku konfiguruję już swoją aplikację Spring (chociaż nie używam web.xmlani żadnego innego pliku statycznego, zdecydowanie programistycznie), więc był tam konflikt.

Acapulco
źródło
1

Spróbuj poprawić swój kod, wprowadzając następującą zmianę w pliku konfiguracyjnym. Konfiguracja Java jest używana zamiast application.properties. Nie zapomnij włączyć konfiguracji w configureDefaultServletHandlingmetodzie.

WebMvcConfigurerAdapterclass jest przestarzała, więc używamy 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();
    }
}

Używam gradle, twój powinien mieć następujące zależności w 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'
}
znak
źródło
0

Natknąłem się na inny powód tego samego błędu. Może to być również spowodowane tym, że pliki klas nie zostały wygenerowane dla pliku controller.java. W rezultacie serwlet dyspozytora wymieniony w pliku web.xml nie może odwzorować go na odpowiednią metodę w klasie kontrolera.

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

W eclipse w Project-> wybierz clean -> Build Project. Sprawdź, czy plik klasy został wygenerowany dla pliku kontrolera w builds w twoim obszarze roboczym.

Anil NP
źródło
0

Dla mnie odkryłem, że moje klasy docelowe zostały wygenerowane we wzorcu folderu, który nie jest taki sam jak źródło. Prawdopodobnie w eclipse dodaję foldery do przechowywania moich kontrolerów i nie dodaję ich jako pakietów. Skończyło się na tym, że zdefiniowałem nieprawidłową ścieżkę w konfiguracji wiosennej.

Moja klasa docelowa generowała klasy w aplikacji, a ja odnosiłem się do com.happy.app

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

Dodałem pakiety (nie foldery) dla com.happy.app i przeniosłem pliki z folderów do pakietów w eclipse i rozwiązałem problem.

Roy
źródło
0

Wyczyść swój serwer. Może usuń serwer i dodaj projekt jeszcze raz i uruchom.

  1. Zatrzymaj serwer Tomcat

  2. Kliknij serwer prawym przyciskiem myszy i wybierz „Wyczyść”

  3. Ponownie kliknij serwer prawym przyciskiem myszy i wybierz „Wyczyść katalog roboczy Tomcat”

2rahulsk
źródło
0

W moim przypadku bawiłem się importem pomocniczych plików konfiguracyjnych java do głównego pliku konfiguracyjnego java. Podczas tworzenia pomocniczych plików konfiguracyjnych zmieniłem nazwę głównej klasy konfiguracyjnej, ale nie udało mi się zaktualizować nazwy w web.xml. Tak więc za każdym razem, gdy restartowałem serwer Tomcat, nie widziałem programów obsługi mapowania odnotowanych w konsoli Eclipse IDE, a kiedy próbowałem przejść do mojej strony głównej, widziałem ten błąd:

1 listopada 2019 11:00:01 org.springframework.web.servlet.PageNotFound noHandlerFound OSTRZEŻENIE: Nie znaleziono mapowania dla żądania HTTP z URI [/ webapp / home / index] w DispatcherServlet o nazwie „dispatcher”

Poprawka polegała na zaktualizowaniu pliku web.xml tak, aby stara nazwa „WebConfig” była zamiast „MainConfig”, po prostu zmieniając jej nazwę tak, aby odzwierciedlała najnowszą nazwę głównego pliku konfiguracyjnego java (gdzie „MainConfig” jest dowolna, a słowa „ Użyte w tym miejscu słowo „Web” i „Main” nie jest wymaganiem dotyczącym składni). MainConfig był ważny, ponieważ był to plik, który skanował komponent w poszukiwaniu „WebController”, mojej wiosennej klasy kontrolera mvc, która obsługuje moje żądania WWW.

@ComponentScan(basePackageClasses={WebController.class})

web.xml miał to:

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

Plik web.xml ma teraz:

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

Teraz widzę mapowanie w oknie konsoli:

INFO: zmapowano „{[/ home / index], methods = [GET]}” na public org.springframework.web.servlet.ModelAndView com.lionheart.fourthed.controller.WebController.gotoIndex ()

Moja strona internetowa ponownie się ładuje.

Steve T.
źródło
-1

Miałem ten sam problem co **No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName**

Po analizowaniu przez 2 do 4 dni odkryłem pierwotną przyczynę. Pliki klas nie zostały wygenerowane po uruchomieniu projektu. Kliknąłem zakładkę projektu.

Projekt -> Zamknij projekt -> OpenProject -> Wyczyść -> Kompiluj projekt

Wygenerowano pliki klas dla kodu źródłowego. To rozwiązało mój problem. Aby sprawdzić, czy pliki klas zostały wygenerowane, czy nie, sprawdź folder Build w folderze projektu.

Thanis Albert
źródło