4

ASP.NET Core SignalR 迟到的、隐藏在角落的 RawResult

 11 months ago
source link: http://blog.tubumu.com/2023/05/14/RawResult-of-ASP-NET-Core-SignalR/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

ASP.NETASP.NET Core 中,如果服务端得到一个 JSON 字符串(比如从 Redis 缓存中获取),我们可以通过 Content 方法或直接创建 ContentResult 对象来作为 Action 的返回值。

var json = "{\"key\": \"value\"}";
return Content(json, "application/json");
return new ContentResult
{
Content = json,
ContentType = "application/json"
};

而在 SignalR 中,在 ASP.NET Core 7.0 之前,对于一个 JSON 字符串只能先反序列化,否则 Web 前端得用 JSON.parse() 处理一次。

本文主要记录测试 RawResult 的结果。

二、服务端

public class KVObject
{
public string Key { get; set; }
}

public class ChatHub : Hub
{
public string Test1()
{
var json = "{\"Key\": \"1\"}";
return json;
}

public KVObject Test2()
{
var json = "{\"Key\": \"2\"}";
var obj = JsonSerializer.Deserialize<KVObject>(json)!;
return obj;
}

public RawResult Test3()
{
var json = "{\"Key\": \"3\"}";
return new RawResult(new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(json)));
}

public async Task Test4()
{
var json = "{\"Key\": \"4\"}";
await Clients.Caller.SendAsync("Notification", json);
}

public async Task Test5()
{
var json = "{\"Key\": \"5\"}";
var obj = JsonSerializer.Deserialize<KVObject>(json)!;
await Clients.Caller.SendAsync("Notification", obj);
}

public async Task Test6()
{
var json = "{\"Key\": \"6\"}";
var rawResult = new RawResult(new ReadOnlySequence<byte>(Encoding.UTF8.GetBytes(json)));
// 异常:
// System.InvalidOperationException: The type 'System.ReadOnlySpan`1[System.Byte]' of property 'FirstSpan' on type 'System.Buffers.ReadOnlySequence`1[System.Byte]' is invalid for serialization or deserialization because it is a pointer type, is a ref struct, or contains generic parameters that have not been replaced by specific types.
await Clients.Caller.SendAsync("Notification", rawResult);
}
}

上述代码中,json 字符串变量可能是从其他进程(如数据库)、文本文件或网络获取。

在 .Net 7 中,Test6 会有异常,.Net 8 尚未测试。

三、客户端

<!DOCTYPE html>
<html>
<head>
<title>SignalR RawResult Test</title>
</head>
<body>
<script src="//cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.js"></script>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', async function () {
var connection = new signalR.HubConnectionBuilder()
.withUrl('/chat')
.build();

connection.on('Notification', function (message) {
console.log("Notification:", message, typeof(message));
});

await connection.start();
var result1 = await connection.invoke('Test1');
console.log("Test1:", result1, typeof (result1));
var result2 = await connection.invoke('Test2');
console.log("Test2:", result2, typeof (result2));
var result3 = await connection.invoke('Test3');
console.log("Test3:", result3, typeof (result3));
await connection.invoke('Test4'); // 无返回值,但会收到一个通知
await connection.invoke('Test5'); // 无返回值,但会收到一个通知
await connection.invoke('Test6'); // 无返回值,但会收到一个通知
});
</script>
</body>
</html>

注释 Test6 的调用后。console.log 输出如下:

Test1: {"Key": "1"} string
Test2: {key: '2'} object
Test3: {Key: '3'} object
Notification: {"Key": "4"} string
Notification: {key: '5'} object

通过查看 WebSocket 消息日志也能确认这一点(下面的日志经过手工整理):

// Test1 的调用和返回值。返回值的类型是 string。
{"arguments":[],"invocationId":"0","target":"Test1","type":1}
{"type":3,"invocationId":"0","result":"{\"Key\": \"1\"}"}

// Test2 的调用和返回值。返回值的类型是 object。
{"arguments":[],"invocationId":"1","target":"Test2","type":1}
{"type":3,"invocationId":"1","result":{"key":"2"}}

// Test3 的调用和返回值。返回值的类型是 object。
{"arguments":[],"invocationId":"2","target":"Test3","type":1}
{"type":3,"invocationId":"2","result":{"Key": "3"}}

// Test4 的调用、返回值(null)和通知。通知的第一个值的类型是 string。
{"arguments":[],"invocationId":"3","target":"Test4","type":1}
{"type":3,"invocationId":"3","result":null}
{"type":1,"target":"Notification","arguments":["{\"Key\": \"4\"}"]}

// Test5 的调用、返回值(null)和通知。通知的第一个值的类型是 object。
{"arguments":[],"invocationId":"4","target":"Test5","type":1}
{"type":3,"invocationId":"4","result":null}
{"type":1,"target":"Notification","arguments":[{"key":"5"}]}

RawResult 文档
RawResult 源码
ASP.NET Core SignalR


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK