spring4 版本post接口传List<Object>类型参数问题

1、需求

通过POST接口传List<Object> 类型的参数,由于项目比较老,使用的ssh框架,spring的版本是4.0.9,这种需求在springboot里面很好实现,可是在老版本的ssh里面就遇到了问题。

2、问题排查

1、首先接口信息如下:

@RequestMapping(value = "/receiveHiddens", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public ApiResultJson receiveHiddens(@RequestBody  List<TSBaseUser> users){
        ApiResultJson resultJson = new ApiResultJson();
        System.out.println(users);
        return resultJson;
    }

2、postman测试


image.png

测试报错结果如下

org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported
    at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.readWithMessageConverters(HandlerMethodInvoker.java:645)
    at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveRequestBody(HandlerMethodInvoker.java:604)
    at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:353)
    at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:170)
    at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:446)
    at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:434)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:660)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.jeecgframework.core.interceptors.CrosFilter.doFilter(CrosFilter.java:31)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.jeecgframework.core.aop.GZipFilter.doFilter(GZipFilter.java:88)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:140)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.orm.hibernate4.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:150)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:651)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:409)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:754)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1376)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

3、debug问题查找
由于是ssh项目,接口肯定会走DispatchServlet里面的doDispatch()方法,在这个方法里面一直走下去。


image.png

在HandlerMethodInvoker.readWithMessageConverters 方法里面报错了。具体代码如下。
在messageConverter.canRead(paramType, contentType)方法里面,继续往下找

List<MediaType> allSupportedMediaTypes = new ArrayList();
            if (this.messageConverters != null) {
                HttpMessageConverter[] var6 = this.messageConverters;
                int var7 = var6.length;

                for(int var8 = 0; var8 < var7; ++var8) {
                    HttpMessageConverter<?> messageConverter = var6[var8];
                    allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
                    if (messageConverter.canRead(paramType, contentType)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType + "\" using [" + messageConverter + "]");
                        }

                        return messageConverter.read(paramType, inputMessage);
                    }
                }
            }
public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
        JavaType javaType = this.getJavaType(type, contextClass);
        return this.objectMapper.canDeserialize(javaType) && this.canRead(mediaType);
    }
//上面的 this.canRead(mediaType)是在AbstractHttpMessageConverter里面canRead()方法
protected boolean canRead(MediaType mediaType) {
        if (mediaType == null) {
            return true;
        } else {
            Iterator var2 = this.getSupportedMediaTypes().iterator();

            MediaType supportedMediaType;
            do {
                //就是在这里,getSupportedMediaTypes()方法里没有找到Content type为 'application/json;charset=UTF-8'的,所以报错
                if (!var2.hasNext()) {
                    return false;
                }

                supportedMediaType = (MediaType)var2.next();
            } while(!supportedMediaType.includes(mediaType));

            return true;
        }
    }

3、分析过程
Content type为 'application/json;charset=UTF-8'是最常见的类型,其它接口都正常,为什么就这个接口不行,后来查很多 blog发现是因为没有配置解析List<Object>类型参数的解析器,所以HandlerMethodInvoker无法解析方法参数,最后找到了解决方法,在xml配置文件里面添加如下配置,里面重要的就是Jaxb2RootElementHttpMessageConverter和MappingJackson2HttpMessageConverter两个messageConverter,这样才能正常解析List类型参数。

<!-- 定义注解驱动Controller方法处理适配器 ,注:该适配器必须声明在<mvc:annotation-driven />之前,否则不能正常处理参数类型的转换 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter" />
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
            </list>
        </property>
    </bean>

    <!-- 会自动注册RequestMappingHandlerMapping与RequestMappingHandlerAdapter
        两个bean,是spring MVC为@Controllers分发请求所必须的。 并提供了:数据绑定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,读写XML的支持(JAXB),读写JSON的支持(Jackson) -->
    <mvc:annotation-driven />

4、postman测试结果


image.png
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容