Wil*_*iam 2 java rest jackson vert.x
我正在尝试通过创建一个小型 ReST API 来学习 Vert.x。一切都很顺利,直到我添加了数据库后端。我选择 Postgres 作为数据库。当 java.time LocalDate 被解析为 JSON 时,我的问题出现了。据我了解,我需要杰克逊后端来解析它。我通过 Maven 依赖项将其添加到我的类路径中。依赖关系存在,但框架抛出异常,因为它找不到名为 jackson-datatype-jsr310 的 jackson 插件。好吧,它就在那里,所以我有点卡住了......
我使用Vert.x 4和java 16,我使用 IntelliJ IDEA 在 Windows 10 上进行开发。
openjdk 16.0.1 2021-04-20
OpenJDK Runtime Environment AdoptOpenJDK-16.0.1+9 (build 16.0.1+9)
OpenJDK 64-Bit Server VM AdoptOpenJDK-16.0.1+9 (build 16.0.1+9, mixed mode, sharing)
Run Code Online (Sandbox Code Playgroud)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>myapi</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
<maven-shade-plugin.version>3.2.4</maven-shade-plugin.version>
<maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
<exec-maven-plugin.version>3.0.0</exec-maven-plugin.version>
<vertx.version>4.1.4</vertx.version>
<junit-jupiter.version>5.8.0</junit-jupiter.version>
<log4j.version>2.14.1</log4j.version>
<jackson.version>2.13.0-rc2</jackson.version>
<main.verticle>dev.schaw.myapi.MainVerticle</main.verticle>
<launcher.class>io.vertx.core.Launcher</launcher.class>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-stack-depchain</artifactId>
<version>${vertx.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-config</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-pg-client</artifactId>
<version>${vertx.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<release>11</release>
</configuration>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>${maven-shade-plugin.version}</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>${launcher.class}</Main-Class>
<Main-Verticle>${main.verticle}</Main-Verticle>
</manifestEntries>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar
</outputFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>${exec-maven-plugin.version}</version>
<configuration>
<mainClass>io.vertx.core.Launcher</mainClass>
<arguments>
<argument>run</argument>
<argument>${main.verticle}</argument>
</arguments>
</configuration>
</plugin>
</plugins>
</build>
</project>
Run Code Online (Sandbox Code Playgroud)
package dev.schaw.myapi;
import io.vertx.config.ConfigRetriever;
import io.vertx.config.ConfigRetrieverOptions;
import io.vertx.config.ConfigStoreOptions;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.AsyncResult;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.pgclient.PgConnectOptions;
import io.vertx.pgclient.PgPool;
import io.vertx.sqlclient.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MainVerticle extends AbstractVerticle {
private HttpServer server;
private Router router;
private Logger logger;
private SqlClient sqlClient;
@Override
public void start() {
server = vertx.createHttpServer();
router = Router.router(vertx);
logger = LoggerFactory.getLogger(MainVerticle.class);
ConfigRetriever retriever = ConfigRetriever.create(vertx, new ConfigRetrieverOptions()
.addStore(new ConfigStoreOptions()
.setType("file")
.setConfig(new JsonObject().put("path", "config.json"))));
retriever.getConfig(this::onConfigRetrieved);
}
public void onConfigRetrieved(AsyncResult<JsonObject> configAsyncResult){
JsonObject config = configAsyncResult.result();
JsonObject dataConnection = config.getJsonObject("myapi").getJsonObject("dataConnection");
PgConnectOptions connectOptions = new PgConnectOptions()
.setHost(dataConnection.getString("host"))
.setPort(dataConnection.getInteger("port"))
.setDatabase(dataConnection.getString("database"))
.setUser(dataConnection.getString("user"))
.setPassword(dataConnection.getString("password"));
PoolOptions poolOptions = new PoolOptions().setMaxSize(5);
sqlClient = PgPool.pool(vertx, connectOptions, poolOptions);
router.route(HttpMethod.GET, "/").handler(ctx -> {
logger.info("received a request");
HttpServerResponse response = ctx.response();
response.putHeader("content-type", "text/plain");
response.end("Hello World!\n");
});
router.get("/data").handler(ctx -> {
sqlClient.preparedQuery("SELECT * FROM sample_data WHERE id=$1")
.execute(Tuple.of(1), queryAsyncResult -> {
RowSet<Row> set = queryAsyncResult.result();
if(set.rowCount() != 1){
ctx.fail(404);
}
Row r = set.iterator().next();
JsonObject body = new JsonObject().put("id", r.getInteger("id"))
.put("name", r.getString("name"))
.put("date", r.getLocalDate("date"));
ctx.response().putHeader("content-type", "application/json").end(body.encode());
});
});
int port = config
.getJsonObject("myapi")
.getJsonObject("server")
.getInteger("port");
server.requestHandler(router).listen(port);
}
}
Run Code Online (Sandbox Code Playgroud)
create table sample_data (
id serial primary key,
name varchar(80) not null,
date date not null
);
Run Code Online (Sandbox Code Playgroud)
你可以在这里检查类路径。
"C:\Program Files\AdoptOpenJDK\jdk-16.0.1.9-hotspot\bin\java.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:53414,suspend=y,server=n -javaagent:C:\Users\willi\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\212.5284.40\plugins\java\lib\rt\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath C:\Users\willi\Projects\myapi\target\classes;C:\Users\willi\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.13.0-rc2\jackson-core-2.13.0-rc2.jar;C:\Users\willi\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.13.0-rc2\jackson-databind-2.13.0-rc2.jar;C:\Users\willi\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.13.0-rc2\jackson-annotations-2.13.0-rc2.jar;C:\Users\willi\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.13.0-rc2\jackson-datatype-jsr310-2.13.0-rc2.jar;C:\Users\willi\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.13.0-rc2\jackson-datatype-jdk8-2.13.0-rc2.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-web\4.1.4\vertx-web-4.1.4.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-web-common\4.1.4\vertx-web-common-4.1.4.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-auth-common\4.1.4\vertx-auth-common-4.1.4.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-bridge-common\4.1.4\vertx-bridge-common-4.1.4.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-core\4.1.4\vertx-core-4.1.4.jar;C:\Users\willi\.m2\repository\io\netty\netty-common\4.1.67.Final\netty-common-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-buffer\4.1.67.Final\netty-buffer-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-transport\4.1.67.Final\netty-transport-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-handler\4.1.67.Final\netty-handler-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-codec\4.1.67.Final\netty-codec-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-handler-proxy\4.1.67.Final\netty-handler-proxy-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-codec-socks\4.1.67.Final\netty-codec-socks-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-codec-http\4.1.67.Final\netty-codec-http-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-codec-http2\4.1.67.Final\netty-codec-http2-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-resolver\4.1.67.Final\netty-resolver-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-resolver-dns\4.1.67.Final\netty-resolver-dns-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-codec-dns\4.1.67.Final\netty-codec-dns-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-config\4.1.4\vertx-config-4.1.4.jar;C:\Users\willi\.m2\repository\org\apache\logging\log4j\log4j-api\2.14.1\log4j-api-2.14.1.jar;C:\Users\willi\.m2\repository\org\apache\logging\log4j\log4j-core\2.14.1\log4j-core-2.14.1.jar;C:\Users\willi\.m2\repository\org\apache\logging\log4j\log4j-slf4j-impl\2.14.1\log4j-slf4j-impl-2.14.1.jar;C:\Users\willi\.m2\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-pg-client\4.1.4\vertx-pg-client-4.1.4.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-sql-client\4.1.4\vertx-sql-client-4.1.4.jar;C:\Users\willi\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\212.5284.40\lib\idea_rt.jar io.vertx.core.Launcher run dev.schaw.myapi.MainVerticle
Connected to the target VM, address: '127.0.0.1:53414', transport: 'socket'
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
16:06:01.100 [vert.x-eventloop-thread-0] INFO io.vertx.core.impl.launcher.commands.VertxIsolatedDeployer - Succeeded in deploying verticle
16:06:03.393 [vert.x-eventloop-thread-1] ERROR io.vertx.core.impl.ContextImpl - Unhandled exception
io.vertx.core.json.EncodeException: Failed to encode as JSON: Java 8 date/time type `java.time.LocalDate` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: java.util.LinkedHashMap["date"])
at io.vertx.core.json.jackson.DatabindCodec.toString(DatabindCodec.java:163) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.json.JsonObject.encode(JsonObject.java:728) ~[vertx-core-4.1.4.jar:4.1.4]
at dev.schaw.myapi.MainVerticle.lambda$onConfigRetrieved$1(MainVerticle.java:73) ~[classes/:?]
at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.PromiseImpl.tryComplete(PromiseImpl.java:23) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.sqlclient.impl.QueryResultBuilder.tryComplete(QueryResultBuilder.java:102) ~[vertx-sql-client-4.1.4.jar:4.1.4]
at io.vertx.sqlclient.impl.QueryResultBuilder.tryComplete(QueryResultBuilder.java:35) ~[vertx-sql-client-4.1.4.jar:4.1.4]
at io.vertx.core.Promise.complete(Promise.java:66) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.Promise.handle(Promise.java:51) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.Promise.handle(Promise.java:29) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.PromiseImpl.tryComplete(PromiseImpl.java:23) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.PromiseImpl.onSuccess(PromiseImpl.java:49) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.FutureImpl$ListenerArray.onSuccess(FutureImpl.java:262) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.FutureBase.lambda$emitSuccess$0(FutureBase.java:54) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.EventLoopContext.execute(EventLoopContext.java:81) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.DuplicatedContext.execute(DuplicatedContext.java:173) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:51) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.PromiseImpl.tryComplete(PromiseImpl.java:23) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.PromiseImpl.onSuccess(PromiseImpl.java:49) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.PromiseImpl.handle(PromiseImpl.java:41) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.future.PromiseImpl.handle(PromiseImpl.java:23) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.sqlclient.impl.command.CommandResponse.fire(CommandResponse.java:46) ~[vertx-sql-client-4.1.4.jar:4.1.4]
at io.vertx.sqlclient.impl.SocketConnectionBase.handleMessage(SocketConnectionBase.java:287) ~[vertx-sql-client-4.1.4.jar:4.1.4]
at io.vertx.pgclient.impl.PgSocketConnection.handleMessage(PgSocketConnection.java:96) ~[vertx-pg-client-4.1.4.jar:4.1.4]
at io.vertx.sqlclient.impl.SocketConnectionBase.lambda$init$0(SocketConnectionBase.java:99) ~[vertx-sql-client-4.1.4.jar:4.1.4]
at io.vertx.core.net.impl.NetSocketImpl.lambda$new$1(NetSocketImpl.java:97) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.streams.impl.InboundBuffer.handleEvent(InboundBuffer.java:240) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.streams.impl.InboundBuffer.write(InboundBuffer.java:130) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.net.impl.NetSocketImpl.lambda$handleMessage$9(NetSocketImpl.java:390) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:50) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.ContextImpl.emit(ContextImpl.java:274) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:22) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.net.impl.NetSocketImpl.handleMessage(NetSocketImpl.java:389) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.net.impl.ConnectionBase.read(ConnectionBase.java:155) ~[vertx-core-4.1.4.jar:4.1.4]
at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:154) ~[vertx-core-4.1.4.jar:4.1.4]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.67.Final.jar:4.1.67.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.67.Final.jar:4.1.67.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.67.Final.jar:4.1.67.Final]
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) ~[netty-transport-4.1.67.Final.jar:4.1.67.Final]
at io.vertx.pgclient.impl.codec.PgEncoder.lambda$write$0(PgEncoder.java:87) ~[vertx-pg-client-
幕后Vertx使用Jackson's ObjectMapper内部encode()和encodePrettily()方法。您需要做的就是JavaTimeModule在这些网站上注册ObjectMappers.
将这些代码片段添加到应用程序的主方法中。
ObjectMapper mapper = DatabindCodec.mapper();
mapper.registerModule(new JavaTimeModule());
ObjectMapper prettyMapper = DatabindCodec.prettyMapper();
prettyMapper.registerModule(new JavaTimeModule());
Run Code Online (Sandbox Code Playgroud)
这是类的完整包DatabindCodec: io.vertx.core.json.jackson.DatabindCodec
public class MainVerticle extends AbstractVerticle {
// code already exists
@Override
public void start() {
ObjectMapper mapper = DatabindCodec.mapper();
mapper.registerModule(new JavaTimeModule());
ObjectMapper prettyMapper = DatabindCodec.prettyMapper();
prettyMapper.registerModule(new JavaTimeModule());
// code already exists
Run Code Online (Sandbox Code Playgroud)