为什么Spring MVC以404响应并报告“未找到带有URI的HTTP请求的映射[...]在 DispatcherServlet 中“?为什么没有找到可以处理我的请求的?DispatcherServletHandlerMapping调试其他常见错误

2022-08-31 10:04:48

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

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

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


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


答案 1

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

查看它和(如果可用)注册到一个特殊bean,它需要设置其请求服务逻辑。这些豆子在文档中进行了描述DispatcherServletApplicationContextApplicationContextContextLoaderListener

可以说是最重要的,处理程序映射类型的豆子

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

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

查找此类型的所有 bean,并按某种顺序(可自定义)注册它们。在为请求提供服务时,循环遍历这些对象,并使用getHandler测试每个对象,以找到可以处理传入请求的对象,表示为标准。从 4.3.x 开始,如果它找不到任何内容,它会记录您看到的警告DispatcherServletDispatcherServletHandlerMappingHttpServletRequest

未找到名为 SomeName 的 URI 的 HTTP 请求的映射[/some/path]DispatcherServlet

并抛NoHandlerFoundException 或立即提交带有 404 Not Found 状态代码的响应。

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

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

  1. 使用 为类添加批注。@Configuration@EnableWebMvc
  2. 在 XML 配置中声明成员。<mvc:annotation-driven />

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

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

该库提供其他内置实现。例如,BeanNameUrlHandlerMapping mapsspring-webmvcHandlerMapping

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

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

如果您没有隐式或显式注册任何bean(或者如果expceptAllHandlerMappings是),则注册一些默认值。这些在 DispatcherServlet.properties 中定义,与类位于同一包中。它们是和DefaultAnnotationHandlerMapping(类似于但已弃用)。HandlerMappingtrueDispatcherServletDispatcherServletBeanNameUrlHandlerMappingRequestMappingHandlerMapping

调试

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 日志记录,以查看哪些 Bean Spring 寄存器。它应该报告它找到哪些带注释的类,它扫描哪些包,以及它初始化了哪些bean。如果预期的不存在,请检查您的配置。ApplicationContext

其他常见错误

A 只是一个典型的 Java EE Servlet。您可以使用典型的声明注册它,或者直接通过WebApplicationInitializer中的ServletContext#addServlet,或者使用Spring boot使用的任何机制来注册它。因此,您必须依赖于 Servlet 规范中指定的 url 映射逻辑,请参阅第 12 章。另请参见DispatcherServlet<web.xml><servlet-class><servlet-mapping>

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

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

使用内部资源查看解决方案

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

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

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

由于 映射到并匹配所有内容(完全匹配项除外,它们具有较高的优先级),因此将选择 从 JstlView 处理 (由 返回)。在几乎所有情况下,DispatcherServlet 都不会被配置为处理这样的请求DispatcherServlet/*/*DispatcherServletforwardInternalResourceViewResolver

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

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


答案 2

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

@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>

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


推荐