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

ASP.NET MVC 5调用无服务器Azure SignalR Hub方法的实现求助

ASP.NET MVC 5集成Azure SignalR Service无服务器模式的正确连接与调用代码

问题背景

我有一个已集成ASP.NET SignalR V2的ASP.NET MVC 5应用,现在需要以无服务器模式集成Azure SignalR Service,但查阅文档后仍未找到正确的Hub方法调用或连接方式。尝试的代码中HubConnectionBuilder.WithUrl()报错,提示方法不存在,已引用Microsoft.AspNetCore.SignalR.Client。

尝试的MVC端代码

private static async void PushNotification(NotificationRequest notificationRequest, NotificationResponse notificationResponse, string signalRUrl, string authenticationType, Cookie cookie, ICredentials credentials, string accessToken, string userId, string cacheString)
{
    if (authenticationType == "AzureSignalR")
    {
        HubConnection _connection = new HubConnectionBuilder().WithUrl(signalRUrl).Build();
    }
    else
    {
        using (var hubConnection = new HubConnection(signalRUrl))
        {
            IHubProxy notificationHubProxy = hubConnection.CreateHubProxy("NotificationHub");
            if (authenticationType != "AzureSignalR")
            {
                ConfigureHubConnection(hubConnection, authenticationType, cookie, credentials);
            }

            try
            {
                await hubConnection.Start();
                await notificationHubProxy.Invoke("Send", notificationRequest, notificationResponse);
                hubConnection.Stop();
            }
            catch (Exception e)
            {
                if (hubConnection != null && !string.IsNullOrEmpty(hubConnection.ConnectionId))
                {
                    hubConnection.Stop();
                }
            }
        }
    }
    
}

Azure Function端Hub代码

public class NotificationHub : ServerlessHub
{
    [FunctionName("index")]
    public static IActionResult GetHomePage([HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequest req, ExecutionContext context)
    {
        var path = Path.Combine(context.FunctionAppDirectory, "content", "index.html");
        return new ContentResult
        {
            Content = File.ReadAllText(path),
            ContentType = "text/html",
        };
    }

    [FunctionName("negotiate")]
    public static SignalRConnectionInfo Negotiate(
       [HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequest req,
       [SignalRConnectionInfo(HubName = "NotificationHub")] SignalRConnectionInfo connectionInfo)
    {
        return connectionInfo;
    }
    [Authorize]
    [FunctionName(nameof(Send))]
    public async Task Send([SignalRTrigger] InvocationContext invocationContext, NotificationRequest notificationRequest, NotificationResponse notificationResponse)
    {
        try
        {
            await Clients.Clients(notificationResponse.ConnectionIds).SendAsync("SendNotification", notificationRequest);
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }
    [FunctionName(nameof(OnDisconnected))]
    public async Task OnDisconnected([SignalRTrigger] InvocationContext invocationContext, ILogger logger)
    {
        var response = await APIService.CallDisconnectedTrigger(invocationContext.ConnectionId);
        if (response != null && response.ErrorMessages.Any())
        {
            string errorMessages = string.Join(", ", response.ErrorMessages);
            logger.LogInformation("Some error occured while disconnecting SignalR Connection. " + errorMessages);
        }
    }
}

解决方案

第一步:修正NuGet包引用

确保ASP.NET MVC 5项目安装兼容.NET Framework的Microsoft.AspNetCore.SignalR.Client版本(推荐3.1.x,高版本可能不支持.NET Framework)。

场景1:MVC作为SignalR客户端直接连接Azure SignalR Service

修正PushNotification方法的Azure SignalR分支代码,需先调用negotiate端点获取连接凭证:

// 新增Negotiate响应模型
public class NegotiateResponse
{
    public string Url { get; set; }
    public string AccessToken { get; set; }
}

// 修正后的PushNotification方法(将async void改为async Task避免异步风险)
private static async Task PushNotification(NotificationRequest notificationRequest, NotificationResponse notificationResponse, string signalRNegotiateUrl, string authenticationType, Cookie cookie, ICredentials credentials, string accessToken, string userId, string cacheString)
{
    if (authenticationType == "AzureSignalR")
    {
        // 1. 调用Azure Function的negotiate端点获取连接信息
        var negotiateResponse = await new HttpClient().GetFromJsonAsync<NegotiateResponse>(signalRNegotiateUrl);
        
        // 2. 构建SignalR连接
        var connection = new HubConnectionBuilder()
            .WithUrl(negotiateResponse.Url, options =>
            {
                options.AccessTokenProvider = () => Task.FromResult(negotiateResponse.AccessToken);
                // 传入身份验证凭证(如果需要)
                if (cookie != null) options.Cookies.Add(cookie);
                options.Credentials = credentials;
            })
            .Build();

        try
        {
            await connection.StartAsync();
            // 调用Hub中的Send方法,参数需与Function端定义匹配
            await connection.InvokeAsync("Send", notificationRequest, notificationResponse);
        }
        catch (Exception e)
        {
            Console.WriteLine($"SignalR连接或调用失败:{e.Message}");
        }
        finally
        {
            await connection.StopAsync();
            await connection.DisposeAsync();
        }
    }
    else
    {
        // 保留原有的SignalR V2逻辑
        using (var hubConnection = new HubConnection(signalRUrl))
        {
            IHubProxy notificationHubProxy = hubConnection.CreateHubProxy("NotificationHub");
            ConfigureHubConnection(hubConnection, authenticationType, cookie, credentials);

            try
            {
                await hubConnection.Start();
                await notificationHubProxy.Invoke("Send", notificationRequest, notificationResponse);
                hubConnection.Stop();
            }
            catch (Exception e)
            {
                if (hubConnection != null && !string.IsNullOrEmpty(hubConnection.ConnectionId))
                {
                    hubConnection.Stop();
                }
            }
        }
    }
}

注意:signalRNegotiateUrl为Azure Function的negotiate端点地址,格式如https://你的函数应用名.azurewebsites.net/api/negotiate

场景2:MVC通过HTTP调用Azure Function推送消息(无服务器模式推荐方案)

无需维护SignalR连接,直接调用Function的HTTP端点触发消息推送:

private static async Task PushNotificationViaFunction(NotificationRequest notificationRequest, NotificationResponse notificationResponse, string functionSendUrl, string accessToken)
{
    var client = new HttpClient();
    // 如果Function启用授权,添加Bearer令牌
    if (!string.IsNullOrEmpty(accessToken))
    {
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    }

    // 构造请求体
    var requestBody = new
    {
        notificationRequest = notificationRequest,
        notificationResponse = notificationResponse
    };
    var content = new StringContent(JsonConvert.SerializeObject(requestBody), Encoding.UTF8, "application/json");
    
    var response = await client.PostAsync(functionSendUrl, content);
    if (!response.IsSuccessStatusCode)
    {
        var errorContent = await response.Content.ReadAsStringAsync();
        Console.WriteLine($"调用Function失败:{errorContent}");
    }
}

注意:functionSendUrl为Azure Function中Send方法的地址,格式如https://你的函数应用名.azurewebsites.net/api/Send,需安装Newtonsoft.Json包序列化请求体。

Azure Function端补充说明

  1. 确保应用设置中已配置AzureSignalRConnectionString(指向你的Azure SignalR Service连接字符串)
  2. 若无需授权,可移除Send方法上的[Authorize]特性;若需授权,需配置对应的身份验证规则
  3. Send方法的参数需与客户端调用时的参数完全匹配,确保消息能正确路由到目标客户端

内容的提问来源于stack exchange,提问作者Mohd Anzal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 14:47:54