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

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

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

最佳答案
您的标准Spring MVC应用程序将通过您在Servlet容器中注册的DispatcherServlet提供所有请求.

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

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

incoming requests to handlers and a list of pre- and post-processors
(handler interceptors) based on some criteria the details of which
vary by HandlerMapping implementation. The most popular implementation
supports annotated controllers but other implementations exists as
well.

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

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

No mapping found for HTTP request with URI [/some/path] in DispatcherServlet with name SomeName

either抛出NoHandlerFoundException或立即提交404 Not Found状态代码的响应.

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

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

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

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

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

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

from URLs to beans with names that start with a slash (“/”)

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

如果您没有隐式或显式注册任何HandlerMapping bean(或者如果detectAllHandlerMappings为真),则DispatcherServlet会注册一些defaults.这些在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中指定的所有限制必须与Spring MVC匹配才能选择处理程序.

其他HandlerMapping实现记录它们自己的语句,这些语句应该提示它们的映射和相应的处理程序.

类似地,在DEBUG级别启用Spring日志记录以查看Spring注册的bean.它应该报告它找到哪些带注释的类,它扫描哪些包,以及它初始化的bean.如果您所期望的那些不存在,那么请查看您的ApplicationContext配置.

其他常见错误

DispatcherServlet只是一个典型的Java EE Servlet.您可以使用典型的< web.xml>注册它. < servlet的类>和< servlet-mapping>声明,或直接通过WebApplicationInitializer中的ServletContext#addServlet,或Spring引导使用的任何机制.因此,您必须依赖于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资源.这不会发生.相反,假设上下文名称为Example,DisaptcherServlet将报告

No mapping found for HTTP request with URI [/Example/WEB-INF/jsps/example-view-name.jsp] in DispatcherServlet with name ‘dispatcher’

因为DispatcherServlet映射到/ *和/ *匹配所有内容(具有更高优先级的完全匹配除外),所以将选择DispatcherServlet来处理JstlView中的转发(由InternalResourceViewResolver返回).几乎在所有情况下,DispatcherServlet都不会配置为处理这样​​的请求.

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

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

转载注明原文:java – 为什么Spring MVC用404响应并报告“在DispatcherServlet中找不到带有URI […]的HTTP请求的映射”? - 代码日志