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




