为什么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"?

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

问题描述

我正在编写一个部署在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
公共类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指数;
}
}

我的应用程序的上下文名称是实施例。当我发送请求

  http:// localhost:8080 / Example / home 

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

  WARN osweb.servlet.PageNotFound  - 找不到带有URI的HTTP请求的映射[/Example/WEB-INF/jsps/index.jsp ]```DispatcherServlet`,名称为'dispatcher'

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






这是针对此警告消息的问题的规范帖子。

解决方案

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



DispatcherServlet 查看其 ApplicationContext ,如果可用,则查看 ApplicationContext 注册了 ContextLoaderListener ,用于设置其请求服务逻辑所需的特殊bean。 这些文档中描述了bean。



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


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


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



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


DispatcherServlet [/ some / path] 的HTTP请求的映射$ c>名称为SomeName


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



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



最常见的 HandlerMapping 实现是 RequestMappingHandlerMapping ,它处理注册 @Controller bean作为处理程序(真的是他们的 @RequestMapping 带注释的方法)。您可以自己声明此类型的bean(使用 @Bean < bean> 或其他机制)或你可以使用建成-in选项。它们是:


  1. 使用注释 @Configuration 类@EnableWebMvc

  2. 在XML中声明< mvc:annotation-driven /> 成员配置。

如上面的链接所述,这两个都会注册 RequestMappingHandlerMapping bean(以及其他一些东西)。但是,没有处理程序, HandlerMapping 不是很有用。 RequestMappingHandlerMapping 需要一些 @Controller bean,所以你需要通过 @Bean <来声明这些bean。 / code> Java配置中的方法或XML配置中的< bean> 声明或 @Controller 其中一个带注释的类。 确保这些bean存在。



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



spring-webmvc 库提供其他内置 HandlerMapping 实现。例如, BeanNameUrlHandlerMapping maps


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


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



如果您没有隐式或显式注册任何 HandlerMapping bean(或者如果 detectAllHandlerMappings true ), DispatcherServlet 注册一些默认值。这些在 DispatcherServlet 类相同的包中的.propertiesrel =noreferrer> DispatcherServlet.properties 。它们是 BeanNameUrlHandlerMapping DefaultAnnotationHandlerMapping (类似于 RequestMappingHandlerMapping 但已弃用)。



调试



Spring MVC将记录通过 RequestMappingHandlerMapping 。例如, @Controller 喜欢

  @Controller 
public class ExampleController {
@RequestMapping(path =/ example,method = RequestMethod.GET,headers =X-Custom)
public String example(){
returnexample-视图名称;
}
}

将在INFO级别记录以下内容

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

这描述了映射注册。当您看到没有找到处理程序的警告时,请将消息中的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 ,或者使用Spring引导使用的任何机制。因此,您必须依赖 url mapping 逻辑。 rel =noreferrer> Servlet规范,请参阅第12章。另请参阅





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

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

带有 InternalResourceViewResolver

  @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.jsp 。这不会发生。相反,假设上下文名称为示例 DisaptcherServlet 将报告


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


因为 DispatcherServlet 映射到 / * / * 匹配所有内容(完全匹配除外)具有更高优先级的, DispatcherServlet 将被选择来处理来自转发: //docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/view/JstlView.html\"rel =noreferrer> JstlView (由 InternalResourceViewResolver 返回)。 几乎在所有情况下, DispatcherServlet 都不会配置为处理此类请求



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



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


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[] { "/*" };
    }
}

Where SpringServletConfig is

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

Finally, I have a @Controller in the package com.example.controllers

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

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

http://localhost:8080/Example/home

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'

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?


This is meant to be a canonical post for questions about this warning message.

解决方案

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

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.

Arguably the most important, beans of type HandlerMapping map

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.

The javadoc of HandlerMapping further describes how implementations must behave.

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

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

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

Why didn't the DispatcherServlet find a HandlerMapping that could handle my request?

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. Annotate your @Configuration class with @EnableWebMvc.
  2. Declare a <mvc:annotation-driven /> member in your XML configuration.

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.

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.

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

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

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.

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).

Debugging

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";
    }
}

will log the following at INFO level

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

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.

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

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.

Other common mistakes

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

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";
}

with an InternalResourceViewResolver

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

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

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

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.

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天全站免登陆