Spring请求映射逻辑是否应根据ServletPath的值映射到处理程序方法?

问题描述

很抱歉,是否曾经有人问过这个问题。我已经在StackOverflow和整个Web上搜索过Spring文档,但没有找到答案。

我对Spring还是很陌生,在调查我遇到的请求映射问题的过程中,我遇到了一些异常和意外的行为(对我而言),这些行为导致了一个<url-pattern>和{{ 1}}从2个不同的URL中调用。 我敢肯定,这是由于我缺乏了解,所以我希望有人可以确认它的行为方式,最好将其指向记录的地方。我通过独立的Servlet容器而不是SpringBoot使用Spring Framework。

下面的示例说明了行为。

考虑以下web.xml代码

@RequestMapping

部署在以下上下文路径中:

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

test-spring-servlet-config.xml包含:

/apps

TestSpringServletController类定义为:

<beans>
  <mvc:annotation-driven />
  <bean id="TestController" class="org.example.TestSpringServletController" />
</beans>

访问: @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

这不是我所期望的,我找不到记录的行为。我期望出现404 Not Found错误。我假设发生的事情是,当PathInfo为null时,Spring Request Mapper使用ServletPath代替。但是,当PathInfo不为null时,则仅使用PathInfo值,如下所示: Spring Test Servlet - testAll() contextpath: [/apps] ServletPath: [/test-servlet] PathInfo: [null] 结果是:

http://localhost:8084/apps/test-servlet/myid

我确实确定我已经读过某个地方servlet不应将servletPath用作请求的一部分,但是此刻无法找到该特定引用。

将testAll()的@GetMapping更改为“ /”,将testWithId()的@GetMapping更改为“ / {id}”也不具有预期的效果,因为访问: HTTP ERROR 404 Problem accessing /apps/test-servlet/myid. Reason: Not Found 现在导致对testWithId()而不是testAll()的调用,该调用现在需要在URL中使用尾随/才能像以前一样操作而没有尾随/。老实说,这也不是我所期望的,并且看起来是另一种情况,其中Spring请求映射器使用servletPath代替PathInfo(在没有尾随空格的情况下为null)。如果有人可以阐明这一点,我也将不胜感激。

我发现可以避免此问题的一种方法是更改​​url-pattern(从而更改servletPath)或更改PathInfo以使值不同。这似乎是一个奇怪的约束(并将部署时间配置与编译时间配置紧密结合在一起),我在任何地方都没有提到。

任何人都可以提供的关于任何一种行为的信息或指针,将不胜感激。道歉这么长的问题!

致谢

====

快速更新,以回应以下初步评论

将web.xml替换为:

http://localhost:8084/apps/test-servlet

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

对观察到的行为没有任何影响。

解决方法

看过Spring源代码后,似乎上面的行为是设计使然的(尽管上面的特定用例可能并非如此)。

确定执行匹配处理程序方法的查找时要使用的路径时,将调用:UrlPathHelper.getLookupPathForRequest(HttpServletRequest request)

此方法首先检查alwaysUseFullPath是否设置为true。如果是,那么它将同时使用servlet路径和路径信息作为查找路径。

如果将alwaysUseFullPath设置为false,则它将尝试仅使用路径信息来构造查找路径(如您期望的那样)。但是,如果找到的路径为空字符串,则它会退回到同时使用servlet路径和路径信息,即好像alwaysUseFullPath设置为true

因此,结果是URI的/apps/test-servlet/test-servlet/apps/test-servlet都导致了/test-servlet的查找路径,因此都将与{{ 1}}。

可惜没有@GetMapping设置,因为我认为我观察到的行为不是可取的,即使这种可能性不太可能(尽管由不同的团队负责编写servlet并部署servlet,那么也许可以在URI中获得重复项。