When I want to custom one argument Resolver in : WebMvcConfigurerAdapter#addArgumentResolvers,and try to override parameter type of ServletRequest ; but I found it that caught low priority after ServletRequestMethodArgumentResolver (same parameter type of ServletRequest) ; review source RequestMappingHandlerAdapter#getDefaultArgumentResolvers discover below :
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
I think that custom arguments resolver should be before the default argument ,so that can get highly priority to handle my logic; Thanks .
Comment From: bclozel
The Javadoc is addressing that bit:
Custom argument resolvers are invoked before built-in resolvers except for those that rely on the presence of annotations (e.g. @RequestParameter, @PathVariable, etc). The latter can be customized by configuring the RequestMappingHandlerAdapter directly.
I don't know if we should update the doc or slightly change the registration order. Before doing any of that, I'd like to know what type of argument resolver you're registering and to what types it's supposed to react to. This would help us figure out how specific it is and if it should be done via the configurer or the handler adapter directly.
Could you provide some background? (what are you trying to achieve, a code snippet showing your custom resolver). Thanks!
Comment From: victorkevin2
I have a subclass of javax.servlet.http.HttpServletRequestWrapper ( class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest ) ,see below :
public class YJJHttpRequest extends HttpRequestWrapper {
public YJJHttpRequest(HttpServletRequest request) {
super(request);
}
public KdUser getKdUser() throws ValidateTokenException {
.....
return kdUser;
}
there is a HandlerMethodArgumentResolver Implementation class ( register it in WebMvcConfigurerAdapter#addArgumentResolvers ) target is to translate HttpServletRequest instance to YJJHttpRequest type like this :
public class HttpRequestHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver,{
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return (AdminHttpRequest.class.isAssignableFrom(paramType) ||
YJJHttpRequest .class.isAssignableFrom(paramType)
|| CommonHttpRequest.class.isAssignableFrom(paramType));
}
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
Class<?> paramType = parameter.getParameterType();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (AdminHttpRequest.class.isAssignableFrom(paramType)) {
return new AdminHttpRequest(request);
} else if (YJJHttpRequest.class.isAssignableFrom(paramType)) {
return new YJJHttpRequest(request);
} else {
return new CommonHttpRequest(request);
}
}
finally I want to define this parameter type in Controller's method like this
@RequestMapping(value = "cancelFavorPriFund")
public void cancelFavorPriFund(YJJHttpRequest request, HttpResponseWrapper response) {
KdUser kdUser = request.getKdUser();
JSONArray crmCodeArray = request.getJSONArrayQuietly("crmCode");
.....
response.outputSuccess();
}
In fact wil caught a exception :
java.lang.IllegalStateException: Current request is not of type
[com.keynes.newlife.common.request.YJJHttpRequest ] org.springframework.mock.web.MockHttpServletRequest
,I found that ServletRequestMethodArgumentResolver have a high priority to handle it ,and thus mine HttpRequestHandlerMethodArgumentResolver have no chance to executed ,so throwed this exception from ServletRequestMethodArgumentResolver ,because ServletRequestMethodArgumentResolver and HttpRequestHandlerMethodArgumentResolver supports the same Parameter Type .
in other words: when default Argument Resolvers and custom Argument Resolvers supports same parameter type , custom argument resolver always no chance to executed . Thanks!
The Javadoc is addressing that bit:
Custom argument resolvers are invoked before built-in resolvers except for those that rely on the presence of annotations (e.g. @RequestParameter, @PathVariable, etc). The latter can be customized by configuring the RequestMappingHandlerAdapter directly.
I don't know if we should update the doc or slightly change the registration order. Before doing any of that, I'd like to know what type of argument resolver you're registering and to what types it's supposed to react to. This would help us figure out how specific it is and if it should be done via the configurer or the handler adapter directly.
Could you provide some background? (what are you trying to achieve, a code snippet showing your custom resolver). Thanks!
Comment From: rstoyanchev
Similar request and discussion under #21874. Changing the order isn't an option but we can check explicitly that the request is either ServletRequest
or HttpServletRequest
(and MultipartRequest
or MultipartHttpServletRequest
). Any other sub-class we wouldn't know how to resolve.
Comment From: victorkevin2
In the end, I found an workaround. But I don't think it's the best
public class HttpRequestHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver, InitializingBean {
@Autowired
private RequestMappingHandlerAdapter requestMappingHandlerAdapter;
public void afterPropertiesSet() throws Exception {
List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers();
List<HandlerMethodArgumentResolver> newArgumentResolvers = new LinkedList<>();
newArgumentResolvers.add(this);
newArgumentResolvers.addAll(argumentResolvers);
requestMappingHandlerAdapter.setArgumentResolvers(Collections.unmodifiableList(newArgumentResovers));
}
Comment From: rstoyanchev
Okay but just to be clear, the change I suggested in ServletRequestMethodArgumentResolver
would have made your custom resolver work with the current order, because the built-in resolver would pass on any concrete request type it does not recognize.
Comment From: lleming
A little bit more info in which cases this might be usefull. I use the same techinque(upper sample, with RequestMappingHandlerAdapter
) , only with HandlerMethodReturnValueHandler
, and face same issue with ordering. There is an legacy application containing something similar to ResponseEntity
, it represents payload or error, includes operation result status and it is sent via different transport (rpc call ?). At first I tried to use spring MessageWriter
but it can't change http status only the way how to serialize entity. That is why I have to use HandlerMethodReturnValueHandler
.
Also I don't like to do manual or copy/paste, just want to introduce mapper between my custom object and ResponseEntity
. Spring already contains HttpEntityMethodProcessor
to handle ResponseEntity
type. I want to reuse it and I just need to replace it with my own, and apply mapping before returned value passed to original handler.
MockMvc will still suffer from same ordering problem even if initialized as
@Bean
MockMvc mockMvc(SampleController sampleController, CustomResponseReturnValueHandler customResponseReturnValueHandler) {
return MockMvcBuilders.standaloneSetup(sampleController)
.setCustomReturnValueHandlers(customResponseReturnValueHandler)
.build();
}
I may miss something, it sounds like common problem but I didn't find builtin/existing solution. What could be done in such case is ability to change order or to intoduce customization of HttpEntityMethodProcessor
instead of hardcoding processing of ResponseEntity
.