You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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"}

问题根源

  1. 错误重写getServletPath:getServletPath应返回相对于应用上下文路径的部分(比如/infra/Test___Test),但代码返回了完整URL(包含http://localhost:9091),导致容器验证contextPath时出错。
  2. shouldNotFilter的空指针风险:当请求路径无额外路径信息时,request.getPathInfo()会返回null,直接调用contains会触发空指针异常。
  3. 编码自动转义:容器会自动对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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 00:42:30