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

如何在Swagger中接收SignalR Hub消息?(.NET 6 Web API场景)

在Swagger中接收SignalR响应的实现方案

问题背景

基于.NET 6.0开发的Web API集成了SignalR,使用SignalRSwaggerGen包可在Swagger中发送SignalR请求,但无法接收Socket响应,该包开发者明确表示不支持此功能,现需实现Swagger内接收SignalR响应的功能。

现有代码

ConfigureServices 配置

public void ConfigureServices(IServiceCollection services)
{
    services.AddSignalR(options =>
    {
        options.EnableDetailedErrors = true;
    });
    services.AddControllers();
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "Game", Version = "v1" });
        c.AddSignalRSwaggerGen(ssgOption => ssgOption.HubPathFunc = (hubPath) => "/game");
    });
}

端点配置

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
    endpoints.MapHub<GameHub>("/game", config =>
    {
        config.Transports = HttpTransportType.WebSockets;
    });
});

app.Run(async (context) =>
{
    var hubConnection = new HubConnectionBuilder().WithUrl($"https://{context.Request.Host.Value}/game{context.Request.QueryString}").WithAutomaticReconnect().Build();
    await hubConnection.StartAsync();
});

实现方法

由于原生Swagger和SignalRSwaggerGen均不支持WebSocket响应接收,最可靠的方式是自定义Swagger UI扩展,嵌入SignalR客户端逻辑来接收消息。

步骤1:自定义Swagger UI的Index页面

修改Program.cs中的SwaggerUI配置,指定使用自定义的index.html文件:

app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "Game v1");
    // 替换成你的项目命名空间和资源路径
    c.IndexStream = () => typeof(Program).Assembly.GetManifestResourceStream("GameAPI.Swagger.index.html");
});

步骤2:创建自定义index.html

在项目中新建Swagger文件夹,添加index.html文件,设置其生成操作为嵌入的资源。文件内容如下:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Game API Swagger UI</title>
    <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4.15.5/swagger-ui.css" />
    <style>
        #signalr-panel {
            margin: 20px 0;
            padding: 15px;
            border: 1px solid #d9d9d9;
            border-radius: 4px;
        }
        #signalr-messages {
            height: 250px;
            overflow-y: auto;
            margin-top: 10px;
            padding: 10px;
            background-color: #fafafa;
            border-radius: 4px;
        }
        .message-line {
            margin-bottom: 8px;
            padding: 6px;
            background-color: #fff;
            border-radius: 2px;
            box-shadow: 0 1px 2px rgba(0,0,0,0.1);
        }
        .message-time {
            color: #666;
            font-size: 12px;
            margin-right: 8px;
        }
    </style>
</head>
<body>
    <div id="swagger-ui"></div>
    <div id="signalr-panel">
        <h3>SignalR 消息接收区</h3>
        <button onclick="connectToHub()">连接SignalR Hub</button>
        <button onclick="clearMessages()">清空消息</button>
        <div id="signalr-messages"></div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4.15.5/swagger-ui-bundle.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@4.15.5/swagger-ui-standalone-preset.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.21/signalr.min.js"></script>

    <script>
        window.onload = function() {
            const ui = SwaggerUIBundle({
                url: "/swagger/v1/swagger.json",
                dom_id: '#swagger-ui',
                presets: [
                    SwaggerUIBundle.presets.apis,
                    SwaggerUIStandalonePreset
                ],
                layout: "StandaloneLayout"
            });
            window.ui = ui;
        };

        let hubConnection;

        function connectToHub() {
            if (hubConnection && hubConnection.state === signalR.HubConnectionState.Connected) {
                alert("已处于连接状态");
                return;
            }

            hubConnection = new signalR.HubConnectionBuilder()
                .withUrl("/game")
                .withAutomaticReconnect()
                .build();

            // 监听服务端推送的所有方法,替换成你的Hub中实际定义的客户端方法
            hubConnection.on("ReceiveGameUpdate", (updateContent) => {
                addMessage(`游戏更新: ${JSON.stringify(updateContent)}`);
            });

            hubConnection.on("PlayerJoined", (playerName) => {
                addMessage(`玩家加入: ${playerName}`);
            });

            hubConnection.start()
                .then(() => addMessage("SignalR Hub 连接成功"))
                .catch(err => addMessage(`连接失败: ${err.message}`));
        }

        function addMessage(content) {
            const messagesDiv = document.getElementById("signalr-messages");
            const messageElement = document.createElement("div");
            messageElement.className = "message-line";
            messageElement.innerHTML = `<span class="message-time">${new Date().toLocaleString()}</span>${content}`;
            messagesDiv.appendChild(messageElement);
            messagesDiv.scrollTop = messagesDiv.scrollHeight;
        }

        function clearMessages() {
            document.getElementById("signalr-messages").innerHTML = "";
        }
    </script>
</body>
</html>

步骤3:适配你的SignalR Hub方法

在上述html的JavaScript代码中,将hubConnection.on中的方法名(如ReceiveGameUpdate、PlayerJoined)替换为你的GameHub中实际定义的客户端接收方法。

注意事项

  • 确保SignalR客户端版本与服务端.NET 6.0版本兼容(示例中使用6.0.21版本)
  • 自定义index.html中的资源CDN链接可根据需要更新版本
  • 如果你的API启用了身份验证,需要在SignalR连接中携带认证信息(如JWT token)

内容的提问来源于stack exchange,提问作者Gegham Hunanyan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 12:35:19