如果之前有人问过这个问题,我道歉。我搜索了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();
}
}
对观察到的行为没有任何影响。
1条答案
按热度按时间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中得到重复)。