2017-01-10 51 views
51

Piszę aplikację Spring MVC, wdrożoną na Tomcat. Zobacz następujące minimal, complete, and verifiable example:Dlaczego Spring MVC odpowiada za pomocą 404 i zgłasza "Brak odwzorowania dla żądania HTTP z URI [...] w DispatcherServlet"?

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 SpringServletConfig jest

@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 @Controller w pakiecie com.example.controllers

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

nazwa kontekstu Moja aplikacja jest Example. Kiedy wysłać żądanie do

http://localhost:8080/Example/home 

aplikacja reaguje ze stanu HTTP 404 i rejestruje następujące

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 zasobem JSP w /WEB-INF/jsps/index.jsp Spodziewałem wiosny MVC do korzystania z mojego kontrolera do obsługi zażądać i przesłać do strony JSP, dlaczego więc odpowiada na pytanie 404?


To ma być kanoniczna postu na pytania dotyczące tego komunikatu ostrzegawczego.

Odpowiedz

48

Twoja standardowa aplikacja Spring MVC będzie obsługiwać wszystkie żądania poprzez numer DispatcherServlet, który został zarejestrowany w kontenerze Serwlet.

DispatcherServlet patrzy na jego ApplicationContext i, jeśli są dostępne, w ApplicationContext zarejestrowany z ContextLoaderListener specjalnego fasola potrzebuje skonfigurować swój wniosek serwującą logiki. These beans are described in the documentation.

zapewne najważniejszym, fasola typu HandlerMapping mapy

przychodzące żądania do ładowarki oraz wykaz pre- i post-procesorów (przechwytujących obsługę) w oparciu o pewne kryteria szczegóły, które różnią się od Implementacja HandlerMapping. Najpopularniejsza implementacja obsługuje kontrolery z adnotacjami, ale inne implementacje istnieją również dobrze jako .

W dalszej części opisu opisano sposób implementacji.

Urządzenie DispatcherServlet wyszukuje wszystkie ziarna tego typu i rejestruje je w dowolnej kolejności (można dostosować). Podczas obsługi żądania, DispatcherServlet wykonuje pętle za pośrednictwem tych obiektów HandlerMapping i testuje je za pomocą getHandler, aby znaleźć taki, który może obsłużyć przychodzące żądanie, reprezentowane jako standard HttpServletRequest. Począwszy od 4.3.x, jeśli nie znajdzie żadnego, to logs the warning że widać

Nie znaleziono mapowanie żądania HTTP z URI [/some/path] w DispatcherServlet o nazwie somename

i either rzuca NoHandlerFoundException lub bezpośrednio zatwierdza odpowiedź z kodem statusu 404 Not Found.

Dlaczego DispatcherServlet nie znalazł HandlerMapping, który mógłby obsłużyć moją prośbę?

Najczęstszym HandlerMapping realizacja jest RequestMappingHandlerMapping, który obsługuje rejestracji @Controller fasolę jako ładowarki (naprawdę ich @RequestMapping adnotacją metody). Możesz zadeklarować tego typu fasolę (z @Bean lub <bean> lub innym mechanizmem) lub możesz użyć the built-in options. Są to:

  1. Opisz swoją klasę @Configuration za pomocą @EnableWebMvc.
  2. Zadeklaruj element <mvc:annotation-driven /> w konfiguracji XML.

Jak opisuje powyższy link, obie z nich zarejestrują fasolę RequestMappingHandlerMapping (i kilka innych rzeczy). Jednak HandlerMapping nie jest zbyt przydatny bez obsługi. RequestMappingHandlerMapping oczekuje niektórych fasoli @Controller, więc należy je zadeklarować również za pomocą metod @Bean w konfiguracji Java lub deklaracji <bean> w konfiguracji XML lub poprzez skanowanie komponentów w klasach z przypisami w @Controller. Upewnij się, że te ziarna są obecne.

Jeśli otrzymujesz komunikat ostrzegawczy i 404 i skonfigurowaniu wszystkich powyższych poprawnie, następnie wysyłasz prośbę o niewłaściwym URI taki, który nie jest obsługiwany przez wykrytą @RequestMapping Metoda obsługi adnotowanych.

Biblioteka spring-webmvc oferuje inne wbudowane implementacje HandlerMapping. Na przykład, BeanNameUrlHandlerMapping odwzorowuje

z adresów URL do ziaren o nazwach zaczynających się od ukośnika („/”)

i zawsze można napisać własne. Oczywiście, musisz upewnić się, że wysłane zapytanie pasuje do co najmniej jednego z zarejestrowanych obiektów obiektu HandlerMapping.

