You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot过滤器配置:如何避免重复路径,同时匹配带末尾斜杠与不带末尾斜杠的精确路径

解决Spring Boot过滤器兼容末尾斜杠的精确匹配问题

我明白你遇到的痛点——为了让过滤器同时匹配/hello/hello/,不想重复配置两条路径,尤其是当有几十个这样的端点时,重复配置会显得非常冗余。结合Servlet规范和Spring Boot的特性,这里有几个更优雅的解决方案:

方案1:全局启用Spring MVC的末尾斜杠匹配

Spring MVC提供了全局配置,可以让框架自动将带末尾斜杠的路径视为和不带斜杠的路径等价。这样你只需要配置/hello,过滤器就能同时匹配/hello/hello/

配置方式(二选一)

1. 通过application.properties配置

# 使用PATH_PATTERN_PARSER策略(Spring Boot 2.6+推荐)
spring.mvc.pathmatch.matching-strategy=PATH_PATTERN_PARSER
# 禁用后缀模式匹配(可选,避免匹配/hello.json这类路径)
spring.mvc.pathmatch.use-suffix-pattern=false
# 启用末尾斜杠匹配
spring.mvc.pathmatch.use-trailing-slash-match=true

2. 通过Java配置类

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        // 开启末尾斜杠匹配,/hello/会被视为与/hello相同
        configurer.setUseTrailingSlashMatch(true);
    }
}

优点:一次配置全局生效,无需修改过滤器代码;缺点:会影响所有请求路径的匹配逻辑,如果你的项目中有需要严格区分末尾斜杠的场景,这个方案就不适用。

方案2:自定义FilterRegistrationBean的匹配逻辑

如果不想影响全局路径匹配,我们可以直接扩展FilterRegistrationBean,重写它的matches方法,自定义路径匹配规则,让它同时匹配带和不带末尾斜杠的精确路径。

示例代码

import com.example.demo.config.filters.LogFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.Arrays;

@Configuration
public class WebConfig {
    @Bean
    public FilterRegistrationBean<LogFilter> logFilter() {
        FilterRegistrationBean<LogFilter> filterRegistrationBean = new FilterRegistrationBean<>(new LogFilter()) {
            @Override
            public boolean matches(String servletPath) {
                // 精确匹配/hello或/hello/
                return matchesExactWithTrailingSlash(servletPath, "/hello");
            }
        };
        // 这里随便设置一个基础路径即可,因为我们重写了matches方法
        filterRegistrationBean.setUrlPatterns(List.of("/hello"));
        return filterRegistrationBean;
    }

    // 抽成工具方法,方便多个过滤器复用
    private boolean matchesExactWithTrailingSlash(String servletPath, String... targetPaths) {
        return Arrays.stream(targetPaths)
                .anyMatch(path -> path.equals(servletPath) || (path + "/").equals(servletPath));
    }
}

优点:针对单个过滤器定制规则,不影响全局;可以批量处理多个路径(比如传入"/hello", "/test"就能同时匹配每个路径的带/不带斜杠版本);缺点:需要为每个过滤器重写matches方法(不过可以通过抽象类封装复用)。

方案3:底层容器级配置(不推荐,耦合Tomcat)

你提到了Tomcat的ApplicationFilterFactory,确实可以通过修改Tomcat的配置来调整路径匹配逻辑,但这种方式耦合Tomcat容器,切换到Jetty或Undertow时会失效,除非你确定只使用Tomcat,否则不推荐。

示例代码(嵌入式Tomcat)

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.catalina.core.ApplicationFilterFactory;
import org.apache.catalina.Context;

@Configuration
public class TomcatConfig {
    @Bean
    public ServletWebServerFactory servletWebServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.addContextCustomizers((Context context) -> {
            // 启用根路径和目录的重定向,让/hello/自动重定向到/hello
            context.setMapperContextRootRedirectEnabled(true);
            context.setMapperDirectoryRedirectEnabled(true);
        });
        return factory;
    }
}

这种方式本质是让Tomcat自动将带末尾斜杠的请求重定向到不带斜杠的版本,从而让过滤器只需要匹配/hello即可。

为什么默认不支持?

Servlet规范中的URL模式匹配是严格区分末尾斜杠的:/hello/hello/被视为两个完全不同的路径;而/hello/*会匹配所有以/hello开头的子路径(比如/hello/abc),不符合你要精确匹配的需求,所以不能直接用。


内容的提问来源于stack exchange,提问作者Maurício Generoso

火山引擎 最新活动