springspring-bootkotlinrest-assuredget-mapping

Handling optional parameters for @GetMapping("") in Spring fails RestAssured tests


I have a Spring Boot application where I am using @RequestMapping at the class level and @GetMapping at the method level. Here is a highly simplified version of my controller:

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

    @GetMapping("")
    fun test(@RequestParam("name", required = false) name: String?): String {
        return "hello " + (name ?: "world")
    }
}

When I call /hello without name, it works fine and returns "hello world".

However, when I call /hello?name=banana, it does not work as expected, giving me the following error:

No static resource hello.

I have tried using @GetMapping("/") instead of @GetMapping(""), but that does not allow me to call /hello without adding the optional parameters (i.e. works with ?name=batman). Is there a way to handle both cases (with and without the request parameter) correctly in Spring Boot?

Additional info: This is a regression failure, from an upgrade of Java from 17 to 21 and Spring Boot from 2.7.5 to 3.2.5. This used to work before this upgrade.

I have noted that the Spring Boot instructions state that

@RequestMapping cannot be used in conjunction with other @RequestMapping annotations that are declared on the same element (class, interface, or method). If multiple @RequestMapping annotations are detected on the same element, a warning will be logged, and only the first mapping will be used. This also applies to composed @RequestMapping annotations such as @GetMapping, @PostMapping, etc

But I am not sure how to avoid it for my use-case


Solution

  • After some investigation I have found that the issue was not present in other places than my tests using RestAssured with an empty endpoint.

    These were my tests originally

    RestAssured.given().basePath("myapi/hello").`when`().get("") //<- succeeded
    RestAssured.given().basePath("myapi/hello").`when`().get("?name=banana") //<- failed
    

    After changing the tests this is the result

    RestAssured.given().basePath("myapi").`when`().get("/hello") //<- succeeded
    RestAssured.given().basePath("myapi").`when`().get("/hello?name=banana") //<- succeeded
    

    Notably, this worked before upgrading to RestAssured 5.3.2, but not after