【发布时间】:2020-12-08 04:32:23
【问题描述】:
如果之前有人问过这个问题,我们深表歉意。我已经搜索了 Spring 文档、StackOverflow 和整个 Web,但没有找到答案。
我对 Spring 还很陌生,在调查我遇到的请求映射问题的过程中,我遇到了一些不寻常和意外的(对我而言)行为,导致单个 <url-pattern> 和 @RequestMapping 到从 2 个不同的 URL 调用。
我确信这是由于我缺乏理解,所以我希望有人可以确认它应该如何表现,并最好指出我记录在哪里。我通过独立的 Servlet 容器使用 Spring 框架,而不是 SpringBoot。
以下示例说明了该行为。
考虑以下 web.xml sn-p
<servlet>
<servlet-name>TestSpringServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/test-spring-servlet-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>TestSpringServlet</servlet-name>
<url-pattern>/test-servlet/*</url-pattern>
</servlet-mapping>
部署在以下上下文路径:
/apps
test-spring-servlet-config.xml 包含:
<beans>
<mvc:annotation-driven />
<bean id="TestController" class="org.example.TestSpringServletController" />
</beans>
TestSpringServletController 类定义为:
@Controller
public class TestSpringServletController
{
@GetMapping("/test-servlet")
public void testAll(HttpServletRequest request, HttpServletResponse response) throws IOException
{
response.getWriter().append("<h2>Spring Test Servlet - testAll()</h2>");
response.getWriter().append("ContextPath: [").append(request.getContextPath()).append("]<br/>");
response.getWriter().append("ServletPath: [").append(request.getServletPath()).append("]<br/>");
response.getWriter().append("PathInfo: [").append(request.getPathInfo()).append("]");
}
@GetMapping("/test-servlet/{id}")
public void testWithId(HttpServletRequest request, HttpServletResponse response, @PathVariable String id) throws IOException
{
response.getWriter().append("<h2>Spring Test Servlet - testWithId()</h2>");
response.getWriter().append("ContextPath: [").append(request.getContextPath()).append("]<br/>");
response.getWriter().append("ServletPath: [").append(request.getServletPath()).append("]<br/>");
response.getWriter().append("PathInfo: [").append(request.getPathInfo()).append("]");
}
}
访问:
http://localhost:8084/apps/test-servlet/test-servlet
结果:
Spring Test Servlet - testAll()
ContextPath: [/apps]
ServletPath: [/test-servlet]
PathInfo: [/test-servlet]
正如预期的那样。
访问:
http://localhost:8084/apps/test-servlet/test-servlet/myid
结果:
Spring Test Servlet - testWithId()
ContextPath: [/apps]
ServletPath: [/test-servlet]
PathInfo: [/test-servlet/myid]
也符合预期。
但是,访问:
http://localhost:8084/apps/test-servlet
结果:
Spring Test Servlet - testAll()
ContextPath: [/apps]
ServletPath: [/test-servlet]
PathInfo: [null]
这不是我所期望的,我找不到这种行为的记录。我预计会出现 404 Not Found 错误。我假设正在发生的是,当 PathInfo 为空时,Spring Request Mapper 正在使用 ServletPath。但是,当 PathInfo 不为 null 时,则仅使用 PathInfo 值,如下所示:
http://localhost:8084/apps/test-servlet/myid
这导致:
HTTP ERROR 404
Problem accessing /apps/test-servlet/myid. Reason: Not Found
我确实确定我在某处读到过 servletPath 不应被 servlet 用作请求的一部分,但目前找不到该特定引用。
将 @GetMapping 更改为 testAll() 的“/”和 testWithId() 的“/{id}”也没有预期的效果,因为访问:
http://localhost:8084/apps/test-servlet
现在导致调用 testWithId() 而不是 testAll(),它现在需要在其 URL 中添加尾随 /,以便在没有尾随 / 的情况下像以前一样运行。老实说,这也不是我所期望的,并且看起来是另一种情况,即 Spring 请求映射器使用 servletPath 代替 PathInfo(没有尾随空格的情况下为 null)。如果有人能阐明这一点,我也将不胜感激。
我发现避免该问题的一种方法是更改 url 模式(以及因此 servletPath)或更改 PathInfo 以使值不同。这似乎是一个奇怪的约束(并且将部署时间配置与编译时间配置紧密结合在一起),我在任何地方都没有提到过。
任何人都可以就这些行为提供的任何信息或指示,我们将不胜感激。为这么冗长的问题道歉!
问候
====
响应以下初始 cmets 的快速更新:
将 web.xml 替换为:
public class WebAppBootstrap implements WebApplicationInitializer
{
private static final String URI_TEST_SERVICE = "/test-servlet/*";
private static final String NAME_TEST_SERVICE = "TestSpringServlet";
@Override
public void onStartup(ServletContext servletContext) throws ServletException
{
AnnotationConfigWebApplicationContext testServletContext = new AnnotationConfigWebApplicationContext();
testServletContext.register(TestServletConfig.class);
ServletRegistration.Dynamic testDispatcher = servletContext.addServlet(NAME_TEST_SERVICE, new DispatcherServlet(testServletContext));
testDispatcher.setLoadOnStartup(1);
testDispatcher.addMapping(URI_TEST_SERVICE);
}
}
和test-spring-servlet-config.xml 与:
@Configuration
@EnableWebMvc
public class TestServletConfig
{
@Bean
public TestSpringServletController testController()
{
return new TestSpringServletController();
}
}
对观察到的行为没有任何影响。
【问题讨论】:
-
这是针对新应用程序还是旧应用程序?现代风格是消除所有
web.xml业务,使用Spring Boot。 (无论哪种情况,您都应该尽量避免直接使用HttpServletR*;Spring 的重点是 MVC 基础架构为您处理所有映射和绑定。) -
这是一个新的应用程序。虽然实际应用程序仍然是 Spring Framework 而不是 Spring Boot,但它不使用 HttpServletR* 或 web.xml。上面的例子只是为了说明我在一个简单的测试 Servlet 中用最少的代码看到的问题,并提供对调试输出的请求 URL 字段的轻松访问。
-
从示例代码中删除了日志记录行,使其更简洁,因为响应输出中的相同细节
-
如果您的“实际应用程序”不使用
web.xml,那么该示例已被删除,以至于无法帮助您调试路径问题。 -
根据上述 cmets,我已经删除了 web.xml 和 Spring xml 配置,并将它们替换为基于代码的配置替代方案(相关代码添加到问题文本中)。这对观察到的行为没有影响。
标签: java spring spring-mvc servlets