为什么 Spring MVC 以 404 响应并报告“在 DispatcherServlet 中找不到带有 URI [...] 的 HTTP 请求的映射"? [英] Why does Spring MVC respond with a 404 and report "No mapping found for HTTP request with URI [...] in DispatcherServlet"?

查看:35
本文介绍了为什么 Spring MVC 以 404 响应并报告“在 DispatcherServlet 中找不到带有 URI [...] 的 HTTP 请求的映射"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个部署在 Tomcat 上的 Spring MVC 应用程序.请参阅以下最小、完整且可验证的示例

I'm writing a Spring MVC application deployed on Tomcat. See the following 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
public class ExampleController {
    @RequestMapping(path = "/home", method = RequestMethod.GET)
    public String example() {
        return "index";
    }
}

我的应用程序的上下文名称是 Example.当我向

My application's context name is Example. When I send a request to

http://localhost:8080/Example/home

应用程序响应 HTTP 状态 404 并记录以下内容

the application responds with an HTTP Status 404 and logs the following

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?

I have a JSP resource at /WEB-INF/jsps/index.jsp I expected Spring MVC to use my controller to handle the request and forward to the JSP, so why is it responding with a 404?

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

推荐答案

您的标准 Spring MVC 应用程序将通过 DispatcherServlet,您已在 Servlet 容器中注册.

Your standard Spring MVC application will serve all requests through a DispatcherServlet that you've registered with your Servlet container.

DispatcherServlet 查看它的 ApplicationContext 和,如果可用,ApplicationContext 注册一个 ContextLoaderListener 用于特殊bean 它需要设置其请求服务逻辑.这些 bean 在文档中进行了描述.

The DispatcherServlet looks at its ApplicationContext and, if available, the ApplicationContext registered with a ContextLoaderListener for special beans it needs to setup its request serving logic. These beans are described in the documentation.

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

Arguably the most important, beans of type HandlerMapping map

对处理程序的传入请求以及预处理器和后处理器的列表(处理程序拦截器)基于一些标准,其中的细节因 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.

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

The javadoc of HandlerMapping further describes how implementations must behave.

DispatcherServlet 查找所有这种类型的 bean 并按某种顺序注册它们(可以自定义).在处理请求时,DispatcherServlet 遍历这些 HandlerMapping 对象并使用 getHandler 找到一个可以处理传入请求的请求,表示为标准的HttpServletRequest.从 4.3.x 开始,如果没有找到,它记录您看到的警告

The DispatcherServlet finds all beans of this type and registers them in some order (can be customized). While serving a request, the DispatcherServlet loops through these HandlerMapping objects and tests each of them with getHandler to find one that can handle the incoming request, represented as the standard HttpServletRequest. As of 4.3.x, if it doesn't find any, it logs the warning that you see

在名称为 SomeName

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

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

and either throws a NoHandlerFoundException or immediately commits the response with a 404 Not Found status code.

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

The most common HandlerMapping implementation is RequestMappingHandlerMapping, which handles registering @Controller beans as handlers (really their @RequestMapping annotated methods). You can either declare a bean of this type yourself (with @Bean or <bean> or other mechanism) or you can use the built-in options. These are:

  1. 使用 @EnableWebMvc 注释您的 @Configuration 类.
  2. 在您的 XML 配置中声明一个 成员.
  1. Annotate your @Configuration class with @EnableWebMvc.
  2. Declare a <mvc:annotation-driven /> member in your XML configuration.

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

As the link above describes, both of these will register a RequestMappingHandlerMapping bean (and a bunch of other stuff). However, a HandlerMapping isn't very useful without a handler. RequestMappingHandlerMapping expects some @Controller beans so you need to declare those too, through @Bean methods in a Java configuration or <bean> declarations in an XML configuration or through component scanning of @Controller annotated classes in either. Make sure these beans are present.

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

If you're getting the warning message and a 404 and you've configured all of the above correctly, then you're sending your request to the wrong URI, one that isn't handled by a detected @RequestMapping annotated handler method.

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

The spring-webmvc library offers other built-in HandlerMapping implementations. For example, BeanNameUrlHandlerMapping maps

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

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

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

