Spring MVC使用过程中遇到了这样一个问题,web.xml有如下的配置,注意两个url-pattern是两种不同的url模式,*.html主要是处理一般的页面请求,而/file/view/*主要是用于文件下载,因为通过response设置下载文件名和文件contentType太过麻烦,所以用了个取巧的方法把文件名作为url的一部分,由于文件后缀有很多种,就没法单靠*.html拦截。
<!-- Spring MVC前端处理器 -->
<servlet>
<servlet-name>Dispatcher Servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<description>Spring MVC定义Bean文件,该文件为空配置,所有配置交给上级WebApplicationContext来处理</description>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/servlet-empty-context.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Dispatcher Servlet</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Dispatcher Servlet</servlet-name>
<url-pattern>/file/view/*</url-pattern>
</servlet-mapping>
然后就有如下的控制器(不相干的代码就跳过了):
@Controller
@RequestMapping("/file/*")
public class FileAction extends AbstractAction {
@RequestMapping(value = "upload.html", method=RequestMethod.POST)
public String upload() {
// ...
}
@RequestMapping(value = "view/{id}/*", method=RequestMethod.GET)
public void view(@PathVariable("id") int id) {
// ...
}
}
结果实际使用时,用作文件上传upload方法跟请求很容易就匹配到了,但是文件下载哪怕是.html结尾的请求也一直就无法匹配导致404.而去掉web.xml中的/file/view/*就正常了。
Debug进Spring源码,发现了类org.springframework.web.util.UrlPathHelper,他被AbstractHandlerMapping作为属性,并调用它的getLookupPathForRequest()方法以请求对象返回一个用于查找匹配项的lookupPath。而这个方法的返回值受其属性alwaysUseFullPath的影响。默认alwaysUseFullPath为false,这时候UrlPathHelper会在原url的基础上截去request.getServletPath()的部分,也就是 /file/view,所以得到的lookupPath就无法匹配了。
现在问题的关键就变成了修改AbstractHandlerMapping中UrlPathHelper的alwaysUseFullPath为true。由于UrlPathHelper是AbstractHandlerMapping直接new出来的,没有在Spring的容器中,无法直接获取,但是发现通过调用AbstractHandlerMapping的setAlwaysUseFullPath()方法可以达到同样的目的(内部自己会改UrlPathHelper的alwaysUseFullPath)。
由于我是直接使用mvc的namespace配置的annotation-driven,找了半天实在找不到如何在配置上对自动生成的HandlerMapping属性进行修改的办法,不过想到UrlPathHelper的getLookupPathForRequest()是在每个请求到达时才调用,在容器初始化到请求到达之间修改它既可,所以单独定义了一个SetAlwaysUseFullPathModule模块来做这个事情。
public class SetAlwaysUseFullPathModule {
@Resource
public void setHandlerMapping(RequestMappingHandlerMapping handlerMapping) {
handlerMapping.setAlwaysUseFullPath(true);
}
}
把这个类部署到Spring配置文件中,再测试下载功能就正常了。
注:这里注入的RequestMappingHandlerMapping是Spring3.1加入的,3.2时它代替原本的DefaultAnnotationHandlerMapping作为Spring MVC annotation-driven的默认HandlerMapping,具体使用哪一个应视情况而定。
来源:oschina
链接:https://my.oschina.net/u/79551/blog/215617