zoukankan      html  css  js  c++  java
  • [踩坑] @RequestBody注解转换modal报400错误问题排查与解决

    背景

    最近在项目上开发接口时,controller接口选择用application/json方式接收参数,接口入参用了@RequestBody。简化controller和modal如下所示。

    @RestController("/jsonModal")
    public class JsonModalController {
    
        @PostMapping
        public Object json(@RequestBody JsonModal jsonModal) {
            return ResultAssistant.ok(jsonModal);
        }
    }

    public class JsonModal {

    private String id;

    private String name;

    private int status;

    public JsonModal(String id, String name, int status) {
    this.id = id;
    this.name = name;
    this.status = status;
    }

      ... 省略get,set
    }

    但是最终调试接口的时候总是报400状态码错误。

    排查与解决

    一开始以为是转换json字符串的格式不对,但后面用了fastJson进行json字符串转换后仍旧是400错误。

    既然不是json字符串的问题,那就换个思路排查,首先需要找到具体的Exception。通过在项目的GlobalExceptionHandler的排查最终确定了此次异常为HttpMessageNotReadableException。

    那么错误找到了,就开始分析为什么会出现这个错误,从该错误的注解来看,这个错误主要是在消息转换的出现错误的时候抛出的。

    /**
     * Thrown by {@link HttpMessageConverter} implementations when the
     * {@link HttpMessageConverter#read} method fails.
     *
     * @author Arjen Poutsma
     * @since 3.0
     */
    @SuppressWarnings("serial")
    public class HttpMessageNotReadableException extends HttpMessageConversionException

    那么接下来的事情自然就是打断点,跟踪排查了为什么在转换的时候会出现这个不可读的异常。通过断点追踪最后在jackson下的一个类解析器BeanDeserializerBase中发现如下的消息。

        protected Object deserializeFromObjectUsingNonDefault(JsonParser p,
                DeserializationContext ctxt) throws IOException
        {
            final JsonDeserializer<Object> delegateDeser = _delegateDeserializer();
            if (delegateDeser != null) {
                return _valueInstantiator.createUsingDelegate(ctxt,
                        delegateDeser.deserialize(p, ctxt));
            }
            if (_propertyBasedCreator != null) {
                return _deserializeUsingPropertyBased(p, ctxt);
            }
            // should only occur for abstract types...
            if (_beanType.isAbstract()) {
                return ctxt.handleMissingInstantiator(handledType(), p,
                        "abstract type (need to add/enable type information?)");
            }
            // 25-Jan-2017, tatu: We do not actually support use of Creators for non-static
            //   inner classes -- with one and only one exception; that of default constructor!
            //   -- so let's indicate it
            Class<?> raw = _beanType.getRawClass();
            if (ClassUtil.isNonStaticInnerClass(raw)) {
                return ctxt.handleMissingInstantiator(raw, p,
    "can only instantiate non-static inner class by using default, no-argument constructor");
            }
            return ctxt.handleMissingInstantiator(raw, p,
    "no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)");
        }

    这个方法主要就是反序列化一个没有默认构造函数的bean,注意到后面注释之后的东西其实就已经说明了本次的问题了。

    We do not actually support use of Creators for non-static inner classes -- with one and only one exception; that of default constructor! -- so let's indicate it

    所以其实使用@RequestBody注解的参数其实是不支持使用静态内部class或者是一个没有默认构造函数的modal。

    回到我自身使用的问题,可以看到我的modal是只有一个全参数构造函数而没有默认空构造函数,所以解决方案就是加一个空构造函数,问题即可解决。

    总结

    当使用@RequestBody注解入参的时候,记得注解后面紧跟的参数不要是一个static inner class或者是一个缺少空构造函数的class

  • 相关阅读:
    【CodeForces】[366A]Dima and Guards
    Linux将程序前台转后台执行
    Storm在Mesos上跑与不在的运行命令区别
    去掉SSH KEY密码
    git 退回到倒数第二次提交
    浅析UGC、PGC和OGC
    linux主机名与IP地址配置文件
    wget ERROR 404: Not Found.
    cpuacct (CPU Accounting Controller)
    Ansible的安装与使用
  • 原文地址:https://www.cnblogs.com/magotzis/p/9605374.html
Copyright © 2011-2022 走看看