Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom validation logic for parameter in REST endpoint Spring Boot

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?

like image 835
ptimson Avatar asked Jul 29 '16 14:07

ptimson


2 Answers

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

}
like image 71
Ulises Avatar answered Oct 11 '22 07:10

Ulises


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.

like image 30
Sarvesh Dubey Avatar answered Oct 11 '22 07:10

Sarvesh Dubey