一堆废话
事出有因, 原先上线的接口现在被要求用Java重写,按照原暴露出去的文档然后毫无疑问的,按照Java的惯例,
一定是@RequestBody然后去接收application/json;charset=utf-8,然后一通参数接收处理逻辑。
结果测试都通过了,上线的时候,刚把原接口切到新接口上,日志就狂飙application/x-www-form-urlencoded;charset=utf-8 NOT SUPPORT
What?然后就一通问号脸。赶紧把接口切回到老接口,然后跑去问PHP的同事,什么情况,原对接参数不是json吗?
然后才明白,草,原来要同时支持application/json;charset=utf-8和application/x-www-form-urlencoded;charset=utf-8
PHP咱是不懂的,但是Java对这个需求的原生支持却不是很好,印象中没有现成的。
因为一般我们定义对象接收参数,如果使用了@RequestBody接收,那么传参一定要使用post+一个对应的参数解析器一个可读的流,按照现在的情况即application/json;charset=utf-8。
要么是直接一个对象接收,不要加任何注解,这个时候对应的Content-Type是application/x-www-form-urlencoded;charset=utf-8则参数可正常解析。
但是这两种情况是矛盾的,如果一个加了@RequestBody的参数对应的Content-Type是application/x-www-form-urlencoded;charset=utf-8, 则最终无法解析。反过来如果一个未加@RequestBody的参数对应的Content-Type是application/json;charset=utf-8则也无法解析。
那么现在就只能来看一下如何定义一个自定义的参数解析器来完成这个需求了。但是这个自定义的参数解析器还有点不太一样,因为数据格式本身不是我们自定义的,本身就存在对应标准的解析器。只是SpringMVC在根据参数去找对应解析器的时候没有对应起来。我们现在只要让自己的解析器能够让这个参数转发到对应可以解析的参数解析器上就可以了。
探究Springmvc参数解析器工作流程
现在就要不怕麻烦的还看一下原来的参数解析器是如何工作的,毕竟不知道它怎么写的我也不知道怎么抄。
SpringMVC项目,二话不说,直接找到org.springframework.web.servlet.DispatcherServlet#doDispatch这个方法,看整个处理器的流程,这里直接简化找到最终映射到方法后的执行
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
方法跳转流程如下
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
}
我再跳org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
/**
* 参数解析器列表
*/
@Nullable
private HandlerMethodArgumentResolverComposite argumentResolvers;
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// 这里删除了大量相关判断方法,只关注实际跳转执行方法
// No synchronization on session demanded at all...
mav = invokeHandlerMethod(request, response, handlerMethod);
}
/**
* 上面跳到了这里
*/
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 这里又删除了大量的代码
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 将本类自己的参数解析器列表赋值给ServletInvocableHandlerMethod
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 带着使命继续往下执行
invocableMethod.invokeAndHandle(webRequest, mavContainer);
}
}
接着跳到了org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod#invokeAndHandle
public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 再删除无关代码
}
}
这里总算看到了一些关键信息了org.springframework.web.method.support.InvocableHandlerMethod#invokeForRequest
public class InvocableHandlerMethod extends HandlerMethod {
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 这里就是解析参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return doInvoke(args);
}
/**
* 上面方法跳到了这里
*/
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 这里就是获取入参的参数类型,如单个字符串的每个参数,或者是某个对象参数,甚至是HttpServletRequest
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}

本文深入探讨SpringMVC的参数解析流程,解决同时支持application/json和application/x-www-form-urlencoded的问题,通过自定义参数解析器实现灵活的参数处理。
最低0.47元/天 解锁文章
8523

被折叠的 条评论
为什么被折叠?



