1

I am extending the HandlerMethodArgumentResolver to get hold of a parameter annotated by a custom annotation

@RequestMapping(value = "/cases/{caseId}", params = "meta",
                method = PUT, produces = APPLICATION_JSON_VALUE)
@ResponseBody
@ResponseStatus(HttpStatus.OK)
public String updateUIMetadata(
    @RequestBody
    @JsonData(schemaLocation = "schema/metadata_schema.json")
    final String metadataJson) {
}

I want to get hold of the value in my string metadataJson in my class, specifically in the resolveArgument method. I know it has a MethodParameter parameter, but is it possible to get hold of the actual value of the parameter which is passed along with the web request?

public class UpdateMetadataInterceptor implements HandlerMethodArgumentResolver {


@Override
public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(JsonData.class);
}

@Override
public String resolveArgument(MethodParameter parameter,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
        WebDataBinderFactory binderFactory) throws Exception {
    System.out.println("Inside UpdateMetadata");
    // TODO something with metadataJson

}
}
Jin Kwon
  • 20,295
  • 14
  • 115
  • 184
rrrocky
  • 696
  • 1
  • 11
  • 27
  • 2
    The purpose of `resolveArgument` is to **provide** the value that will be bound to the parameter. Spring will call your `UpdateMetadataInterceptor#resolverArgument`, gets its return value, and invoke `updateUIMetadata` with it. – Sotirios Delimanolis Aug 21 '15 at 05:04
  • 1
    In this case, it doesn't make sense to annotate your parameter with both `@RequestBody` and `@JsonData`. Spring will use only one `HandlerMethodArgumentResolver` (the one for `@RequestBody`) to produce an argument. – Sotirios Delimanolis Aug 21 '15 at 05:05
  • 1
    [Read this.](http://stackoverflow.com/questions/18944627/form-submit-in-spring-mvc-3-explanation/18944736#18944736) – Sotirios Delimanolis Aug 21 '15 at 05:09
  • Thanks for the explanation. So what I want to do is intercept the method `updateUIMetadata`, extract the value of the parameter and do something with it before the method is invoked. And I want to do it without using aop. I guess I should use `HandlerInterceptor` then. Or is there a better way that you might be aware of? – rrrocky Aug 21 '15 at 06:24
  • Spring MVC doesn't provide a way to intercept the arguments used to invoke your handler method. AOP seems appropriate. – Sotirios Delimanolis Aug 21 '15 at 15:14
  • @SotiriosDelimanolis, I am still confused by your second comment. Can you please elaborate on it. – rrrocky Aug 24 '15 at 13:08
  • The arguments are resolved after the `HandlerInterceptor` is invoked so you don't get access to them. I think AOP is the only _simple_ way to do this at the moment. – Sotirios Delimanolis Aug 24 '15 at 14:33
  • But I have not used `HandlerInterceptor`. I am using `HandlerMethodArgumentResolver ` – rrrocky Aug 24 '15 at 16:01
  • Neither of those can be used to achieve your goal. – Sotirios Delimanolis Aug 24 '15 at 16:49

1 Answers1

1

HandlerMethodArgumentResolver is invoked only if Spring cannot resolve one or more of a method's arguments. In the above case, a simple String is annotated with @RequestBody. Hence, Spring will be able to resolve it from the web request. Thus HandlerMethodArgumentResolver will never get invoked. In order to achieve what I wanted, I followed the following strategy

  1. Remove the @RequestBody annotation and club it with the @JsonData annotation
  2. Now, since Spring does not know how to resolve metadataJson, it will invoke HandlerMethodArgumentResolver. Here, we need to implement the logic of @RequestBody to extract the string from the body of the web request. This I did as below:

    public class ValidateJsonSchema implements HandlerMethodArgumentResolver {
    
    @Bean
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
    return new RequestMappingHandlerAdapter();
    }
    private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = null;
    
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
    return parameter.hasParameterAnnotation(JsonData.class);
    }
    
    @Override
    public String resolveArgument(MethodParameter parameter,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
        WebDataBinderFactory binderFactory) throws Exception {
    String value = (String) getRequestResponseBodyMethodProcessor()
            .resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    // do something with value
    return value;
    
    }
    
    private RequestResponseBodyMethodProcessor getRequestResponseBodyMethodProcessor() {
    
    if (requestResponseBodyMethodProcessor == null) {
        List<HttpMessageConverter<?>> messageConverters = requestMappingHandlerAdapter().getMessageConverters();
        requestResponseBodyMethodProcessor = new RequestResponseBodyMethodProcessor(messageConverters);
    }
    return requestResponseBodyMethodProcessor;
    }
    }
    

I used RequestMappingHandlerAdapter to get the message converters and created a new instance of RequestResponseBodyMethodProcessor. Then I called the resolveArgument method of the same instance which returned the object, which I then cast to String

rrrocky
  • 696
  • 1
  • 11
  • 27