使用okHttp3发送sse请求,当事件为finish的时候,如何接收附加的数据?

作者站长头像
站长
· 阅读数 5

问题描述

当前需要调用一个AI的接口,使用的工具是okHttp3,请求的方式是SSE。当事件状态是finish的时候,对方的响应事件中会携带一个附加的数据(meta)。结构如图:使用okHttp3发送sse请求,当事件为finish的时候,如何接收附加的数据?在传入的监听器中,onEvent()方法打印响应的data当状态是finish是,打印到的数据是空的,如下图:使用okHttp3发送sse请求,当事件为finish的时候,如何接收附加的数据?第一张图的来源是在初始化htttp客户端的时候加的一个网络请求拦截器打印出的信息,第二张图是代码中打印的log日志。我的问题是为什么在onEvent()中接收不到meta的数据呢?应该如何接收呢?

问题出现的环境背景及自己尝试过哪些方法

1.首先确定了对方服务在finish状态的时候是一定响应了meta数据的。2.查询过一些资料,尝试从将data转变为对象,取获取meta,但是也没有结果,是空的。3.查询过一些其他的系统的源码,发现他们就是从data中获取的meta的数据,我也做同样的尝试,但是没有获取到。

相关代码

发送请求的代码:

    
    public void chat(ModelEnum model, String content, WebSocketEventSourceListener listener) {
        // 初始化客户端
        OkHttpClient okHttpClient = initOkHttpClient();
        RequestBody requestBody = getRequestBody("你只需要回答'收到'两个字就可以了");
        Request request = new Request.Builder()
                .url(getRequestUrl(model))
                .header("Authorization", createToken())
                .header("Accept", "text/event-stream")
                .post(requestBody)
                .build();
        RealEventSource eventSource = new RealEventSource(request, listener);
        eventSource.connect(okHttpClient);
    }

    private OkHttpClient initOkHttpClient() {
        // 初始化客户端
        return new OkHttpClient
                .Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(600, TimeUnit.SECONDS)
                .readTimeout(600, TimeUnit.SECONDS)
                .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
                .build();
    }

监听器代码:

public class WebSocketEventSourceListener extends EventSourceListener {

    @Override
    public void onOpen(EventSource eventSource, Response response) {
        log.info("ListenerOnOpen============={}", "建立sse连接");
    }

    @Override
    public void onEvent(EventSource eventSource, String id, String type, String data) {
        log.info("ListenerOnEvent========== id:{}, type: {}, data: {}", id, type, data);
    }

    @Override
    public void onClosed(EventSource eventSource) {
        log.info("ListenerOnClose========== {}", "连接sse关闭");
    }

    @Override
    public void onFailure(EventSource eventSource, Throwable t, Response response) {

    }
}

在此段代码中onEvent()会接收响应的数据。单是在状态是finish的时候却收不到附加的数据。

你期待的结果是什么?实际看到的错误信息又是什么?

能够在onEvent()中收到meta的数据,实际并没有收到。

回复
1个回答
avatar
test
2024-06-24

SSE 的规范中明确指出,每个事件的结构中的 field 只能是以下几个取值:

  • event
  • data
  • id
  • retry

所有其他的字段名都会被忽略。

https://developer.mozilla.org/zh-CN/docs/Web/API/Server-sent_...

显然 OkHttp 在解析事件时遵守了此规范。从源码中我们也可以看出这一点:

https://github.com/square/okhttp/blob/okhttp_3.14.x/okhttp-sse/src/main/java/okhttp3/internal/sse/ServerSentEventReader.java#L55-L102

所以你这里的问题是上游系统使用了非标准的 SSE 协议。如果上游服务不做修改的话,你只能从底层的 HttpClient 自己动手撸一个 Parser 来处理这种特殊场景了。

回复
likes
适合作为回答的
  • 经过验证的有效解决办法
  • 自己的经验指引,对解决问题有帮助
  • 遵循 Markdown 语法排版,代码语义正确
不该作为回答的
  • 询问内容细节或回复楼层
  • 与题目无关的内容
  • “赞”“顶”“同问”“看手册”“解决了没”等毫无意义的内容