最近在尝试用 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>
排查过程
构造接口
编写一个 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));
}
}
发现问题
但是在反序列化的时候报错 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/:?]
跟进代码
查看 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;
}
}
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>
由此可以理解:Vert.x Core 引入了 jackson-core
以提供基本的 Json 编解码能力,但对于 Json 与对象映射这类更高级的操作,需要借助 jackson-databind
实现。
那只需要引入 jackson-databind
就能解决问题了。