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

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

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


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

4b9b3361

Ответ 1

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

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

Возможно, самое важное, бобы типа HandlerMapping map

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

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

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

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

и либо выдает NoHandlerFoundException, либо сразу же отправляет ответ с кодом состояния 404 "Не найден".

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

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

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

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

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

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

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

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

Если вы неявно или явно не регистрируете какие-либо bean-компоненты 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";
    }
}

будет регистрировать следующее на уровне INFO

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 и ожиданием визуализации 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 не будет настроен для обработки такого запроса.

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

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

Ответ 2

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

@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 загрузочном веб-приложении

Ответ 3

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

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

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

Ответ 6

Почистите свой сервер. Может быть, удалить сервер и добавить проект еще раз и запустить.

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

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

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