首页 文章

为什么Spring MVC用404响应并报告“在DispatcherServlet中找不到带有URI [...]的HTTP请求的映射”?

提问于
浏览
70

我正在编写一个部署在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;
    }
}

最后,我在包中有一个 @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'

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


对于有关此警告消息的问题,这是一个规范的帖子 .

6 回答

  • 2

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

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

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

    对处理程序的传入请求以及基于某些标准的预处理器和后处理器列表(处理程序拦截器),其详细信息因HandlerMapping实现而异 . 最流行的实现支持带注释的控制器,但也存在其他实现 .

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

    DispatcherServlet 找到此类型的所有bean并按某种顺序注册它们(可以自定义) . 在提供请求时, DispatcherServlet 循环遍历这些 HandlerMapping 对象,并使用getHandler测试每个对象,找到一个可以处理传入请求的对象,表示为标准 HttpServletRequest . 从4.3.x, if it doesn't find any ,你看到它logs the warning

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

    并且either抛出NoHandlerFoundException或立即使用404 Not Found状态代码提交响应 .

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

    最常见的 HandlerMapping 实现是RequestMappingHandlerMapping,它处理 @Controller bean作为处理程序(实际上是 @RequestMapping 注释方法) . 您可以自己声明此类型的bean(使用 @Bean<bean> 或其他机制),也可以使用the built-in options . 这些是:

    • 使用 @EnableWebMvc 注释 @Configuration 类 .

    • 在XML配置中声明 <mvc:annotation-driven /> 成员 .

    正如上面的链接描述的那样,这两个都将注册一个 RequestMappingHandlerMapping bean(和其他一些东西) . 但是,没有处理程序, HandlerMapping 不是很有用 . RequestMappingHandlerMapping 需要一些 @Controller bean,因此您需要通过Java配置中的 @Bean 方法或XML配置中的 <bean> 声明或通过组件扫描 @Controller 注释类来声明这些bean . Make sure these beans are present.

    如果're getting the warning message and a 404 and you'已正确配置了上述所有内容, then you're sending your request to the wrong URI ,未被检测到的 @RequestMapping 带注释的处理程序方法处理的那个 .

    spring-webmvc 库提供其他内置的 HandlerMapping 实现 . 例如,BeanNameUrlHandlerMapping Map

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

    而你总是可以自己写 . 显然, you'll have to make sure the request you're sending matches at least one of the registered HandlerMapping object's handlers.

    如果您没有隐式或显式注册任何 HandlerMapping bean(或者如果detectAllHandlerMappingstrue ),则 DispatcherServlet 会注册defaults . 这些在DispatcherServlet.properties中定义在与 DispatcherServlet 类相同的包中 . 它们是 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 . 您可以使用典型的 <web.xml> <servlet-class><servlet-mapping> 声明注册它,或者直接通过WebApplicationInitializer中的ServletContext#addServlet注册它,或者使用Spring引导使用的任何机制注册它 . 因此,您必须依赖Servlet specification中指定的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资源forwarded . 这不会发生 . 相反,假设上下文名称为 ExampleDisaptcherServlet 将报告

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

    因为 DispatcherServlet 映射到 /* 并且 /* 匹配所有内容(具有更高优先级的完全匹配除外),所以将选择 DispatcherServlet 来处理来自JstlViewforward (由 InternalResourceViewResolver 返回) . In almost every case, the DispatcherServlet will not be configured to handle such a request .

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

    这就是你在你的例子中看到的 .

  • 0

    除了之前描述之外,我解决了我的问题:`

    @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 file not rendering in Spring Boot web application

  • 76

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

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

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

  • 0

    在我的情况下,我跟随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 或任何其他静态文件,但它绝对是以编程方式),所以这是一个冲突 .

  • 0

    对我来说,我发现我的目标类是在与源不同的文件夹模式中生成的 . 这可能是在eclipse中我添加用于包含我的控制器的文件夹而不是将它们添加为包 . 所以我最终在spring配置中定义了错误的路径 .

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

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

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

  • -1

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

    • 停止Tomcat服务器

    • 右键单击服务器并选择“清除”

    • 再次右键单击服务器并选择“清除Tomcat工作目录”

相关问题