当前位置: 首页 > article >正文

C#通过API接口返回流式响应内容---SignalR方式

1、背景

在上两篇《C#通过API接口返回流式响应内容—分块编码方式》和《C#通过API接口返回流式响应内容—SSE方式》实现了流式响应的内容。
上面的这两个主要是通过HTTP的一些功能,除了这些之外,还有WebSocket的方式。C#中的WebSocket的有比较多的方案:SignalRFleckFreeIMWebSocket-SharpNetCoreServer等。其中SignalR是微软官网推荐的、FleckWebSocketC#的开源项目,并且用的比较多,其他三个(FreeIM、WebSocket-Sharp、NetCoreServer)本人未使用,但原理相同,大家可以网上找找方案。
本篇主要是使用SignalR的方案

2、效果

在这里插入图片描述

3、具体代码

3.1、创建一个Hub

在这里插入图片描述
创建一个Hub服务,本质上它是一个服务器,用作一个服务转发。它是通过继承Hub实现。

using Microsoft.AspNetCore.SignalR;

namespace SignalRHub.Hubs
{
    public class DeepSeekChat:Hub
    {
        public async Task SendDeepSeekStream(string msg)
        {
            await Clients.All.SendAsync("ReceiveMsg", msg); //非常简单的方法,就是直接推送数据
        }
    }
}

3.2、配置Hub

program.cs中配置Hub

using Microsoft.AspNetCore.SignalR;

using SignalRHub.Hubs;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSignalR();      //【添加SignalR服务】
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

//app.UseHttpsRedirection();
app.UseCors(MyAllowSpecificOrigins);

app.UseAuthorization();

app.MapControllers();
app.MapHub<DeepSeekChat>("/dschat"); //【配置Endpoint】
app.Run();

3.3、配置推送服务

当我们实现了DeepSeekChat时,我们如何通过服务器,将数据推送到终端呢?因此我们需对外的API接口,当我们调用API的接口时,API方法内部调用signalr的实例,并调用推送方法,从而将我们的内容推送到终端。
在apicontroller中或者program.cs
在apicontroller中的方法如下,【这个是一个Get方法】

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using SignalRHub.Hubs;

namespace SignalRHub.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ViaAPIController : ControllerBase
    {
        private readonly IHubContext<DeepSeekChat> chatHub;

        public ViaAPIController(IHubContext<DeepSeekChat> chatHub)
        {
            this.chatHub = chatHub;
        }

        [HttpGet("sendmsg")]
        public async Task SendMsg()
        {
            //模拟deepseek的对话内容
            var phrases = new string[] { "你好!", "我是", "北京清华长庚医院", "信息管理部的", "郑林" };
            foreach (var item in phrases)
            {
                await chatHub.Clients.All.SendAsync("ReceiveMsg", item);
                await Task.Delay(1000);
            }
        }
    }
}

在program.cs的代码如下:

//上文中的其他配置略过
app.UseAuthorization();
app.MapControllers();
app.MapHub<DeepSeekChat>("/dschat");
//【这儿就是直接配置的,两者的效果是一样的,都是发送数据】
app.MapPost("sendNotification", async (string msg, IHubContext<DeepSeekChat> context) => {
    var phrases = new string[] { "你好!", "我是", "北京清华长庚医院", "信息管理部的", "郑林" };
    foreach (var item in phrases)
    {
        await context.Clients.All.SendAsync("ReceiveMsg", item);
        await Task.Delay(1000);
    }
});

app.Run();

两者的效果是一致的

3.4、前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SignalR Client</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.18/signalr.min.js"></script>
</head>
<body>
    <h3>流式响应</h3>
    <div id="stockPrices"></div>

    <script>
        const connection = new signalR.HubConnectionBuilder()
            .withUrl("http://localhost:5071/dschat")
            .build();

        connection.on("ReceiveMsg", (msg) => {
            const stockPricesDiv = document.getElementById("stockPrices");
            stockPricesDiv.innerHTML+= msg +'&nbsp;'; 
        });

        connection.start()
            .then(() => {
                console.log("SignalR connection started.");
            })
            .catch(err => {
                console.error("Error connecting to SignalR: ", err);
            });

        window.addEventListener("beforeunload", () => {
            connection.stop().then(() => {
                console.log("SignalR connection stopped.");
            }).catch(err => {
                console.error("Error stopping SignalR connection: ", err);
            });
        });
    </script>
</body>
</html>

比较简单

3.5、配置Cors

这样运行的话,浏览器是报错的,因为有Cors限制,因此需要在program.cs中配置

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

//设置CORS
builder.Services.AddCors(options => {
    options.AddPolicy(name: MyAllowSpecificOrigins,
            policy => {
                policy.SetIsOriginAllowed(t=>true) //默认全部访问
                .AllowAnyHeader()
                .AllowAnyMethod()
                .AllowCredentials();
            }
        );
});
builder.Services.AddControllers();

//其他配置

//app.UseHttpsRedirection();
app.UseCors(MyAllowSpecificOrigins); //配置Cors
app.UseAuthorization();
app.MapControllers();

3.6、完整的program配置

完整的配置如下:

using Microsoft.AspNetCore.SignalR;
using SignalRHub.BackgroundServices;
using SignalRHub.Hubs;

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

var builder = WebApplication.CreateBuilder(args);

//设置CORS
builder.Services.AddCors(options => {
    options.AddPolicy(name: MyAllowSpecificOrigins,
            policy => {
                policy.SetIsOriginAllowed(t=>true) //
                .AllowAnyHeader()
                .AllowAnyMethod()
                .AllowCredentials();
            }
        );
});


builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSignalR();//添加SignalR服务
//builder.Services.AddHostedService<Worker>(); //添加一个后台服务

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

//app.UseHttpsRedirection();
app.UseCors(MyAllowSpecificOrigins); //配置Cors

app.UseAuthorization();

app.MapControllers();
app.MapHub<DeepSeekChat>("/dschat");

app.MapPost("sendNotification", async (string msg, IHubContext<DeepSeekChat> context) => {
    var phrases = new string[] { "你好!", "我是", "北京清华长庚医院", "信息管理部的", "郑林" };
    foreach (var item in phrases)
    {
        await context.Clients.All.SendAsync("ReceiveMsg", item);
        await Task.Delay(1000);
    }
});

app.Run();

4、原理

在这里插入图片描述这个是一个原理图,只不过本文的样例是单向的

5、参考资料

1、SignalR微软官网
2、ASP.NET Core 配置跨域(CORS)
3、3个WebSocket的.Net开源项目


http://www.kler.cn/a/593633.html

相关文章:

  • Powershell WSL导出导入ubuntu22.04.5子系统
  • 【数据库系统原理】Ch7 数据库应用设计与开发实例
  • NLP探索
  • 目标检测中归一化的目的?
  • 用数组模拟循环队列
  • IOS接入微信方法
  • atoi 函数
  • IP查询底层逻辑解析:数据包与地理位置
  • stm32 2.0.3.0
  • 知识库项目开场白
  • 封装Socket编程接口
  • 蓝桥杯--冲刺题单--随时更新
  • 物联网平台与边缘计算网关的深度结合应用
  • Spring Boot 集成 Kafka 消息发送方案
  • ‌C# I/O 核心用法
  • 【工程实践/大批量文件处理】文件排序
  • 笛卡尔轨迹规划之齐次变换矩阵与欧拉角、四元数的转化
  • 数据类设计_图片类设计之7_矩阵图形类设计更新_实战之页面简单设计(前端架构)
  • VLLM专题(十九)—兼容 OpenAI 的服务器
  • Matplotlib 柱形图