Spring Boot 3 Controller Handling POST Requests with Trailing Slash Issue

2.6k views Asked by At

I am facing an issue with my Spring Boot application where I have a single controller that handles POST requests only. I have set the context path in my application.yml as server.servlet.context-path: /context/path. The goal is to handle POST requests for both /context/path and /context/path/ URLs.

My controller looks like this:

@RestController
@RequestMapping("")
public class MyController {

    @PostMapping({ "", "/" })
    public ResponseEntity<String> handlePostRequest() {
        // Handling POST request logic
        return ResponseEntity.ok("POST request handled");
    }
}

When I send a POST request to /context/path, it gets redirected with a 302 status code and the request method changes to GET, and it gets redirected to /context/path/.

I have tried different combinations of @RequestMapping and PostMapping. Nothing worked.

I found some recommended solution to create a WebConfiguration and override the configurePathMatch method. But the methods like setUseTrailingSlashMatch or setMatchOptionalTrailingSeparator are deprecated.

Despite these attempts, the issue persists. How can I configure my application to handle the requests with or without trailing slashes? Any insights or suggestions on resolving this issue would be greatly appreciated.

1

There are 1 answers

4
Slevin On

The migration to Spring Boot 3 and thus respectively to Spring Framework 6 comes with major changes. One of them is that the configuration option for trailing slash matching has been declared deprecated

There are a few options to deal with this:

  • Use a redirect filter within the SecurityFilterChain
  • Explicitly declare all supported routes (with and w/o trailing slash) -> my personal preference
  • Use a request wrapper filter to internally follow the correct route w/o requesting a redirect
  • Use an URL rewrite filter at servlet container level (e.g. Tuckey)
  • Use an URL rewrite engine at server level (e.g. Apache mod_rewrite)

To hardcode somehow additionally the routes for trailing slashes as well is for the most applications not that hard. You only have to find your preferred way to get them into the endpoints declarations. Since most applications have 10 or so hard routes and almost all other routes are dynamically generated via PathVariables, managing this amount of endpoints ist quite possible.

However, if you wanna deal with this situation at application level anyhow, here a proper working redirect filter you can add to the SecurityFilterChain:

public class TrailingSlashRedirectFilter extends OncePerRequestFilter {

    private static final Logger logger = LoggerFactory.getLogger(TrailingSlashRedirectFilter.class);

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain) throws ServletException, IOException {

        /* We want to obtain the complete request URL including the query string */
        String url = ServletUriComponentsBuilder.fromRequest(request).build().toUriString();
        String path = request.getRequestURI();
        String fixedUrl = "";

        if (url.endsWith("/") && path.length() > 1 /* not the root path */)
            fixedUrl = url.substring(0, url.length() - 1);

        if (path.isEmpty() /* root path without '/' */)
            fixedUrl = url + "/";

        if (!fixedUrl.isEmpty()) {
            response.setHeader(HttpHeaders.LOCATION, fixedUrl);
            response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
            logger.trace("Redirecting with HttpStatus 301 for requested URL '{}' to '{}'", url, fixedUrl);
        } else {
            filterChain.doFilter(request, response);
        }
    }
}

You then simply add it like that:

public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

    return http
        //...

        .addFilterBefore(new TrailingSlashRedirectFilter(), DisableEncodeUrlFilter.class)

        //...


        .build();
    }