and you can always write your own. Obviously, 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 注册一些 默认值.这些定义在 DispatcherServlet.propertiesDispatcherServlet 类位于同一包中.它们是 BeanNameUrlHandlerMappingDefaultAnnotationHandlerMapping(类似于RequestMappingHandlerMapping,但已弃用).

If you don't implicitly or explicitly register any HandlerMapping beans (or if detectAllHandlerMappings is true), the DispatcherServlet registers some defaults. These are defined in DispatcherServlet.properties in the same package as the DispatcherServlet class. They are BeanNameUrlHandlerMapping and DefaultAnnotationHandlerMapping (which is similar to RequestMappingHandlerMapping but deprecated).

Spring MVC 将记录通过 RequestMappingHandlerMapping 注册的处理程序.例如,一个 @Controller

Spring MVC will log handlers registered through RequestMappingHandlerMapping. For example, a @Controller like

@Controller
public class ExampleController {
    @RequestMapping(path = "/example", method = RequestMethod.GET, headers = "X-Custom")
    public String example() {
        return "example-view-name";
    }
}

将在 INFO 级别记录以下内容

will log the following at INFO level

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

这描述了注册的映射.当您看到未找到处理程序的警告时,将消息中的 URI 与此处列出的映射进行比较.@RequestMapping 必须匹配 Spring MVC 才能选择处理程序.

This describes the mapping registered. When you see the warning that no handler was found, compare the URI in the message to the mapping listed here. All the restrictions specified in the @RequestMapping must match for Spring MVC to select the handler.

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

Other HandlerMapping implementations log their own statements that should hint to their mappings and their corresponding handlers.

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

Similarly, enable Spring logging at DEBUG level to see which beans Spring registers. It should report which annotated classes it finds, which packages it scans, and which beans it initializes. If the ones you expected aren't present, then review your ApplicationContext configuration.

DispatcherServlet 只是一个典型的 Java EE Servlet.您可以使用典型的 声明来注册它,或者直接通过 ServletContext#addServletWebApplicationInitializer,或使用 Spring boot 使用的任何机制.因此,您必须依赖 Servlet 规范,见第 12 章.另见

A DispatcherServlet is just a typical Java EE Servlet. You register it with your typical <web.xml> <servlet-class> and <servlet-mapping> declaration, or directly through ServletContext#addServlet in a WebApplicationInitializer, or with whatever mechanism Spring boot uses. As such, you must rely on the url mapping logic specified in the Servlet specification, see Chapter 12. See also

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

With that in mind, a common mistake is to register the DispatcherServlet with a url mapping of /*, returning a view name from a @RequestMapping handler method, and expecting a JSP to be rendered. For example, consider a handler method like

@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资源.这不会发生.相反,假设上下文名称为 ExampleDisaptcherServlet 将报告

you might expect the request to be forwarded to a JSP resource at the path /WEB-INF/jsps/example-view-name.jsp. This won't happen. Instead, assuming a context name of Example, the DisaptcherServlet will report

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

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

因为 DispatcherServlet 映射到 /* 并且 /* 匹配所有内容(精确匹配除外,它们具有更高的优先级),<将选择 code>DispatcherServlet 来处理 JstlView(由 InternalResourceViewResolver 返回).几乎在所有情况下,DispatcherServlet 都不会被配置为处理这样​​的请求.

Because the DispatcherServlet is mapped to /* and /* matches everything (except exact matches, which have higher priority), the DispatcherServlet would be chosen to handle the forward from the JstlView (returned by the InternalResourceViewResolver). In almost every case, the DispatcherServlet will not be configured to handle such a request.

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

Instead, in this simplistic case, you should register the DispatcherServlet to /, marking it as the default servlet. The default servlet is the last match for a request. This will allow your typical servlet container to chose an internal Servlet implementation, mapped to *.jsp, to handle the JSP resource (for example, Tomcat has JspServlet), before trying with the default servlet.

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

That's what you're seeing in your example.

这篇关于为什么 Spring MVC 以 404 响应并报告“在 DispatcherServlet 中找不到带有 URI [...] 的 HTTP 请求的映射"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