为什么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"?
问题描述
我正在编写一个部署在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 $ c中找不到带有URI
[/ some / path]
的HTTP请求的映射$ c>名称为SomeName
和要么抛出 NoHandlerFoundException
或立即使用404 Not Found状态代码提交响应。
为什么没有 DispatcherServlet
找到一个可以处理我的请求的 HandlerMapping
?
最常见的 HandlerMapping
实现是 RequestMappingHandlerMapping
,它处理注册 @Controller
bean作为处理程序(真的是他们的 @RequestMapping
带注释的方法)。您可以自己声明此类型的bean(使用 @Bean
或< bean>
或其他机制)或你可以使用建成-in选项。它们是:
- 使用
注释
。@Configuration
类@EnableWebMvc - 在XML中声明
< mvc:annotation-driven />
成员配置。
如上面的链接所述,这两个都会注册 RequestMappingHandlerMapping
bean(以及其他一些东西)。但是,没有处理程序, HandlerMapping
不是很有用。 RequestMappingHandlerMapping
需要一些 @Controller
bean,所以你需要通过 @Bean <来声明这些bean。 / code> Java配置中的方法或XML配置中的
< bean>
声明或 @Controller $的组件扫描c $ c>其中一个带注释的类。 确保这些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]
inDispatcherServlet
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:
- Annotate your
@Configuration
class with@EnableWebMvc
. - 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]
inDispatcherServlet
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屋!