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

ServiceStack ServerEventsClient无法处理含空格字符串事件求助

问题:ServiceStack ServerEventsClient接收含空格消息时触发异常

我搭建了一个Java SpringBoot服务器,负责接收消息并向C#和JS客户端广播。发送不含空格的消息时,所有客户端都能正常接收;但发送含空格的消息时,C#端的ServiceStack ServerEventsClient会触发OnException异常,非ServiceStack的JS客户端则完全正常——不管空格在什么位置、有多少个,只要有单个空格就会导致C#客户端失败。

异常栈追踪

System.ArgumentException: Invalid Selector '{"characterId":"6ed46423-6d70-4aae-a37f-4f5e799d326b","message":"\"hello'
   at ServiceStack.ServerEventsClient.ToTypedMessage(ServerEventMessage e) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack.Client/ServerEventsClient.cs:line 804
   at ServiceStack.ServerEventsClient.ProcessEventMessage(ServerEventMessage e) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack.Client/ServerEventsClient.cs:line 859
   at ServiceStack.ServerEventsClient.<>c__DisplayClass115_0.<ProcessResponse>b__0(Task`1 t) in /home/runner/work/ServiceStack/ServiceStack/ServiceStack/src/ServiceStack.Client/ServerEventsClient.cs:line 725

注:示例消息的第一个单词“hello”后带有空格。消息是一个包含Guid类型characterId和string类型message的DTO。

相关代码

Java SpringBoot服务器代码

@PostMapping(path = "/message")
public boolean sendMessage(@RequestBody DTOs.MessageDTO request) {
    for(SseEmitter sseEmitter: emitters){
        try {
            sseEmitter.send(request);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    return true;
}

C#客户端代码(使用ServiceStack)

private ServerEventsClient _create_sse_client()
{
    baseUri = $"...";

    return new ServerEventsClient(baseUri)
    {
        OnMessage = this._events.Enqueue,
        OnException = errors.Add,
        OnCommand = this._commands.Enqueue,
        AllRequestFilters = req =>
        {
            req.Method = HttpMethod.Get;
            req.AddHeader("Accept", "text/event-stream");
        },
    };
}

我是C#新手,想知道有没有明显遗漏的配置或处理步骤。


解决方案

问题核心是SpringBoot的SseEmitter默认序列化格式与ServiceStack ServerEventsClient的解析逻辑不兼容。ServiceStack的客户端基于自身的Server Events协议实现,对SSE消息的格式有特定要求,而SpringBoot直接发送DTO对象生成的内容,在包含空格、引号时会触发解析截断错误。

方案1:修改SpringBoot服务器的消息发送格式

手动构造符合ServiceStack要求的标准SSE消息结构,确保完整的JSON被包裹在data字段中:

@PostMapping(path = "/message")
public boolean sendMessage(@RequestBody DTOs.MessageDTO request) {
    ObjectMapper objectMapper = new ObjectMapper();
    // 序列化DTO为完整转义的JSON字符串
    String jsonPayload = objectMapper.writeValueAsString(request);
    // 构造标准SSE消息格式:data: [JSON内容]\n\n
    String sseMessage = "data: " + jsonPayload + "\n\n";
    
    for(SseEmitter sseEmitter: emitters){
        try {
            // 发送原始字符串格式的SSE消息
            sseEmitter.send(sseMessage);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    return true;
}

这样能保证ServiceStack客户端接收到完整、格式正确的消息,不会出现解析截断。

方案2:C#客户端自定义消息解析

如果不想修改服务器代码,可以绕过ServiceStack默认的解析逻辑,手动处理原始消息:

private ServerEventsClient _create_sse_client()
{
    baseUri = $"...";

    var client = new ServerEventsClient(baseUri)
    {
        OnException = errors.Add,
        OnCommand = this._commands.Enqueue,
        AllRequestFilters = req =>
        {
            req.Method = HttpMethod.Get;
            req.AddHeader("Accept", "text/event-stream");
        },
    };
    // 替换默认OnMessage,用OnRawMessage手动解析JSON
    client.OnRawMessage = (msg) =>
    {
        try
        {
            var messageDto = JsonSerializer.Deserialize<MessageDTO>(msg.Data);
            this._events.Enqueue(messageDto);
        }
        catch (Exception ex)
        {
            errors.Add(ex);
        }
    };
    return client;
}

通过OnRawMessage获取原始JSON字符串,自己处理反序列化,避免ServiceStack默认解析的问题。

额外验证

在服务器端打印序列化后的JSON字符串,确认格式完整且转义正确,比如:
{"characterId":"6ed46423-6d70-4aae-a37f-4f5e799d326b","message":"hello world"}

内容的提问来源于stack exchange,提问作者Curious User 123

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 06:05:13