为啥 Spring MVC 以 404 响应并报告“No mapping found for HTTP request with URI [...] in DispatcherServlet”?

Posted

技术标签:

【中文标题】为啥 Spring MVC 以 404 响应并报告“No mapping found for HTTP request with URI [...] in DispatcherServlet”?【英文标题】:Why does Spring MVC respond with a 404 and report "No mapping found for HTTP request with URI [...] in DispatcherServlet"?为什么 Spring MVC 以 404 响应并报告“No mapping found for HTTP request with URI [...] in DispatcherServlet”? 【发布时间】:2017-05-25 10:31:41 【问题描述】:

我正在编写一个部署在 Tomcat 上的 Spring MVC 应用程序。见下文minimal, complete, and verifiable example

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;
    

最后,我在包com.example.controllers中有一个@Controller

@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'

我在/WEB-INF/jsps/index.jsp 有一个 JSP 资源我希望 Spring MVC 使用我的控制器来处理请求并转发到 JSP,那么为什么它会以 404 响应?


这是一篇关于此警告消息问题的规范帖子。

【问题讨论】:

【参考方案1】:

您的标准 Spring MVC 应用程序将通过您在 Servlet 容器中注册的 DispatcherServlet 处理所有请求。

DispatcherServlet 查看它的ApplicationContext,如果可用,ApplicationContext 注册了一个ContextLoaderListener,用于设置其请求服务逻辑的特殊bean。 These beans are described in the documentation.

可以说是最重要的,HandlerMapping map 类型的 bean

对处理程序的传入请求以及前处理程序和后处理程序的列表 (处理程序拦截器)基于一些标准,其中的细节 因HandlerMapping 实施而异。最流行的实现 支持带注释的控制器,但其他实现存在为 好吧。

javadoc of HandlerMapping 进一步描述了实现必须如何表现。

DispatcherServlet 查找所有这种类型的 bean 并以某种顺序注册它们(可以自定义)。在处理请求时,DispatcherServlet 循环遍历这些 HandlerMapping 对象并使用 getHandler 测试每个对象,以找到可以处理传入请求的对象,表示为标准 HttpServletRequest。从 4.3.x 开始,如果没有找到,您会看到 logs the warning

在名称为 SomeName 的 DispatcherServlet 中找不到具有 URI [/some/path] 的 HTTP 请求的映射

并且either 抛出NoHandlerFoundException 或立即提交带有 404 Not Found 状态代码的响应。

为什么DispatcherServlet 没有找到可以处理我的请求的HandlerMapping

最常见的HandlerMapping 实现是RequestMappingHandlerMapping,它将@Controller bean 注册为处理程序(实际上是它们的@RequestMapping 注释方法)。您可以自己声明这种类型的 bean(使用 @Bean&lt;bean&gt; 或其他机制),也可以使用 the built-in options。它们是:

    使用 @EnableWebMvc 注释您的 @Configuration 类。 在您的 XML 配置中声明一个 &lt;mvc:annotation-driven /&gt; 成员。

正如上面的链接所描述的,这两个都将注册一个RequestMappingHandlerMapping bean(和一堆其他的东西)。但是,如果没有处理程序,HandlerMapping 并不是很有用。 RequestMappingHandlerMapping 需要一些 @Controller bean,因此您也需要通过 Java 配置中的 @Bean 方法或 XML 配置中的 &lt;bean&gt; 声明或通过对其中 @Controller 注释类的组件扫描来声明它们。 确保这些 bean 存在。

如果您收到警告消息和 404 并且您已正确配置上述所有内容,那么您将请求发送到错误的 URI,即未处理的通过检测到的@RequestMapping 注释处理程序方法。

spring-webmvc 库提供了其他内置的 HandlerMapping 实现。例如,BeanNameUrlHandlerMapping 地图

从 URL 到名称以斜杠 ("/") 开头的 bean

而且你总是可以自己写。显然,您必须确保您发送的请求至少与注册的HandlerMapping 对象的处理程序之一匹配。

