Home > Back-end >  Spring differentiates path with and without trailing slash when applying filters
Spring differentiates path with and without trailing slash when applying filters

Time:11-22

I'm working on a Spring Boot application and I want to apply some filters in some specific URLs, for this I'm implementing the Filter interface (code below) and using FilterRegistrationBean where I have the method setUrlPatterns to define the endpoints that use the filter. I've created an example application from scratch and I set up my filter for /hello, but the filter is not applied for /hello/.

It makes the necessity of change the line from filterRegistrationBean.setUrlPatterns(List.of("/hello")); to filterRegistrationBean.setUrlPatterns(List.of("/hello", "/hello/"));. It resolves my problem, but I don't want to duplicate the path only to include the trailing slash.

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

Code:

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

Research:

  • There is another way to define filters, enabling @ServletComponentScan, create a class that implements Filter and annotate it with @WebFilter(urlPatterns = {"/hello"}), but the result is the same.
  • I could use /hello/*, it will consider the path with and without trailing slash, but i don't want to apply the filter for everything else like /hello/abc/, I want to apply the filter only exactly for /hello (and /hello/).

I did debug on the embeded Tomcat and I found the ApplicationFilterFactory where it uses some logic to decide when apply filter or not in a path, it looks duplicate the path is the unique solution, but in this example I'm using only one path, imagine you have 20..30 paths for this filter, once duplicated to include trailing slash it becomes 40..60, so I'm trying to find another solution for this (I'm not sure if there is another solution because of this ApplicationFilterFactory implementation on the embeded tomcat).

CodePudding user response:

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. ...

a variant of this method (with more precise documentation):

addUrlPatterns

public void addUrlPatterns(String... urlPatterns)

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

...(searching;) :

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.

So to your core question:

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

Nope(, sorry), not with Servlet <=3.1. "hello", "hello/" would be two exact matches according to this specification. (And List.of(few, ..., items) is "quite cool"/up-to-date/immutable!;))

  • Related