• Vert.x web 接收请求时反序列化对象 Failed to decode 如何解决?


    最近在尝试用 Vert.x 进行 web 开发,发现接口在接收请求体后,反序列化报错,跟进一下原因。

    解决方案

    引入 jackson-databind
    我是用的是 Vert.x 4.3.1,所以引入 jackson-databind 2.13.2.* 版本:

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.2.2</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    排查过程

    构造接口

    编写一个 Post 接口,将请求体转换为一个对象 NewOrderRequest(对象是个 DTO,只有一些基本的字段定义):

    import io.vertx.core.Future;
    import io.vertx.core.Vertx;
    import io.vertx.core.json.JsonObject;
    import io.vertx.ext.web.Router;
    import io.vertx.ext.web.RoutingContext;
    import io.vertx.ext.web.handler.BodyHandler;
    import io.vertx.sqlclient.Pool;
    
    public class OrderRestAPI {
        
        private final OrderService orderService;
    
        public OrderRestAPI(Vertx vertx, Router router, Pool pool, JsonObject orderConfig) {
            orderService = new OrderService(vertx, pool, orderConfig);
            router.post("/order").handler(BodyHandler.create().setBodyLimit(256)).respond(this::newOrder);
        }
    
        private Future<ResponseDTO<NewOrderResponse>> newOrder(RoutingContext ctx) {
            NewOrderRequest newOrderRequest = ctx.body().asPojo(NewOrderRequest.class);
            return orderService.newOrder(newOrderRequest).map(result -> new ResponseDTO<>("OK", result));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    发现问题

    但是在反序列化的时候报错 Failed to decode

    22:25:50.048 [vert.x-eventloop-thread-1] ERROR io.vertx.ext.web.RoutingContext - Unhandled exception in router
    io.vertx.core.json.DecodeException: Failed to decode
    	at io.vertx.core.json.jackson.JacksonCodec.cast(JacksonCodec.java:350) ~[vertx-core-4.3.1.jar:4.3.1]
    	at io.vertx.core.json.jackson.JacksonCodec.fromParser(JacksonCodec.java:187) ~[vertx-core-4.3.1.jar:4.3.1]
    	at io.vertx.core.json.jackson.JacksonCodec.fromBuffer(JacksonCodec.java:74) ~[vertx-core-4.3.1.jar:4.3.1]
    	at io.vertx.core.json.Json.decodeValue(Json.java:119) ~[vertx-core-4.3.1.jar:4.3.1]
    	at io.vertx.ext.web.impl.RequestBodyImpl.asPojo(RequestBodyImpl.java:126) ~[vertx-web-4.3.1.jar:4.3.1]
    	at io.vertx.ext.web.RequestBody.asPojo(RequestBody.java:115) ~[vertx-web-4.3.1.jar:4.3.1]
    	at icu.wwj.web.handler.OrderRestAPI.newOrder(OrderRestAPI.java:25) ~[classes/:?]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    跟进代码

    查看 Vert.x JacksonCodec 发现,请求体被正常反序列化为了 LinkedHashMap,但是代码中判断,如果反序列化的目标不是 Map 或着 Map 的派生,就抛出 Failed to decode 的错误:
    在这里插入图片描述

    看到上面的代码首先感到的是奇怪:

    • 为什么非常简单的对象反序列化也无法进行?
    • 是不是我是用 asPojo 的方式不对?

    往上层看了下,Vert.x 不出所料地使用了流行的 Jackson,但是 JsonCodec 方法有两个实现:

    • DatabindCodec
    • JacksonCodec
      在这里插入图片描述
      目前运行的进程默认使用了 JacksonCodec,是不是可以切换到 DatabindCodec 试试?

    如何切换 Databind 实现?

    Vert.x 通过 JacksonFactory 创建 JsonCodec 的实现,先尝试创建 DatabindCodec,如果抛出异常(也就是缺少 databind 相关的类)则创建 JacksonCodec

    public class JacksonFactory implements io.vertx.core.spi.JsonFactory {
    
      public static final JacksonFactory INSTANCE = new JacksonFactory();
    
      public static final JacksonCodec CODEC;
    
      static {
        JacksonCodec codec;
        try {
          codec = new DatabindCodec();
        } catch (Throwable ignore) {
          // No databind
          codec = new JacksonCodec();
        }
        CODEC = codec;
      }
    
      @Override
      public JsonCodec codec() {
        return CODEC;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    源码链接:https://github.com/eclipse-vertx/vert.x/blob/0bdaecf62dbb14421d0277fbfe3c90a47812f538/src/main/java/io/vertx/core/json/jackson/JacksonFactory.java

    vertx-core 在引入 jackson-databind 的时候声明了 optional

        <!-- Jackson -->
        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-core</artifactId>
        </dependency>
        <dependency>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
          <optional>true</optional>
        </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    源码链接:https://github.com/eclipse-vertx/vert.x/blob/0bdaecf62dbb14421d0277fbfe3c90a47812f538/pom.xml#L127-L136

    由此可以理解:Vert.x Core 引入了 jackson-core 以提供基本的 Json 编解码能力,但对于 Json 与对象映射这类更高级的操作,需要借助 jackson-databind 实现。

    那只需要引入 jackson-databind 就能解决问题了。

  • 相关阅读:
    RuoYi-Vue-Plus (角色部门-数据权限 @DataPermission使用、自定义数据权限、数据权限拦截 、处理器解读)
    诺顿杀毒软件设置日常设置及防火墙配置(图文)
    特征融合篇 | YOLOv8 引入中心化特征金字塔 EVC 模块 | 《Centralized Feature Pyramid for Object Detection》
    开发知识点-Ruby
    多模态3D目标检测-自动驾驶
    CentOS8安装Docker与卸载Docker
    docker安装RocketMQ
    Java数组案例
    在 Istio 服务网格内连接外部 MySQL 数据库
    Azkaban集群模式部署
  • 原文地址:https://blog.csdn.net/wu_weijie/article/details/125510504