如果您没有隐式或显式注册任何HandlerMapping bean(或者如果detectAllHandlerMappingstrue),则DispatcherServlet 会注册一些defaults。这些定义在与DispatcherServlet 类相同的包中的DispatcherServlet.properties 中。它们是BeanNameUrlHandlerMappingDefaultAnnotationHandlerMapping(类似于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 实现会记录它们自己的语句,这些语句应该提示它们的映射和对应的处理程序。

同样,在 DEBUG 级别启用 Spring 日志记录以查看 Spring 注册了哪些 bean。它应该报告它找到了哪些带注释的类,它扫描了哪些包,以及它初始化了哪些 bean。如果您预期的不存在,请检查您的 ApplicationContext 配置。

其他常见错误

DispatcherServlet 只是一个典型的 Java EE Servlet。您可以使用典型的 &lt;web.xml&gt; &lt;servlet-class&gt;&lt;servlet-mapping&gt; 声明注册它,或者直接通过 ServletContext#addServletWebApplicationInitializer 中注册它,或者使用 Spring boot 使用的任何机制。因此,您必须依赖Servlet specification 中指定的 url 映射 逻辑,请参阅第 12 章。另请参阅

How are Servlet url mappings in web.xml used?

考虑到这一点,一个常见的错误是使用/* 的url 映射注册DispatcherServlet,从@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;

您可能期望forwarded 对路径/WEB-INF/jsps/example-view-name.jsp 处的JSP 资源的请求。这不会发生。相反,假设上下文名称为 ExampleDisaptcherServlet 将报告

在名称为“dispatcher”的DispatcherServlet 中找不到具有URI [/Example/WEB-INF/jsps/example-view-name.jsp] 的HTTP 请求的映射

因为DispatcherServlet 映射到/* 并且/* 匹配所有内容(除了具有更高优先级的完全匹配),将选择DispatcherServlet 来处理来自JstlViewforward(由InternalResourceViewResolver 返回)。 几乎在所有情况下,DispatcherServlet 都不会配置为处理此类请求

相反,在这种简单的情况下,您应该将DispatcherServlet 注册到/,将其标记为默认servlet。默认 servlet 是请求的最后一个匹配项。这将允许您的典型 servlet 容器在尝试使用默认 servlet 之前选择一个映射到 *.jsp 的内部 Servlet 实现来处理 JSP 资源(例如,Tomcat 有 JspServlet)。

这就是您在示例中看到的内容。

【讨论】:

使用@EnableWebMvc,它已经注册到/的dispatcherServlet。 “您可能希望将请求转发到路径 /WEB-INF/jsps/example-view-name.jsp 处的 JSP 资源。这不会发生。”你如何使它工作,以便它转发到该路径上的 JSP 资源?这基本上是问的问题。 @Tor 就其自身而言,@Configuration 注释类上的@EnableWebMvc 不会这样做。它所做的只是将一些默认的 Spring MVC 处理程序/适配器 bean 添加到应用程序上下文中。注册DispatcherServlet 以服务/ 是一个完全独立的过程,可以通过我在其他常见错误部分中描述的多种方式完成。我在您引用的内容下方的两段中回答提出的问题【参考方案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>

` 来自:JSP file not rendering in Spring Boot web application

【讨论】:

【参考方案3】:

就我而言,我关注的是Interceptors Spring documentation for version 5.1.2(在使用Spring Boot v2.0.4.RELEASE时),WebConfig 类有注释@EnableWebMvc,这似乎是冲突的我的应用程序中有其他东西阻止了我的静态资产被正确解析(即没有 CSS 或 JS 文件被返回给客户端)。

在尝试了很多不同的事情之后,我尝试删除@EnableWebMvc,它成功了!

编辑:Here's the reference documentation 表示您应该删除 @EnableWebMvc 注释

显然,至少在我的情况下,我已经在配置我的 Spring 应用程序(尽管不是通过使用 web.xml 或任何其他静态文件,它肯定是通过编程方式),所以那里存在冲突。

【讨论】:

【参考方案4】:

尝试通过对配置文件进行以下更改来修改您的代码。使用 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'

【讨论】:

【参考方案5】:

我遇到了同样错误的另一个原因。这也可能是由于没有为您的 controller.java 文件生成的类文件。因此,web.xml 中提到的调度程序 servlet 无法将其映射到控制器类中的适当方法。

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

在 Eclipse 下的 Project->select clean ->Build Project.Do 检查是否已经为工作区中构建下的控制器文件生成了类文件。

【讨论】:

【参考方案6】:

对我来说,我发现我的目标类是在与源不同的文件夹模式中生成的。这可能是在 Eclipse 中,我添加了包含我的控制器的文件夹,而不是将它们添加为包。所以我最终在 spring config 中定义了不正确的路径。

我的目标类是在 app 下生成类,我指的是 com.happy.app

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

我为 com.happy.app 添加了包(不是文件夹),并将文件从文件夹移动到 Eclipse 中的包,它解决了这个问题。

【讨论】:

【参考方案7】:

清理您的服务器。也许删除服务器并再次添加项目并运行。

    停止 Tomcat 服务器

    右击服务器,选择“清理”

    再次右键服务器,选择“清理Tomcat工作目录”

【讨论】:

【参考方案8】:

就我而言,我正在尝试将辅助 java 配置文件导入到主 java 配置文件中。在制作辅助配置文件时,我更改了主配置类的名称,但未能更新 web.xml 中的名称。因此,每次我重新启动我的 tomcat 服务器时,我都没有在 Eclipse IDE 控制台中看到映射处理程序,当我尝试导航到我的主页时,我看到了这个错误:

2019 年 11 月 1 日晚上 11:00:01 org.springframework.web.servlet.PageNotFound noHandlerFound 警告:未找到带有 URI 的 HTTP 请求的映射 [/webapp/home/index] 在 DispatcherServlet 中,名称为 'dispatcher'

修复方法是更新 web.xml 文件,以便旧名称“WebConfig”改为“MainConfig”,只需重命名它以反映主 java 配置文件的最新名称(其中“MainConfig”是任意的,并且此处使用的单词“Web”和“Main”不是语法要求)。 MainConfig 很重要,因为它是对“WebController”进行组件扫描的文件,我的 spring mvc 控制器类处理我的 Web 请求。

@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>

现在我在控制台窗口中看到了映射:

INFO:将“[/home/index],methods=[GET]”映射到公共 org.springframework.web.servlet.ModelAndView com.lionheart.fourthed.controller.WebController.gotoIndex()

我的网页再次加载。

【讨论】:

【参考方案9】:

在我的例子中,我创建了 Config.java(类)和 config.xml,并且在它们中都部分完成了映射。并且由于 config.java 使用 @Configuration 注释,它被认为是优先级。并且没有考虑 config.xml。 如果有人遇到这样的麻烦,只需删除带有注释的 config.java 并尝试保留 config.xml 即可。

【讨论】:

【参考方案10】:

我和**No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName**有同样的问题

在我分析了 2 到 4 天后,我找到了根本原因。运行项目后未生成类文件。我单击了项目选项卡。

Project-->CloseProject-->OpenProject-->Clean-->构建项目

已生成源代码的类文件。它解决了我的问题。要检查类文件是否已经生成,请检查项目文件夹中的 Build 文件夹。

【讨论】:

【参考方案11】:

所以问题可以很简单,就像项目路径中的额外空间一样。确保路径中没有空间,我花了很长时间才解决。

【讨论】:

以上是关于为啥 Spring MVC 以 404 响应并报告“No mapping found for HTTP request with URI [...] in DispatcherServlet”?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Spring 文档并不完美 [关闭]

Spring-Boot MVC 模板未加载(未找到 404)

使用 Spring MVC 和 Spring Security 进行 404 注销

Spring MVC + Eclipse:HTTP 404 – 未找到

HTTP 状态 404 - /spring-mvc/login

由于 Spring MVC 中不存在 URL,验证表单提交按钮在第二次尝试后返回 404