Custom validation logic for parameter in REST endpoint Spring Boot

ptimson picture ptimson · Jul 29, 2016 · Viewed 30.9k times · Source

I currently have this RequestMapping where I use validation through a regular expression:

 @RequestMapping(value = "/example/{id}", method = GET)
 public Response getExample(
         @PathVariable("id") String id,
         @RequestParam(value = "myParam", required = true) @Valid @Pattern(regexp = MY_REGEX) String myParamRequest,
         @RequestParam(value = "callback", required = false) String callback,
         @RequestHeader(value = "X-API-Key", required = true) @Valid @Pattern(regexp = SEGMENTS_REGEX) String apiKeyHeader) {

     // Stuff here...
 }

However, the regulare expression is not enough. Instead, I would like to do some custom validation on the header attribute i.e.

if (!API_KEY_LIST.contains(apiKeyHeader)) {
    throw Exception();
}

Is this possible?

Answer

Ulises picture Ulises · Jul 30, 2016

Best way to do this IMO is to create a custom HandlerMethodArgumentResolver that would look something like this using a custom annotation @Segment:

public class SegmentHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().equals(String.class)
            && parameter.getParameterAnnotation(Segment.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        String apiKey = webRequest.getHeader("X-API-Key");
        if (apiKey != null) {
            if (!API_KEY_LIST.contains(apiKey)) {
                throw new InvalidApiKeyException();
            }
            return apiKey;
        } else {
            return WebArgumentResolver.UNRESOLVED;
        }
    }
}

Then your controller signature looks like this:

@RequestMapping(value = "/example/{id}", method = GET)
 public Response getExample(
         @PathVariable("id") String id,
         @RequestParam(value = "myParam", required = true) @Valid @Pattern(regexp = MY_REGEX) String myParamRequest,
         @RequestParam(value = "callback", required = false) String callback,
         @Segment String apiKeyHeader) {

     // Stuff here...
 }

You'll register the handler method argument resolver in you WebMvcConfigurationAdapter:

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(segmentHandler());
    }

    @Bean
    public SegmentHandlerMethodArgumentResolver segmentHandler() {
        return new SegmentHandlerMethodArgumentResolver();
    }

}