You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 20:24:55