Spring中请求路径'___'转'/'的过滤器失效问题求助
问题场景与解决方案
问题背景
有一个不可修改的外部服务,会在URL路径变量中用___替代/,例如把http://localhost:9091/infra/Test/Test转换为http://localhost:9091/infra/Test___Test。需要在请求处理前把所有___转换回/,于是编写了如下SlashFilter过滤器:
@Component public class SlashFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final HttpServletRequestWrapper wrapped = new HttpServletRequestWrapper(request) { @Override public String getServletPath() { final String originalUrl = ((HttpServletRequest) getRequest()).getRequestURL().toString(); return originalUrl.replace("___", "/").replace("%20", " "); // emphasis } }; filterChain.doFilter(wrapped, response); } @Override protected boolean shouldNotFilter(HttpServletRequest request) { return !request.getPathInfo().contains("___"); } }
遇到的错误
过滤器未生效,收到http://localhost:9091/infra/Test___Test请求时,抛出500错误:
DELETE /infra/Test___Test http: 500 INTERNAL_SERVER_ERROR {"status":500,"error":"Internal Server Error","message":"Invalid contextPath: 'http://localhost:9091/infra/Test/Test': must start with '/' and not end with '/'","path":"/infra/Test___Test"}
尝试将替换后的/编码为%2F,修改代码行:
return originalUrl.replace("___", "%2F").replace("%20", " ");
又出现新错误,%2F被转义为%252F:
DELETE /infra/Test___Test http: 500 INTERNAL_SERVER_ERROR {"status":500,"error":"Internal Server Error","message":"Invalid contextPath: 'http://localhost:9091/infra/Test%252FTest': must start with '/' and not end with '/'","path":"/infra/Test___Test"}
问题根源
- 错误重写
getServletPath:getServletPath应返回相对于应用上下文路径的部分(比如/infra/Test___Test),但代码返回了完整URL(包含http://localhost:9091),导致容器验证contextPath时出错。 shouldNotFilter的空指针风险:当请求路径无额外路径信息时,request.getPathInfo()会返回null,直接调用contains会触发空指针异常。- 编码自动转义:容器会自动对URL特殊字符编码,手动替换
%2F会被二次转义成%252F。
可行解决方案
修改过滤器,正确重写getRequestURI方法(而非getServletPath),只处理路径部分而非完整URL,同时修正过滤判断逻辑:
@Component public class SlashFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { HttpServletRequestWrapper wrapped = new HttpServletRequestWrapper(request) { @Override public String getRequestURI() { // 获取原始请求URI(不含域名端口,比如/infra/Test___Test) String originalUri = super.getRequestURI(); // 替换___为/,同时处理空格转义 return originalUri.replace("___", "/").replace("%20", " "); } // 可选:如果需要同时修正getPathInfo,也可以重写这个方法 @Override public String getPathInfo() { String originalPathInfo = super.getPathInfo(); if (originalPathInfo == null) { return null; } return originalPathInfo.replace("___", "/").replace("%20", " "); } }; filterChain.doFilter(wrapped, response); } @Override protected boolean shouldNotFilter(HttpServletRequest request) { // 先判断pathInfo是否为null,避免空指针 String pathInfo = request.getPathInfo(); return pathInfo == null || !pathInfo.contains("___"); } }
关键修改点说明
- 重写
getRequestURI:getRequestURI返回请求的路径部分(不含协议、域名和端口),替换后容器能正确识别上下文路径和资源路径。 - 修正
shouldNotFilter:先判断pathInfo是否为null,避免空指针异常;仅当路径包含___时才进行过滤。 - 避免手动编码:直接替换为
/即可,容器会自动处理路径合法性,无需手动编码%2F。
内容的提问来源于stack exchange,提问作者JasperMW
相关产品推荐
相关产品推荐

