0

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
  • You are getting this from spring itself, for bad request it should be a valid request first of all , what this error says is , the API you are invoking itself could not be found by Spring , hence you are getting 404 not found from dispatcher servlet Commented Jan 4, 2024 at 7:23
  • API consumer must know what endpoint he/she is consuming , for Bad request post hitting the correct end point, you must utilize spring annotations such as @Validated & @Valid Commented Jan 4, 2024 at 7:24

2 Answers 2

4

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

}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks @Tamas Csizmadia. your solution seems more logical and solves my issue. Still have one doubt How can I generalize this behaviour for all of endpoints within my application having pathvariables. Can we think of any general handler to all this validations.
Dear @NitinGangwar - to be honest I rather handle these cases in a Service component and keep the Controllers as thin as possible. You can properly plan and design your Service classes with composition to have a common logic to handle parameters passed to your Services.
0

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

2 Comments

If you don't specify the mapping without the variable notation, it will still respond with HTTP 404 (which is the expected behaviour from Spring's side, IMHO)
Hey Arslan, In this case request will never reach endpoint as spring treats it as a different url. Below tamas solution has extra check of empty url using that it works.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.