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

Sot*_*lis 82 java spring servlets spring-mvc

我正在编写一个部署在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[] { "/*" };
    }
}
Run Code Online (Sandbox Code Playgroud)

哪里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;
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,我有一个@Controllercom.example.controllers

@Controller
public class ExampleController {
    @RequestMapping(path = "/home", method = RequestMethod.GET)
    public String example() {
        return "index";
    }
}
Run Code Online (Sandbox Code Playgroud)

我的应用程序的上下文名称是Example.当我发送请求时

http://localhost:8080/Example/home
Run Code Online (Sandbox Code Playgroud)

应用程序以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'
Run Code Online (Sandbox Code Playgroud)

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


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

Sot*_*lis 88

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

DispatcherServlet在它的外观ApplicationContext,如果可用,ApplicationContext用注册ContextLoaderListener的,它需要建立其请求的服务逻辑特殊豆.这些bean在文档中描述.

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

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

的javadoc中HandlerMapping进一步描述了实现必须的行为.

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

没有找到映射与URI HTTP请求[/some/path]DispatcherServlet使用的名字SomeName

并且要么抛出一个NoHandlerFoundException或立即提交一个404 Not Found状态码的响应.

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

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

  1. 用你的@Configuration课程注释@EnableWebMvc.
  2. <mvc:annotation-driven />在XML配置中声明成员.

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

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

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

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

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

如果您没有隐式或显式注册任何HandlerMappingbean(或者如果detectAllHandlerMappingstrue),则DispatcherServlet寄存器会默认.这些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";
    }
}
Run Code Online (Sandbox Code Playgroud)

将在INFO级别记录以下内容

Mapped "{[/example],methods=[GET],headers=[X-Custom]}" onto public java.lang.String com.spring.servlet.ExampleController.example()
Run Code Online (Sandbox Code Playgroud)

这描述了已注册的映射.当您看到没有找到处理程序的警告时,请将消息中的URI与此处列出的映射进行比较.@RequestMapping必须匹配Spring MVC中指定的所有限制以选择处理程序.

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

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

其他常见错误

A DispatcherServlet只是一个典型的Java EE Servlet.你与你的典型进行注册<web.xml> <servlet-class><servlet-mapping>申报,或直接通过ServletContext#addServlet在一个WebApplicationInitializer或与任何机构弹簧开机使用.因此,您必须依赖Servlet规范中指定的url映射逻辑,请参阅第12章.另请参见

考虑到这一点,一个常见的错误是DispatcherServlet使用url映射注册/*,从@RequestMapping处理程序方法返回视图名称,并期望呈现JSP.例如,考虑一个像这样的处理程序方法

@RequestMapping(path = "/example", method = RequestMethod.GET)
public String example() {
    return "example-view-name";
}
Run Code Online (Sandbox Code Playgroud)

InternalResourceViewResolver

@Bean
public InternalResourceViewResolver resolver() {
    InternalResourceViewResolver vr = new InternalResourceViewResolver();
    vr.setPrefix("/WEB-INF/jsps/");
    vr.setSuffix(".jsp");
    return vr;
}
Run Code Online (Sandbox Code Playgroud)

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

未找到HTTP请求的URI与映射[/Example/WEB-INF/jsps/example-view-name.jsp]DispatcherServlet名为"调度"

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

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

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

  • @Tor 本身,“@Configuration”注释类上的“@EnableWebMvc”不会执行此操作。它所做的只是向应用程序上下文添加一些默认的 Spring MVC 处理程序/适配器 bean。注册“DispatcherServlet”来服务“/”是一个完全独立的过程,可以通过我在 **其他常见错误** 部分中描述的多种方式完成。我在你引用的内容下面两段回答_所问的问题。 (2认同)

Rou*_*com 6

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

@Bean
public InternalResourceViewResolver resolver() {
    InternalResourceViewResolver vr = new InternalResourceViewResolver();
    vr.setPrefix("/WEB-INF/jsps/");
    vr.setSuffix(".jsp");
    return vr;
}
Run Code Online (Sandbox Code Playgroud)

added tomcat-embed-jasper:

<dependency>
       <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-jasper</artifactId>
       <scope>provided</scope>
</dependency>
Run Code Online (Sandbox Code Playgroud)

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