为什么 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 应用程序.请参阅以下最小、完整且可验证的示例
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]
inDispatcherServlet
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:
- 使用
@EnableWebMvc
注释您的@Configuration
类. - 在您的 XML 配置中声明一个
成员.
- Annotate your
@Configuration
class with@EnableWebMvc
. - Declare a
<mvc:annotation-driven />
member in your XML configuration.
正如上面的链接所描述的,这两个都将注册一个 RequestMappingHandlerMapping
bean(以及一堆其他东西).但是,如果没有处理程序,HandlerMapping
不是很有用.RequestMappingHandlerMapping
需要一些 @Controller
bean,因此您也需要通过 Java 配置中的 @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(或者如果 detectAllHandlerMappings
是 true
),DispatcherServlet
注册一些 默认值.这些定义在 DispatcherServlet.properties
与 DispatcherServlet
类位于同一包中.它们是 BeanNameUrlHandlerMapping
和 DefaultAnnotationHandlerMapping
(类似于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#addServlet
在 WebApplicationInitializer
,或使用 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资源.这不会发生.相反,假设上下文名称为 Example
,DisaptcherServlet
将报告
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]
inDispatcherServlet
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屋!