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?
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();
    }
}
                        There is aleardy a feature request Spring backlog, checkout JIRA. However, i was able to achieve what you are trying using @Validated annotation on Controller.
@RestController
@RequestMapping("/user")
@Validated
public class UserController {
   @GetMapping("/{loginId}")
   public User getUserBy(@PathVariable @LoginID final String loginId) {
      // return some user
   }
}
Here @LoginID is custom validator. And @Validated is from org.springframework.validation.annotation.Validated which does the trick.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With