Почему весной MVC отвечает 404 и сообщает "Не найдено отображения для HTTP-запроса с URI [...]в DispatcherServlet".

java spring spring-mvc servlets


Я пишу приложение Spring MVC, развернутое на Tomcat. Смотрите следующий минимальный, полный и проверяемый пример

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

Где 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;
    }
}

Наконец, у меня есть @Controller в пакете com.example.controllers

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

Контекстное имя моего приложения - Example . Когда я отправляю запрос

http://localhost:8080/Example/home

приложение отвечает статусом HTTP 404 и записывает в журнал следующее

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'

У меня есть ресурс JSP по адресу /WEB-INF/jsps/index.jsp . Я ожидал, что Spring MVC будет использовать мой контроллер для обработки запроса и пересылки в JSP, так почему он отвечает 404?


Это должен быть канонический пост для вопросов об этом предупреждающем сообщении.




Answer 1 Sotirios Delimanolis


Ваше стандартное приложение Spring MVC будет обслуживать все запросы через DispatcherServlet , который вы зарегистрировали в своем контейнере сервлетов.

В DispatcherServlet смотрит на его ApplicationContext и, если имеется, ApplicationContext регистрировался ContextLoaderListener для специальной фасоли ему необходимо настроить его запрос обслуживания логики. Эти бобы описаны в документации .

Пожалуй, самое важное, HandlerMapping типа HandlerMapping map

входящие запросы к обработчикам и список пре- и постпроцессоров (перехватчиков обработчиков) на основе некоторых критериев, детали которых различаются в зависимости от реализации HandlerMapping . Самая популярная реализация поддерживает аннотированные контроллеры, но существуют и другие реализации.

Javadoc из HandlerMapping далее описывает , как должны вести себя реализации.

DispatcherServlet находит все бобы этого типа и регистрирует их в определенном порядке (можно настроить). Служа запрос, DispatcherServlet перебирает эти HandlerMapping объектов и проверяет каждый из них с getHandler , чтобы найти тот , который может обрабатывать входящий запрос, представленный в качестве стандарта HttpServletRequest . Начиная с 4.3.x, если он не находит , он записывает предупреждение, которое вы видите

Не найдено сопоставление для HTTP-запроса с URI [/some/path] в DispatcherServlet с именем SomeName

и либо бросает NoHandlerFoundException или сразу совершает ответ с 404 не найден кодом статуса.

Почему DispatcherServlet не нашел HandlerMapping , который мог бы обработать мой запрос?

Наиболее распространенное HandlerMapping реализация RequestMappingHandlerMapping , что ручки регистрации @Controller бобы в качестве обработчиков ( на самом деле их @RequestMapping аннотированных методов). Вы можете либо объявить bean-компонент этого типа самостоятельно (с помощью @Bean или <bean> или другого механизма), либо использовать встроенные опции . Эти:

  1. Аннотировать @Configuration класс с @EnableWebMvc .
  2. Объявите элемент <mvc:annotation-driven /> в вашей конфигурации XML.

Как описано выше по ссылке, оба из них зарегистрируют bean-компонент RequestMappingHandlerMapping (и кучу других вещей). Однако HandlerMapping не очень полезен без обработчика. RequestMappingHandlerMapping ожидает некоторые @Controller компоненты @Controller, поэтому вам необходимо также объявить их с помощью методов @Bean в конфигурации Java или объявлений <bean> в конфигурации XML или путем сканирования @Controller аннотированных классов @Controller в обоих. Убедитесь, что эти бобы присутствуют.

Если вы получаете предупреждающее сообщение и номер 404 и правильно настроили все вышеперечисленное, то вы отправляете запрос на неправильный URI , который не обрабатывается обнаруженным аннотированным методом обработчика @RequestMapping .

Библиотека spring-webmvc предлагает другие встроенные реализации HandlerMapping . Например, BeanNameUrlHandlerMapping maps

от URL-адресов до бобов с именами,начинающимися со слеша ("").

и вы всегда можете написать свой собственный. Очевидно, вы должны убедиться , что запрос вы посылаете матчи по крайней мере одной из зарегистрированных HandlerMapping обработчиков объекта.

Если вы не явно или неявно регистрировать любые HandlerMapping бобов (или если detectAllHandlerMappings это true ), то DispatcherServlet регистрирует некоторые значения по умолчанию . Они определены в DispatcherServlet.properties в том же пакете, что и класс DispatcherServlet . Это BeanNameUrlHandlerMapping и DefaultAnnotationHandlerMapping (что аналогично RequestMappingHandlerMapping , но не рекомендуется).

Debugging

Spring MVC будет регистрировать обработчики, зарегистрированные через RequestMappingHandlerMapping . Например, @Controller , как

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

