java - 为什么Spring MVC会以404响应并报告“在DispatcherServlet中未找到带有URI […]的HTTP请求的映射”?

标签 java spring spring-mvc servlets

我正在编写部署在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 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'


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



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

最佳答案

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

DispatcherServlet会查看其ApplicationContext,如果有的话,还会向ApplicationContext注册以查找特殊bean的ContextLoaderListener,它需要设置其请求服务逻辑。 These beans are described in the documentation

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


  向处理程序的传入请求以及预处理程序和后处理程序的列表
  (处理程序拦截器)基于某些标准
  因HandlerMapping实现而异。最受欢迎的实施
  支持带注释的控制器,但存在其他实现
  好。


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

DispatcherServlet查找所有此类型的bean,并按一定顺序注册它们(可以自定义)。在处理请求时,DispatcherServlet循环遍历这些HandlerMapping对象,并使用getHandler测试它们中的每个对象,以找到可以处理传入请求的对象,以标准HttpServletRequest表示。从4.3.x版本开始,如果找不到任何内容,则会显示logs the warning


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


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

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

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


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


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

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

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


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


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

如果您没有隐式或显式注册任何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>声明,或者直接通过ServletContext#addServlet中的WebApplicationInitializer或使用Spring boot使用的任何机制来注册它。因此,您必须依赖Servlet specification中指定的url映射逻辑,请参见第12章。另请参见


How are Servlet url mappings in web.xml used?


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


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


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


由于DispatcherServlet映射到/*并且/*匹配所有内容(精确匹配除外,它们具有更高的优先级),因此将选择DispatcherServlet来处理forward中的JstlView(由InternalResourceViewResolver)。在几乎每种情况下,DispatcherServlet都不会配置为处理此类请求。

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

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

关于java - 为什么Spring MVC会以404响应并报告“在DispatcherServlet中未找到带有URI […]的HTTP请求的映射”?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30119810/

相关文章:

Java - 在数组干草堆中查找针的一部分的索引

java - 使用 Java 中的 Web 服务检索 XML 数据

spring - docker 化的 postgres 和 docker 化的 Spring boot 应用程序

java - 多个项目共享同一个数据库(Spring JAVA)

java - Spring Web MVC Java 配置-默认 Servlet 名称

spring - 使用 @ComponentScan 或 <context :component-scan/> with only one class

java - 如何调用带有多个参数的spring API?

java - 如何在 X509 中编码专有的非对称公钥

java - 在 wiremock 中,是否可以根据请求记录 cookie?

java - 无法在 Spring Boot 中注册 Gson TypeAdapter