4

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?

Ken Chan
  • 84,777
  • 26
  • 143
  • 172
riorio
  • 6,500
  • 7
  • 47
  • 100
  • 1
    If you put in your controller method the annotation @RequestParam and the proper field, every request which have more or different param will rejected. I think you don't need do nothing except, if you want a custom exception you can create one and send a specific code error for that situation. – Adrian Lagartera Feb 01 '22 at 07:45
  • You can also create other methods with unacceptable params and return your custom error code in those methods. – behrad Feb 01 '22 at 09:58

1 Answers1

1

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.

Ken Chan
  • 84,777
  • 26
  • 143
  • 172
  • Tried following your approach but in line '.map(req -> req.value())' here "req" is always null even if there is a post method which accepts a @valid object. – VirtualNomad Jan 13 '23 at 09:51