Чому 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 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?


Це має бути канонічним дописом для питань щодо цього попереджувального повідомлення.

Відповіді:


100

Ваша стандартна програма Spring MVC буде обслуговувати всі запити через DispatcherServletте, що ви зареєстрували у своєму контейнері Servlet.

Переглядає DispatcherServletсвою ApplicationContextта, якщо вона доступна, ApplicationContextзареєстровану ContextLoaderListenerдля спеціального бобу, який йому потрібен для налаштування логіки обслуговування запитів. Ці боби описані в документації .

Мабуть, найважливіше, боби типу HandlerMappingкарти

вхідні запити до обробників та список попереднього та післяпроцесора (перехоплювачі обробників) на основі деяких критеріїв, деталі яких різняться залежно від 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>або іншим механізмом) , або ви можете використовувати вбудовані в налаштуваннях . Це:

  1. Анотуйте свої @Configuration клас за допомогою @EnableWebMvc.
  2. Заявіть a <mvc:annotation-driven /> учасника у вашій конфігурації XML.

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

Якщо ви отримуєте попереджувальне повідомлення та 404, і ви все правильно вказали все вище, тоді ви надсилаєте свій запит на неправильний URI , той, який не обробляється виявленим @RequestMappingанотованим методом обробника.

У spring-webmvcбібліотеці пропонує інші вбудовані HandlerMappingреалізації. Наприклад,BeanNameUrlHandlerMapping карти

від URL-адрес до компонентів з іменами, які починаються косою рисою ("/")

і ви завжди можете написати своє. Очевидно, вам доведеться переконатися, що запит, який ви надсилаєте, відповідає принаймні одному із зареєстрованих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";
    }
}

буде реєструвати наступне на рівні INFO

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

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

Інший HandlerMapping реалізації реєструють власні оператори, які повинні натякати на їх відображення та відповідні обробники.

Подібним чином увімкніть весняний журнал на рівні DEBUG, щоб побачити, які компоненти Spring реєструються. Він повинен повідомляти, які анотовані класи він знаходить, які пакети сканує та які компоненти ініціалізує. Якщо тих, яких ви очікували, немає, перегляньте свою ApplicationContextконфігурацію.

Інші поширені помилки

A DispatcherServlet- це лише типовий Java EE Servlet. Ви реєструєте його за допомогою типового тексту <web.xml> <servlet-class>та <servlet-mapping>декларації, або безпосередньо ServletContext#addServletв a 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назві 'диспетчер'

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

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

Це те, що ви бачите на своєму прикладі.


З @EnableWebMvc диспетчерServlet він вже зареєструвався в /. "Ви можете очікувати, що запит буде перенаправлений до ресурсу JSP за шляхом /WEB-INF/jsps/example-view-name.jsp. Цього не станеться." Як змусити це працювати так, щоб воно переходило до ресурсу JSP за цим шляхом? В основному це питання.
Tor

@Tor Сам по собі, @EnableWebMvcна @Configurationанотованому класі цього не робить. Все, що він робить, це додати ряд обробників / адаптерів Spring MVC за замовчуванням до контексту програми. Реєстрація DispatcherServletдля обслуговування /- це абсолютно окремий процес, який виконується багатьма способами, описаними у розділі Інші поширені помилки . Я відповідаю на запитання, задане на два абзаци нижче того, що ви цитували.
Sotirios Delimanolis

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>

`з: файл JSP не відображається у веб-програмі Spring Boot


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методі.

WebMvcConfigurerAdapterклас застарілий, тому ми використовуємо 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 у розділі Проект -> виберіть очистити -> Створити проект. Перевірте, чи не було створено файл класу для файлу контролера під збірками у вашій робочій області.


0

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

Мій цільовий клас генерував класи за програмою, і я посилався на 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"


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 з ім'ям 'диспетчер'

Виправлення полягало в тому, щоб оновити файл web.xml так, щоб старе ім'я "WebConfig" було замість "MainConfig", просто перейменовуючи його, щоб відображати останнє ім'я основного файлу конфігурації Java (де "MainConfig" є довільним, а слова " Веб "та" Основні ", що використовуються тут, не є вимогою синтаксису). 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 -> Clean -> Збір проекту

Створено файли класів для вихідного коду. Це вирішило мою проблему. Щоб перевірити, чи були створені файли класів чи ні, перевірте папку Build у папці проекту.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.