nie Jeśli nie jawnie lub niejawnie zarejestrować żadnych HandlerMapping fasoli (lub jeśli detectAllHandlerMappings jest true), The DispatcherServlet rejestrów niektóre defaults. Są one zdefiniowane w DispatcherServlet.properties w tym samym pakiecie co klasa DispatcherServlet. Są to: BeanNameUrlHandlerMapping i DefaultAnnotationHandlerMapping (które są podobne do RequestMappingHandlerMapping, ale są przestarzałe).

Debugowanie

Wiosna MVC będzie koparki zarejestrowane przez RequestMappingHandlerMapping zalogować. Na przykład, @Controller jak

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

będzie rejestrować następujące INFO na poziomie

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

ten opisuje odwzorowanie zarejestrowany. Po wyświetleniu ostrzeżenia o tym, że nie znaleziono programu obsługi, porównaj identyfikator URI w komunikacie z wymienionym tutaj mapowaniem. Wszystkie ograniczenia określone w @RequestMapping muszą pasować do Spring MVC, aby wybrać handler.

Inne implementacje HandlerMapping rejestrują własne instrukcje, które powinny wskazywać na ich odwzorowania i odpowiednie programy obsługi.

W podobny sposób włącz rejestrację sprężyny na poziomie DEBUG, aby zobaczyć, które ziarna rejestruje sprężyna. Powinien on raportować, które z adnotowanych klas znajduje, które pakiety skanuje i które z nich inicjuje. Jeśli te, których oczekiwałeś, nie są obecne, przejrzyj konfigurację ApplicationContext.

Inne częste błędy

DispatcherServlet jest po prostu typowy Java EE Servlet. Zarejestrować go z typowej deklaracji <web.xml><servlet-class> i <servlet-mapping> lub bezpośrednio przez ServletContext#addServlet w WebApplicationInitializer lub z dowolnym mechanizmem Spring boot używa. Jako takie, trzeba polegać na url odwzorowania logiką określoną w Servlet specification Rozdział 12. Patrz także

Mając to na uwadze, częstym błędem jest, aby zarejestrować DispatcherServlet z mapowaniem adresów URL pod numerem /*, zwracając nazwę widoku z metody obsługi obsługi @RequestMapping i oczekując na renderowanie strony JSP. Na przykład, rozważmy metodę uchwytu 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 się spodziewać prośba być forwarded do zasobu JSP na ścieżce /WEB-INF/jsps/example-view-name.jsp. To się nie stanie. Zamiast tego, przyjmując nazwę kontekstu Example The DisaptcherServlet zgłosi

Nie mapowanie znalezionych dla żądania HTTP z URI [/Example/WEB-INF/jsps/example-view-name.jsp] w DispatcherServlet o nazwie 'dyspozytora'

Ponieważ DispatcherServlet jest odwzorowywany /* i /* dopasowuje wszystko (oprócz dokładnych dopasowań, które mają wyższy priorytet), forward zostanie wybrany do obsługi forward z JstlView (zwrócony przez InternalResourceViewResolver).W prawie każdym przypadku DispatcherServlet nie zostanie skonfigurowany do obsługi takich żądań.

Zamiast tego w tym uproszczonym przypadku należy zarejestrować DispatcherServlet na /, oznaczając go jako domyślny serwlet. Domyślny serwlet to ostatnie dopasowanie dla żądania. Pozwoli to typowemu kontenerowi serwletu wybrać wewnętrzną implementację serwletu, zmapowaną na *.jsp, aby obsłużyć zasoby JSP (na przykład Tomcat ma JspServlet), zanim spróbujesz z domyślnym serwletem.

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

+0

Jak można "zarejestrować DispatcherServlet do /, znakowanie to jest domyślny serwlet. " ? Czy możesz podzielić się tym fragmentem kodu? –

+0

@SaifMasadeh Jeśli spojrzysz na link do specyfikacji serwletu w odpowiedzi (lub wpisie SO poniżej), zobaczysz, że '/' jest specjalnym mapowaniem dla kontenerów serwletów, wskazującym "domyślny" serwlet, który zostanie wybrany jeśli nie zostaną dopasowane żadne inne odwzorowania. –

+0

@SotiriosDelimanolis Czy możesz rzucić okiem na repozytorium github, które utworzyłem https://github.com/saifmasadeh/WeatherBoard/blob/master/src/main/java/com/demo/controller/RootController.java? –

0

Natknąłem się na inny powód tego samego błędu. Może to również wynikać z plików klas, które nie zostały wygenerowane dla pliku controller.java. W wyniku tego serwlet dyspozytorski wymieniony w web.xml nie jest w stanie zmapować go do odpowiedniej metody w klasie kontrolera.

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

w Eclipse pod projektu-> wybierz czyste -> Budowanie Project.Do dać sprawdzić, czy plik został wygenerowany klasy dla pliku sterownik pod buduje w swojej pracy.

2

postanowiłem mój problem, gdy oprócz opisanych 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> 

` od: JSP file not rendering in Spring Boot web application