如何搭建最简Serverless SignalR API及客户端?遇端点调用无响应问题
问题分析
核心问题在于Serverless环境(Azure Functions)下的SignalR实现逻辑和传统ASP.NET Core SignalR完全不同:
- 传统ASP.NET Core依赖Hub上下文的
Clients对象推送消息,但Azure Functions的Serverless SignalR通过触发器绑定+输出绑定实现功能,无法直接复用ASP版本的代码。 - 你的
SendMessage既未标记为Azure Function,也未配置SignalR相关绑定,前端调用时无法找到对应端点。
解决方案一:HTTP触发+SignalR输出绑定(适合HTTP调用场景)
修正后端代码
// 正常触发的Negotiate函数保持不变 [FunctionName("negotiate")] public static SignalRConnectionInfo Negotiate( [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req, [SignalRConnectionInfo(HubName = "TestHub")] SignalRConnectionInfo connectionInfo) { return connectionInfo; } // 修正后的Serverless版本SendMessage函数 [FunctionName("SendMessage")] public static async Task<IActionResult> SendMessage( [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req, [SignalR(HubName = "TestHub")] IAsyncCollector<SignalRMessage> signalRMessages) { // 解析前端传入的message参数 var requestBody = await new StreamReader(req.Body).ReadToEndAsync(); var data = JsonSerializer.Deserialize<dynamic>(requestBody); string message = data?.message; if (string.IsNullOrEmpty(message)) { return new BadRequestObjectResult("消息不能为空"); } // 处理消息(反转字符串) char[] stringArray = message.ToCharArray(); Array.Reverse(stringArray); string reversedStr = new string(stringArray); // 通过SignalR输出绑定推送消息给所有客户端 await signalRMessages.AddAsync(new SignalRMessage { Target = "ReceiveMessage", Arguments = new[] { reversedStr } }); return new OkObjectResult("消息发送成功"); }
修正前端代码
const connection = new signalR.HubConnectionBuilder() .withUrl("http://localhost:7230/api/TestHub") // 关键:添加Hub名称TestHub .configureLogging(signalR.LogLevel.Information) .withAutomaticReconnect() .build(); // 接收消息的代码保持不变 connection.on("ReceiveMessage", (reply) => { console.log("[SignalR.ReceiveMessage] " + reply); serverReply.value = reply; }); // ... 其他连接逻辑保持不变 // 发送消息的函数改为调用HTTP接口 async function sendToServer(){ try { console.log("[SignalR.SendMessage] " + userInput.value); await fetch("http://localhost:7230/api/SendMessage", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message: userInput.value }) }); } catch (err) { console.error(err); } }
解决方案二:SignalRTrigger触发(保持前端invoke调用习惯)
如果想让前端继续使用connection.invoke方式调用,可改用SignalRTrigger绑定接收客户端请求:
修正后端代码
[FunctionName("SendMessage")] public static async Task SendMessage( [SignalRTrigger("TestHub", "messages", "SendMessage")] InvocationContext invocationContext, string message, [SignalR(HubName = "TestHub")] IAsyncCollector<SignalRMessage> signalRMessages) { // 处理消息 char[] stringArray = message.ToCharArray(); Array.Reverse(stringArray); string reversedStr = new string(stringArray); // 推送消息给所有客户端 await signalRMessages.AddAsync(new SignalRMessage { Target = "ReceiveMessage", Arguments = new[] { reversedStr } }); }
前端调整
仅需修改连接地址,sendToServer函数保持原有invoke逻辑即可:
const connection = new signalR.HubConnectionBuilder() .withUrl("http://localhost:7230/api/TestHub") // 添加Hub名称TestHub // ... 其他配置保持不变 .build(); // sendToServer函数无需修改 async function sendToServer(){ try { console.log("[SignalR.SendMessage] " + userInput.value); await connection.invoke("SendMessage", userInput.value); } catch (err) { console.error(err); } }
必要配置说明
- 安装依赖包:确保项目已安装
Microsoft.Azure.WebJobs.Extensions.SignalRServiceNuGet包 - 本地调试配置:在
local.settings.json中添加SignalR连接字符串(可使用Azure SignalR模拟器)
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "dotnet", "AzureSignalRConnectionString": "Endpoint=http://localhost:5001;AccessKey=abc123;" } }
内容的提问来源于stack exchange,提问作者ScumSprocket
相关产品推荐
相关产品推荐

