Spring boot PathVariable not passed getting 404 URL doesn't exists

188 views Asked by At

While creating a GET endpoint in a Spring web application I have a path variable(annotated with @PathVariable) passed to my API.

If I don't pass any value on path, the controller responds with HTTP 404 status.

Endpoint: http://localhost:8080/observability-core/v1/systems/

If I pass a value it responds as expected (e.g http://localhost:8080/observability-core/v1/systems/123)

If the path variable is missing on the request, I want to throw HTTP 400 Bad Request to indicate something like is not passed.

PS: - when I hit request with above url I am not getting any logs implying no request reached the application. That could mean endpoint doesn't exists, which is misleading. How do I customise the error response in this case?

Default response from Spring controller endpoint:

{
    "timestamp": "2024-01-04T06:48:18.584+00:00",
    "status": 404,
    "error": "Not Found",
    "path": "/observability-core/v1/systems/"
}
2

There are 2 answers

2
Tamas Csizmadia On BEST ANSWER

My proposed solution:

  • Specify the path with and without the parameter: @GetMapping(value = { "/path", "/path/{param}" })
  • Mark the Path variable as required = false (@PathVariable(required = false) String param
  • handle the situation when parameter is missing and respond as required (with HTTP Status 400 in your case)

Minimalistic Example

@RestController
@RequestMapping("/foo")
public class FooController {

    @GetMapping(value = { "/{firstParam}", "/" })
    public ResponseEntity<String> getFoo(@PathVariable(required = false) String firstParam) {
        if (null == firstParam) {
            return ResponseEntity.badRequest().body("The first param is required");
        }
        return ResponseEntity.ok("The first param is: " + firstParam);
    }
}

A Test case:

@SpringBootTest
@AutoConfigureMockMvc
class FooControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void whenParameterIsPassesShouldReturnOk() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/foo/param"))
                .andExpect(status().isOk())
                .andExpect(content().string("The first param is: param"));
    }

    @Test
    void whenParameterIsNotPassedShouldReturnBadRequest() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/foo/"))
                .andExpect(status().isBadRequest())
                .andExpect(content().string("The first param is required"));
    }

}
2
Arslan aka On

You can get it done by following this approach.

 @RequestMapping("/observability-core/v1/systems")
 public class YourController {

    @GetMapping("/{id}")
    public ResponseEntity<String> getSystemDetails(@PathVariable(required = false) String id) {
        if (id == null) {
            // Handle the case where the path variable is not provided
            return new ResponseEntity<>("ID not provided in the request.", HttpStatus.BAD_REQUEST);
        }

        // Your existing logic when the ID is provided
        return new ResponseEntity<>("System details for ID: " + id, HttpStatus.OK);
    }