目录
application/x-www-form-urlencoded;charset=utf-8 NOT SUPPORTWhat?然后就一通问号脸。赶紧把接口切回到老接口,然后跑去问PHP的同事,什么情况,原对接参数不是json吗?
// 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 AbstractHandlerMethodAdapterimplements 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 = https://www.it610.com/article/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 {// 这里就是获取入参的参数类型,如单个字符串的每个参数,或者是某个对象参数,甚至是HttpServletRequestMethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS; }Object[] args = new Object[parameters.length]; // 下面就是遍历每个参数类型,然后挨个解析for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); args[i] = findProvidedArgument(parameter, providedArgs); if (args[i] != null) {continue; }// org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#supportsParameter// 后面就贴出了这个方法的内部,就是遍历所有的参数解析器判断是否能够解析当前参数if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); }try {// 获取参数解析器,解析当前参数args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); }catch (Exception ex) {// Leave stack trace for later, exception may actually be resolved and handled...if (logger.isDebugEnabled()) {String exMsg = ex.getMessage(); if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {logger.debug(formatArgumentError(parameter, exMsg)); }}throw ex; }}return args; }}
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {private final List argumentResolvers = new LinkedList<>(); private final MapargumentResolverCache =new ConcurrentHashMap<>(256); /*** 判断参数是否能够找到对应的参数解析器*/ @Override public boolean supportsParameter(MethodParameter parameter) {return getArgumentResolver(parameter) != null; } @Nullable private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {// 这里指向一个本地缓存,如果一个参数类型可以被某个参数解析器解析,则缓存下次无须遍历HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); if (result == null) {// 遍历所有的参数解析器,判断是否支持当前参数类型for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {if (resolver.supportsParameter(parameter)) {result = resolver; // 如果支持则放入本地缓存,下次直接从缓存中取this.argumentResolverCache.put(parameter, result); break; }}}return result; } @Override @Nullable public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {// 获取该参数类型对应的参数解析器HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); if (resolver == null) {throw new IllegalArgumentException("Unsupported parameter type [" +parameter.getParameterType().getName() + "]. supportsParameter should be called first."); }// 解析参数return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); } }
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#getArgumentResolver
application/x-www-form-urlencoded对应的解析器为ServletModelAttributeMethodProcessor@RequestBody application/json对应的解析器为RequestResponseBodyMethodProcessorpublic class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor { @Override public boolean supportsParameter(MethodParameter parameter) {// 可以看到必须要有RequestBody这个注解,该参数解析器才会工作return parameter.hasParameterAnnotation(RequestBody.class); }}
org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolverorg.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver
import java.lang.annotation.*; /** * 标识参数可以被多个参数解析器尝试进行参数解析 * * 同一个参数支持application/json和application/x-www-form-urlencoded * * @see com.company.content.risk.order.common.handle.MultiArgumentResolverMethodProcessor * @author Snowball * @version 1.0 * @date 2020/08/31 18:57 */@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface MultiArgumentResolver {}
import com.company.content.risk.order.common.annotation.MultiArgumentResolver; import com.google.common.collect.ImmutableList; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor; import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * 自定义参数解析器用以支持同一个参数支持application/json和application/x-www-form-urlencoded解析 * * @see MultiArgumentResolver * @author Snowball * @version 1.0 * @date 2020/08/31 19:00 */public class MultiArgumentResolverMethodProcessor implements HandlerMethodArgumentResolver{@Autowiredprivate RequestMappingHandlerAdapter requestMappingHandlerAdapter; private static final String CONTENT_TYPE_JSON = "application/json"; private static final String CONTENT_TYPE_FORM_URLENCODED = "application/x-www-form-urlencoded"; /*** 支持的content_type*/private static final ImmutableList SUPPORT_CONTENT_TYPE_LIST = ImmutableList.of(CONTENT_TYPE_JSON, CONTENT_TYPE_FORM_URLENCODED); /*** 参考这个写法, 同一个类型的参数解析后缓存对应的参数解析器,不过这里的key改为了Content-Type* @see HandlerMethodArgumentResolverComposite#argumentResolverCache*/private final Map argumentResolverCache =new ConcurrentHashMap<>(8); @Overridepublic boolean supportsParameter(MethodParameter parameter) {return parameter.getParameter().isAnnotationPresent(MultiArgumentResolver.class); }/*** 解析参数*/@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {String contentType = webRequest.getHeader("Content-Type"); isSupport(contentType); List argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers(); HandlerMethodArgumentResolver handlerMethodArgumentResolver = argumentResolverCache.get(contentType); if (handlerMethodArgumentResolver != null) {return handlerMethodArgumentResolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); }for (HandlerMethodArgumentResolver argumentResolver : argumentResolvers) {if (isJson(contentType) && argumentResolver instanceof RequestResponseBodyMethodProcessor) {argumentResolverCache.put(contentType, argumentResolver); return argumentResolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); } else if (isFormUrlEncoded(contentType) && argumentResolver instanceof ServletModelAttributeMethodProcessor) {argumentResolverCache.put(contentType, argumentResolver); return argumentResolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); }}return null; }private boolean isJson(String contentType) {return contentType.contains(CONTENT_TYPE_JSON); }private boolean isFormUrlEncoded(String contentType) {return contentType.contains(CONTENT_TYPE_FORM_URLENCODED); }/*** 判断当前参数解析器是否支持解析当前的Content-Type* @param contentType* @return* @throws HttpMediaTypeNotSupportedException*/private boolean isSupport(String contentType) throws HttpMediaTypeNotSupportedException {if (contentType == null) {throw new HttpMediaTypeNotSupportedException("contentType不能为空"); }boolean isMatch = false; for (String item : SUPPORT_CONTENT_TYPE_LIST) {if (contentType.contains(item)) {isMatch = true; break; }}if (!isMatch) {throw new HttpMediaTypeNotSupportedException("支持Content-Type" + SUPPORT_CONTENT_TYPE_LIST.toString()); }return true; }
import com.company.content.risk.order.common.handle.MultiArgumentResolverMethodProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; /** * web核心配置 * * @author Snowball * @version 1.0 * @date 2020/08/31 18:57 */@Configuration@Orderpublic class CoreWebConfig implements WebMvcConfigurer {/*** 注册自定义参数解析器* @return*/@Beanpublic MultiArgumentResolverMethodProcessor multiArgumentResolverMethodProcessor() {return new MultiArgumentResolverMethodProcessor(); }/*** 添加自定义参数解析器* @param resolvers*/@Overridepublic void addArgumentResolvers(List resolvers) {resolvers.add(0, multiArgumentResolverMethodProcessor()); }}
@PostMapping(value = "https://www.it610.com/article/text/submit")public OutApiResponsesubmitText(@MultiArgumentResolver OutTextRequest outTextRequest) {}
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapterimplements BeanFactoryAware, InitializingBean { @Nullable private HandlerMethodArgumentResolverComposite argumentResolvers; @Override public void afterPropertiesSet() {// Do this first, it may add ResponseBody advice beansinitControllerAdviceCache(); if (this.argumentResolvers == null) {List resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } } private List getDefaultArgumentResolvers() {List resolvers = new ArrayList<>(); // Annotation-based argument resolutionresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new PathVariableMethodArgumentResolver()); resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new MatrixVariableMethodArgumentResolver()); resolvers.add(new MatrixVariableMapMethodArgumentResolver()); resolvers.add(new ServletModelAttributeMethodProcessor(false)); resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); resolvers.add(new RequestHeaderMapMethodArgumentResolver()); resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new SessionAttributeMethodArgumentResolver()); resolvers.add(new RequestAttributeMethodArgumentResolver()); // Type-based argument resolutionresolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); 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 argumentsif (getCustomArgumentResolvers() != null) {resolvers.addAll(getCustomArgumentResolvers()); }// Catch-allresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; }}