Почему Spring MVC отвечает 404 и сообщает «Не найдено сопоставлений для HTTP-запроса с URI […] в DispatcherServlet»?

91

Я пишу приложение 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?


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

Сотириос Делиманолис
источник

Ответы:

100

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

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

Возможно, наиболее важные компоненты типа HandlerMappingmap

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

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

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

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

и либо выдает, NoHandlerFoundExceptionлибо немедленно фиксирует ответ с кодом состояния 404 Not Found.

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

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

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

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

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

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

от URL до beans с именами, начинающимися с косой черты ("/")

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

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

Отладка

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, чтобы увидеть, какие компоненты Spring регистрируются. Он должен сообщать, какие аннотированные классы он находит, какие пакеты сканирует и какие компоненты инициализирует. Если те, которые вы ожидали, отсутствуют, проверьте свою ApplicationContextконфигурацию.

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

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

Имея это в виду, распространенной ошибкой является регистрация DispatcherServletобъекта с сопоставлением URL-адресов /*, возвращение имени представления из @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с именем «диспетчером»

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

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

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

Сотириос Делиманолис
источник
С @EnableWebMvc диспетчерServlet уже зарегистрирован в /. «можно ожидать, что запрос будет перенаправлен на ресурс JSP по пути /WEB-INF/jsps/example-view-name.jsp. Этого не произойдет». Как заставить его работать так, чтобы он перенаправлялся на ресурс JSP по этому пути? Это в основном вопрос.
Tor
@Tor Сам @EnableWebMvcпо себе в @Configurationаннотированном классе этого не делает. Все, что он делает, - это добавляет в контекст приложения ряд бинов обработчиков / адаптеров Spring MVC по умолчанию. Регистрация DispatcherServletдля обслуживания /- это совершенно отдельный процесс, который выполняется несколькими способами, которые я описываю в разделе « Другие распространенные ошибки ». Я отвечаю на вопрос, заданный двумя абзацами ниже того, что вы цитировали.
Сотириос Делиманолис
5

Я решил свою проблему, когда в дополнение к описанному ранее:

@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

RoutesMaps.com
источник
2

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

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

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

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

Акапулько
источник
1

Попробуйте внести в код следующие изменения в файл конфигурации. Конфигурация Java используется вместо application.properties. Не забудьте включить конфигурацию в configureDefaultServletHandlingметоде.

WebMvcConfigurerAdapterclass устарел, поэтому мы используем WebMvcConfigurerинтерфейс.

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

Я использую gradle, у вас должны быть следующие зависимости 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

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

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

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

Анил Н.П.
источник
0

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

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

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

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

Рой
источник
0

Очистите свой сервер. Возможно, удалите сервер и снова добавьте проект и запустите.

  1. Остановите сервер Tomcat

  2. Щелкните сервер правой кнопкой мыши и выберите «Очистить».

  3. Снова щелкните сервер правой кнопкой мыши и выберите «Очистить рабочий каталог Tomcat»

2рагульск
источник
0

В моем случае я играл с импортом вторичных файлов конфигурации java в основной файл конфигурации java. При создании дополнительных файлов конфигурации я изменил имя основного класса конфигурации, но мне не удалось обновить имя в web.xml. Итак, каждый раз, когда я перезапускал свой сервер tomcat, я не видел обработчиков сопоставления, отмеченных в консоли Eclipse IDE, и когда я пытался перейти на свою домашнюю страницу, я видел эту ошибку:

1 ноября 2019 г., 23:00:01 org.springframework.web.servlet.PageNotFound noHandlerFound ПРЕДУПРЕЖДЕНИЕ: не найдено сопоставление для HTTP-запроса с URI [/ webapp / home / index] в DispatcherServlet с именем 'dispatcher'

Исправление заключалось в обновлении файла web.xml так, чтобы старое имя «WebConfig» было вместо «MainConfig», просто переименовав его, чтобы отразить последнее имя основного файла конфигурации java (где «MainConfig» произвольно, а слова « Используемые здесь Web и Main не являются синтаксическими требованиями). MainConfig был важен, потому что это был файл, в котором компонент сканировал "WebController", мой класс весеннего контроллера mvc, который обрабатывает мои веб-запросы.

@ComponentScan(basePackageClasses={WebController.class})

web.xml имел это:

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

В файле web.xml теперь есть:

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

Теперь я вижу отображение в окне консоли:

ИНФОРМАЦИЯ: "{[/ home / index], methods = [GET]}" сопоставлен с общедоступным org.springframework.web.servlet.ModelAndView com.lionheart.fourthed.controller.WebController.gotoIndex ()

И моя веб-страница снова загружается.

Стив Т
источник
-1

У меня была такая же проблема как **No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName**

После того, как я проанализировал от 2 до 4 дней, я выяснил первопричину. Файлы классов не были созданы после запуска проекта. Я щелкнул вкладку проекта.

Проект -> CloseProject -> OpenProject -> Очистить -> Создать проект

Были созданы файлы классов для исходного кода. Это решило мою проблему. Чтобы проверить, были ли сгенерированы файлы классов, проверьте папку Build в папке вашего проекта.

Танис Альберт
источник