Spring 在应用过滤器时区分带和不带尾部斜线的路径

Spring differentiates path with and without trailing slash when applying filters

我正在开发一个 Spring 引导应用程序,我想在某些特定的 URL 中应用一些过滤器,为此我正在实现 Filter 接口(下面的代码)并使用 FilterRegistrationBean 其中我有方法 setUrlPatterns 来定义使用过滤器的端点。我从头开始创建了一个示例应用程序,并为 /hello 设置了过滤器,但过滤器不适用于 /hello/.

这使得有必要将行从 filterRegistrationBean.setUrlPatterns(List.of("/hello"));filterRegistrationBean.setUrlPatterns(List.of("/hello", "/hello/"));。它解决了我的问题,但我不想复制仅包含尾部斜杠的路径。

有没有更好的方法来代替 List.of("/hello", "/hello/")?

代码:

import javax.servlet.*;
import java.io.IOException;

public class LogFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("-----------------FILTER-----------------");
        chain.doFilter(request, response);
    }

    // ... other methods
}
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;

@Configuration
public class WebConfig {

    @Bean
    public FilterRegistrationBean<LogFilter> logFilter() {
        var filterRegistrationBean = new FilterRegistrationBean<>(new LogFilter());
        filterRegistrationBean.setUrlPatterns(List.of("/hello"));
        return filterRegistrationBean;
    }
}
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController()
@RequestMapping("/hello")
public class HelloController {

    @GetMapping
    public String hello() {
        return "Hello";
    }
}

研究:

我在嵌入式 Tomcat 上进行了调试,我发现 ApplicationFilterFactory 它使用一些逻辑来决定何时在路径中应用过滤器,它看起来重复路径是唯一的解决方案,但在这个例子中我只使用一个路径,假设你有 20..30 个路径用于这个过滤器,一旦复制以包含尾部斜线它就变成 40..60,所以我试图为此找到另一个解决方案(由于嵌入式 tomcat).

上的 ApplicationFilterFactory 实现,我不确定是否有其他解决方案

See here :

setUrlPatterns

public void setUrlPatterns(Collection<String> urlPatterns)

Set the URL patterns that the filter will be registered against. This will replace any previously specified URL patterns. ...

此方法的变体(具有更精确的文档):

addUrlPatterns

public void addUrlPatterns(String... urlPatterns)

Add URL patterns, as defined in the Servlet specification, that the filter will be registered against.

...(搜索;):

12.2 Specification of Mappings

In the Web application deployment descriptor, the following syntax is used to define mappings:

  • A string beginning with a / character and ending with a /* suffix is used for path mapping.
  • A string beginning with a *. prefix is used as an extension mapping.
  • The empty string ("") is a special URL pattern that exactly maps to the application's context root, i.e., requests of the form http://host:port/<contextroot>/. In this case the path info is / and the servlet path and context path is empty string ("").
  • A string containing only the / character indicates the "default" servlet of the application. In this case the servlet path is the request URI minus the context path and the path info is null.
  • All other strings are used for exact matches only.

If the effective web.xml (after merging information from fragments and annotations) contains any url-patterns that are mapped to multiple servlets then the deployment must fail.

所以对于你的核心问题:

Is there a better way to do this instead write List.of("/hello", "/hello/")?

不(抱歉),Servlet <=3.1 不支持。根据此规范,"hello", "hello/" 将是 两个 完全匹配。 (并且 List.of(few, ..., items) 是“非常酷”/最新的/不可变的!;))