解决Spring @MVC DispatcherServlet绑定多种URL模式时遇到的问题

和自甴很熟 提交于 2019-11-30 08:21:09

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,具体使用哪一个应视情况而定。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!