riorio
riorio

Reputation: 6826

Rejecting GET requests with additional query params

I have an HTTP GET endpoint that accepts some query parameters:

@GetMapping("/cat")
public ResponseEntity<Cat> getCat(@RequestParam("catName") String catName){ ...

If the clients will send additional query parameters, the endpoint will ignore them.

GET .../cat?catName=Oscar                Getting Oscar
GET .../cat?catName=Oscar&gender=male    Getting Oscar
GET .../cat?catName=Oscar&x=y            Getting Oscar

I want to reject HTTP requests that will send additional query parameters:

GET .../cat?catName=Oscar                OK
GET .../cat?catName=Oscar&gender=male    Reject (HTTP error code XYZ)
GET .../cat?catName=Oscar&x=y            Reject (HTTP error code XYZ)

I can change the signature of the method to accept a map and validate the values in the map as suggested here.

Is there a way to do with while keeping the cleaner and self explained method signature?

Upvotes: 4

Views: 1896

Answers (1)

Ken Chan
Ken Chan

Reputation: 90497

You can try to implement a HandlerInterceptor and validate such rule in preHandle(). If the request contains the query parameters that does not defined in the controller method , you just throw a specific type of Exception and configure a @ControllerAdvice to handle this exception. Something like :

public class FooHandlerInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        if (handler instanceof HandlerMethod) {
            HandlerMethod hm = (HandlerMethod) handler;

            Set<String> allowQueryParams = Stream.of(hm.getMethodParameters())
                    .map(p -> p.getParameterAnnotation(RequestParam.class))
                    .map(req -> req.value())
                    .collect(toSet());

            for (String currentRequestParamName : request.getParameterMap().keySet()) {
                if (!allowQueryParams.contains(currentRequestParamName)) {
                    throw new FooRestException();
                }
            }
        }

        return true;
    }
}

And the @ControllerAdvice to handle the Exception :

@ControllerAdvice
public class FooExceptionHandler {

    @ExceptionHandler(FooRestException.class)
    public ResponseEntity<Object> handle(FooRestException ex) {
        return new ResponseEntity<>("Some query parameter are not defined", HttpStatus.BAD_REQUEST);
    }
}

Finally register FooHandlerInterceptor to use it :

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new FooHandlerInterceptor());
    }
} 

I just show you the idea. You can further tweak the codes in HandlerInterceptor if you want such checking is only applied to a particular controller method.

Upvotes: 1

Related Questions