будет регистрировать на уровне ИНФО следующие данные

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

Это описывает зарегистрированное отображение. Когда вы увидите предупреждение о том, что обработчик не найден, сравните URI в сообщении с отображением, указанным здесь. Все ограничения, указанные в @RequestMapping , должны соответствовать Spring MVC для выбора обработчика.

Другие реализации HandlerMapping регистрируют свои собственные операторы, которые должны указывать на их отображения и соответствующие обработчики.

Аналогично, включите ведение журнала Spring на уровне DEBUG, чтобы увидеть, какие bean-компоненты регистрируются Spring. Он должен сообщить, какие аннотированные классы он находит, какие пакеты он сканирует и какие компоненты он инициализирует. Если ожидаемых вами нет, просмотрите конфигурацию ApplicationContext .

Другие распространенные ошибки

DispatcherServlet просто типичный Java EE Servlet . Вы регистрируете его в своем типичном объявлении <web.xml> <servlet-class> и <servlet-mapping> , либо напрямую через ServletContext#addServlet в WebApplicationInitializer , либо с помощью любого другого механизма, используемого Spring boot. Таким образом, вы должны полагаться на логику отображения URL, указанную в спецификации сервлета , см. Главу 12. См. Также

Учитывая это, распространенной ошибкой является регистрация DispatcherServlet с отображением URL- @RequestMapping /* , возвращением имени представления из метода- обработчика @RequestMapping и ожиданием визуализации JSP. Например, рассмотрим метод-обработчик, такой как

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

с помощью InternalResourceViewResolver

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

вы можете ожидать, что запрос будет перенаправлен на ресурс JSP по пути /WEB-INF/jsps/example-view-name.jsp . Этого не произойдет. Вместо этого, принимая имя контекста Example , DisaptcherServlet сообщит

Не найдено сопоставление для HTTP-запроса с URI [/Example/WEB-INF/jsps/example-view-name.jsp] в DispatcherServlet с именем dispatcher.

Поскольку DispatcherServlet сопоставлен с /* и /* соответствует всему (кроме точных совпадений, которые имеют более высокий приоритет), DispatcherServlet будет выбран для обработки forward из JstlView (возвращаемого InternalResourceViewResolver ). Почти в каждом случае DispatcherServlet не будет настроен для обработки такого запроса .

Вместо этого в этом упрощенном случае вы должны зарегистрировать DispatcherServlet в / , отметив его как сервлет по умолчанию. Сервлет по умолчанию - это последнее совпадение для запроса. Это позволит вашему типичному сервлет-контейнеру выбрать внутреннюю реализацию сервлета, сопоставленную с *.jsp , для обработки ресурса JSP (например, Tomcat имеет JspServlet ), прежде чем пытаться использовать сервлет по умолчанию.

Это то,что ты видишь в своем примере.




Answer 2 RoutesMaps.com


Я решил свой вопрос,когда в дополнение к описанному выше:`

@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 не отображается в веб-приложении Spring Boot




Answer 3 Acapulco


В моем случае я следовал документации Interceptors Spring для версии 5.1.2 (при использовании Spring Boot v2.0.4.RELEASE ), а класс WebConfig содержал аннотацию @EnableWebMvc , которая в моем приложении противоречила чему-то другому, что было предотвращение правильного разрешения моих статических ресурсов (т. е. никакие файлы CSS или JS не возвращались клиенту).

После попытки много разных вещей, я попытался удаление с @EnableWebMvc и это сработало!

Изменить: Вот справочная документация, которая говорит, что вы должны удалить аннотацию @EnableWebMvc

По-видимому, в моем случае, по крайней мере, я уже настраиваю свое приложение Spring (хотя не с использованием web.xml или любого другого статического файла, это определенно программно), так что это был конфликт там.




Answer 4 Anil N.P


Я наткнулся на другую причину той же ошибки.Это также может быть из-за файлов классов,не сгенерированных для вашего файла controller.java.В результате чего сервлет-диспетчер,упомянутый в web.xml,не может сопоставить его с соответствующим методом в классе контроллера.

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

В eclipse в Project-> выберите clean -> Build Project. Проверьте, был ли создан файл класса для файла контроллера при сборках в вашей рабочей области.




Answer 5 Roy


Для меня,я обнаружил,что мои целевые классы были сгенерированы в шаблоне папок,не совпадающем с исходным.Возможно,в eclipse я добавляю папки,содержащие мои контроллеры,а не добавляю их в качестве пакетов.Так что в конце концов я определил неверный путь в конфигурационном файле весной.

Мой целевой класс генерировал классы под приложением,и я ссылался на com.happy.app.

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

Я добавил пакеты (не папки)для com.happy.app и переместил файлы из папок в пакеты в eclipse,и это решило проблему.