java—spring请求Map逻辑应该基于servletpath的值Map到处理程序方法吗?

jdgnovmf  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(379)

如果之前有人问过这个问题,我道歉。我搜索了spring文档、stackoverflow和web,但没有找到答案。
我对spring还相当陌生,在调查我遇到的一个请求Map问题的过程中,我遇到了一些(对我来说)不寻常和意外的行为,这些行为会导致 <url-pattern> 以及 @RequestMapping 从两个不同的URL调用。我确信这是因为我自己缺乏理解,所以我希望有人能证实它应该如何运作,最好能告诉我它是在哪里被记录的。我通过一个独立的servlet容器使用spring框架,而不是springboot。
下面的例子说明了这种行为。
考虑以下web.xml片段

<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未找到错误。我假设当pathinfo为null时,spring请求Map器使用servletpath。但是,如果pathinfo不为null,则只使用pathinfo值,如下所示: http://localhost:8084/apps/test-servlet/myid 结果是:

HTTP ERROR 404
Problem accessing /apps/test-servlet/myid. Reason: Not Found

实际上,我肯定在某个地方读到过,servlet不应该将servletpath用作请求的一部分,但目前找不到特定的引用。
将@getmapping更改为“/”for testall()和“/{id}”for testwithid()也不会产生预期效果,因为访问: http://localhost:8084/apps/test-servlet 现在导致对testwithid()的调用,而不是testall(),后者现在需要在其url中有一个尾随的/,以便像以前一样运行,而不需要尾随的/。老实说,这也不是我所预期的,而且似乎是spring请求Map器使用servletpath代替pathinfo的另一种情况(pathinfo是空的,没有尾随空格)。如果有人能阐明这一点,我也将不胜感激。
我发现避免这个问题的一种方法是更改url模式(以及servletpath)或更改pathinfo以使值不同。这似乎是一个奇怪的约束(并且将部署时配置与编译时配置紧密耦合),我在任何地方都没有见过。
任何人能提供的关于这些行为的任何信息或建议都将受到感激。很抱歉问这么冗长的问题!
当做

针对以下初始评论的快速更新:
将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();
  }
}

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

vwkv1x7d

vwkv1x7d1#

在查看了spring源代码之后,似乎上面的行为在某种程度上是设计的(尽管上面的特定用例可能不是)。
在确定执行匹配处理程序方法的查找时要使用的路径时,将调用: UrlPathHelper.getLookupPathForRequest(HttpServletRequest request) 此方法首先检查 alwaysUseFullPath 设置为 true . 如果是,则使用servlet路径和路径信息作为查找路径。
如果 alwaysUseFullPath 设置为 false 然后它尝试仅使用路径信息来构造查找路径(正如您所期望的那样)。但是,如果找到的路径是一个空字符串,那么它将返回到同时使用servlet路径和路径信息,即 alwaysUseFullPath 已设置为 true .
因此,结果是uri的 /apps/test-servlet/test-servlet 以及 /apps/test-servlet 导致查找路径为 /test-servlet 因此双方都将与 @GetMapping 的值 testAll() .
遗憾的是没有 neverUseFullPath 设置为我不认为我观察到的行为是可取的,即使它可能不是那么可能(尽管如果另一个团队负责编写servlet并部署servlet,那么您可能会在uri中得到重复)。

相关问题