为什么Spring MVC以404响应并报告 "在DispatcherServlet中没有找到具有URI [...]的HTTP请求的映射"?

java spring spring-mvc servlets


我正在编写部署在Tomcat上的Spring MVC应用程序。请参阅以下最小,完整和可验证的示例

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响应?


这旨在成为有关此警告消息问题的规范帖子。





Answer 1 Sotirios Delimanolis


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

DispatcherServlet 的外观在它 ApplicationContext ,如果可用, ApplicationContext 中,注册 ContextLoaderListener 的特殊豆需要设置其请求服务的逻辑。这些bean在文档中进行了描述

可以说是最重要的,类型豆 HandlerMapping 地图

根据某些条件向处理程序传入的请求以及一系列预处理程序和后处理程序(处理程序拦截器),这些标准的细节因 HandlerMapping 实现而异。最流行的实现支持带注释的控制器,但也存在其他实现。

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

DispatcherServlet 的发现这种类型的所有豆类和以某种顺序将它们登记(可定制)。在处理请求时, DispatcherServlet 循环遍历这些 HandlerMapping 对象,并使用 getHandler 测试每个对象,以找到可以处理传入请求的对象,表示为标准 HttpServletRequest 。从4.3.x版本开始,如果找不到任何内容,它将记录您看到的警告

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

任一抛出一个 NoHandlerFoundException 或立即提交一个404 Not Found状态码的响应。

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

最常见的 HandlerMapping 实现是 RequestMappingHandlerMapping ,它将 @Controller bean 注册为处理程序(实际上是 @RequestMapping 注释方法)。您可以自己声明这种类型的bean(使用 @Bean<bean> 或其他机制),也可以使用内置选项。这些是:

  1. @EnableWebMvc 注释您的 @Configuration 类。
  2. 在您的XML配置中声明一个 <mvc:annotation-driven /> 成员。

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

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

spring-webmvc 库提供其他内置 HandlerMapping 实现。例如, BeanNameUrlHandlerMapping 映射

从URL到名称以斜线("")开头的豆子

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

如果您没有隐式或显式注册任何 HandlerMapping Bean(或者 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";
    }
}

将在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 。您注册它典型的 <web.xml> <servlet-class><servlet-mapping> 申报,也可以直接通过 ServletContext#addServlet WebApplicationInitializer ,或与任何机构弹簧开机使用。因此,您必须依赖Servlet规范中指定的url映射逻辑,请参阅第12章。另请参见

考虑到这一点,一个常见的错误是使用 /* 的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;
}

您可能希望将请求转发到路径 /WEB-INF/jsps/example-view-name.jsp 的JSP资源。这不会发生。相反,假设上下文名称为 Example ,则 DisaptcherServlet 将报告

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

因为 DispatcherServlet 的被映射到 /*/* 匹配一切(除了精确匹配,其具有更高的优先级),则 DispatcherServlet 的将被选择来处理 forward JstlView (由返回 InternalResourceViewResolver )。几乎在每种情况下, DispatcherServlet 都不会配置为处理此类请求

相反,在这种简单的情况下,应该将 DispatcherServlet 注册到 / ,将其标记为默认servlet。默认servlet是请求的最后一个匹配项。这将允许您的典型Servlet容器在尝试使用默认Servlet之前,选择一个内部Servlet实现(映射到 *.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>

来自:Spring Boot Web应用程序中未呈现的JSP文件




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中提到的dispatcher servlet无法将其映射到控制器类中的相应方法。

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

在Eclipse中的Project->选择clean-> Build Project下。检查工作区中builds下是否为控制器文件生成了类文件。




Answer 5 Roy


对我来说,我发现我的目标类是以文件夹模式生成的,和源码不一样。这可能是在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中将文件从文件夹中移动到包中,这个问题就解决了。