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

Azure SignalR:未知UserId时如何返回SignalRConnectionInfo?

解决方案

你遇到的核心问题是SignalRConnectionInfoInput属性仅支持编译时常量,无法传入运行时动态获取的UserId,所以不能用属性方式生成连接信息,需要手动调用Azure SignalR SDK的API来动态生成带UserId的ConnectionInfo,同时完成用户加组操作。

步骤1:重构SignalRService,添加动态生成连接信息的方法

修改SignalRService,保留ServiceManager实例(不要在StartAsync里用using销毁,否则后续无法复用),并新增生成ConnectionInfo的方法:

public class SignalRService(IConfiguration configuration) : IHostedService
{
    private ServiceManager _serviceManager;
    private ServiceHubContext _messageHubContext;

    // 新增:生成带指定UserId的连接信息
    public async Task<ClientConnectionInfo> GenerateConnectionInfo(Guid userId, CancellationToken cancellationToken = default)
    {
        return await _serviceManager.CreateClientConnectionInfoAsync(
            hubName: "SomeHub",
            options: new ClientConnectionInfoOptions { UserId = userId.ToString() },
            cancellationToken: cancellationToken);
    }

    public async Task AddUserToGroupAll(Guid userId)
    {
        await _messageHubContext.Groups.AddToGroupAsync(userId.ToString(), "YourTargetGroupName"); // 替换为实际组名
    }

    async Task IHostedService.StartAsync(CancellationToken cancellationToken)
    {
        _serviceManager = new ServiceManagerBuilder()
            .WithOptions(o => o.ConnectionString = configuration[ConfigurationConstants.AzureSignalRConnectionString])
            .BuildServiceManager();
        _messageHubContext = await _serviceManager.CreateHubContextAsync("SomeHub", cancellationToken);
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        await _messageHubContext.DisposeAsync();
    }
}

步骤2:修改Negotiate函数,动态生成ConnectionInfo

将函数改为非静态,通过FunctionContext获取SignalRService实例,先查询数据库拿到UserId,再生成连接信息并完成加组:

[Function("Negotiate")]
public async Task<HttpResponseData> Negotiate(
    [HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequestData req,
    FunctionContext context)
{
    // 1. 从请求中获取用户身份标识(示例:从Header取Bearer Token,根据你的认证方式调整)
    if (!req.Headers.TryGetValues("Authorization", out var authHeaders) || !authHeaders.Any())
    {
        var badResp = req.CreateResponse(HttpStatusCode.Unauthorized);
        await badResp.WriteStringAsync("缺少身份认证信息");
        return badResp;
    }

    var token = authHeaders.First().Replace("Bearer ", string.Empty);
    // 2. 从数据库查询UserId(替换为你的实际查询逻辑)
    Guid userId = await QueryUserIdFromDatabase(token);

    // 3. 获取SignalRService实例
    var signalRService = context.InstanceServices.GetRequiredService<SignalRService>();

    // 4. 生成带UserId的连接信息
    var connectionInfo = await signalRService.GenerateConnectionInfo(userId);

    // 5. 将用户加入目标组
    await signalRService.AddUserToGroupAll(userId);

    // 6. 返回连接信息JSON
    var resp = req.CreateResponse(HttpStatusCode.OK);
    await resp.WriteAsJsonAsync(connectionInfo);
    return resp;
}

// 示例:数据库查询UserId的方法
private async Task<Guid> QueryUserIdFromDatabase(string token)
{
    // 替换为你的实际数据库查询逻辑
    // 例如:return await _dbContext.Users.Where(u => u.AuthToken == token).Select(u => u.Id).FirstAsync();
    return Guid.NewGuid(); // 仅作示例,实际需替换
}

关键说明

  • 为什么不用SignalRConnectionInfoInput:该属性的UserId参数只能接受编译期常量(比如固定字符串、配置项),无法传入运行时从数据库获取的动态值,所以必须手动调用ServiceManager.CreateClientConnectionInfoAsync来生成连接信息。
  • 依赖注入:确保在Program.cs中已注册SignalRService为托管服务:builder.Services.AddHostedService<SignalRService>();
  • 认证调整:示例中保留了AuthorizationLevel.Anonymous,但实际生产环境建议根据认证方式调整为AuthorizationLevel.Function或AuthorizationLevel.User,并完善身份校验逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 10:41:21