Quarkus Client:Server Side Events反序列化失败问题求助
解决Quarkus SSE客户端自动映射Java Record失败问题
问题背景
我正在学习Quarkus,若有错误欢迎指正。调用第三方SSE服务端时,客户端无法自动将SSE事件数据映射到Java Record(TestDTO),抛出处理异常。
错误信息
ERROR [io.qua.ver.cor.run.VertxCoreRecorder] (vert.x-eventloop-thread-1) Uncaught exception received by Vert.x: jakarta.ws.rs.ProcessingException: Response could not be mapped to type class TestDTO for response with media type null
服务端实际响应
< HTTP/1.1 200 OK < X-Powered-By: Express < Content-Type: text/event-stream < Cache-Control: no-cache < Connection: keep-open < Transfer-Encoding: chunked < event: success data: {"a":"str_value_1","date":"1970-01-01T02:00:00.000Z","b":1} event: success data: {"a":"str_value_2","date":"1970-01-01T02:00:00.000Z","b":2}
我的Quarkus代码
1. Rest客户端接口
@Path("/api") @RegisterRestClient(configKey = "service-api") public interface TestClient { @GET @Path("test") @Produces(MediaType.SERVER_SENT_EVENTS) Multi<TestDTO> getTest(); }
2. TestDTO Record
@JsonIgnoreProperties(ignoreUnknown = true) public record TestDTO(String a, LocalDate date, int b) { private static final ObjectWriter WRITER = new ObjectMapper() .writerWithDefaultPrettyPrinter() .withoutAttribute("jacksonObjectMapper"); @Override public String toString() { try { return WRITER.writeValueAsString(this); } catch (Exception e) { return String.format("IssueComment[id=%s]", id); // 注:此处id未定义,为笔误 } } }
3. 资源类TestResource
@Path("/test") @ApplicationScoped public class testResource { @RestClient GitHubServiceClient client; // 注:此处应为TestClient,为笔误 @GET @Produces(MediaType.SERVER_SENT_EVENTS) public Multi<TestDTO> hello() { return client.getTest().onItem().invoke(item -> System.out.println(item)); } }
错误分析
将TestDTO简化为TestDTO(String a)时,客户端不再报错,但映射结果显示Jackson把整个SSE的data字段内容当作字符串赋值给了a属性:
{ "a" : "{\"a\":\"str_value_1\",\"date\":\"1970-01-01T02:00:00.000Z\",\"b\":1}" } { "a" : "{\"a\":\"str_value_2\",\"date\":\"1970-01-01T02:00:00.000Z\",\"b\":2}" }
说明Quarkus的Rest Client默认没有正确识别SSE的data字段是JSON格式,而是直接将其作为纯文本处理。
临时解决方案
手动处理JSON映射:将客户端方法返回类型改为Multi<String>,然后在资源类中用ObjectMapper手动解析:
@Path("/test") @ApplicationScoped public class TestResource{ @RestClient TestClient client; @GET @Produces(MediaType.SERVER_SENT_EVENTS) public Multi<TestDTO> hello() { ObjectMapper objectMapper = new ObjectMapper() .registerModule(new JavaTimeModule()); // 注册JavaTimeModule处理LocalDate return client.getTest() .map(Unchecked.function(s -> objectMapper.readValue(s, TestDTO.class))) .onItem().invoke(item -> System.out.println(item)); } }
正式解决方案(无需手动映射)
要让Quarkus Rest Client自动解析SSE的data为JSON对象,需做以下调整:
1. 为客户端方法添加@SseEvent注解
Quarkus的Rest Client需要明确指定SSE事件的解析方式,通过@SseEvent注解告诉客户端将每个事件的data字段解析为JSON:
@Path("/api") @RegisterRestClient(configKey = "service-api") public interface TestClient { @GET @Path("test") @Produces(MediaType.SERVER_SENT_EVENTS) @SseEvent(name = "success") // 指定监听的事件名称,无需过滤可省略name属性 Multi<TestDTO> getTest(); }
2. 修复TestDTO的问题
- 修正
toString()方法中的未定义变量id - 确保Jackson能正确处理
LocalDate,注册JavaTimeModule:
@JsonIgnoreProperties(ignoreUnknown = true) public record TestDTO(String a, LocalDate date, int b) { private static final ObjectWriter WRITER = new ObjectMapper() .registerModule(new JavaTimeModule()) .writerWithDefaultPrettyPrinter(); @Override public String toString() { try { return WRITER.writeValueAsString(this); } catch (Exception e) { return String.format("TestDTO[a=%s, date=%s, b=%d]", a, date, b); } } }
3. 修正资源类中的笔误
将GitHubServiceClient改为TestClient:
@Path("/test") @ApplicationScoped public class TestResource { @RestClient TestClient client; @GET @Produces(MediaType.SERVER_SENT_EVENTS) public Multi<TestDTO> hello() { return client.getTest().onItem().invoke(item -> System.out.println(item)); } }
原理说明
Quarkus的Rest Client处理SSE时,默认会将每个事件的data作为纯文本返回。添加@SseEvent注解后,客户端会自动识别data字段的JSON类型,并使用Jackson将其映射到指定的Java类型(TestDTO)。同时,注册JavaTimeModule可确保Jackson正确解析LocalDate类型。
环境信息:
- Java 17
- Quarkus 3.17.0
内容的提问来源于stack exchange,提问作者dali
相关产品推荐
相关产品推荐

