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

SignalR .NET7中OnConnectedAsync内调用InvokeAsync报错问题

.NET 7 SignalR Client Results 报错:"Client results inside OnConnectedAsync Hub methods are not allowed" 解决

问题场景

在使用.NET 7的SignalR Client Results功能时触发错误:"Client results inside OnConnectedAsync Hub methods are not allowed"。业务逻辑是通过OnConnectedAsync方法缓存客户端的Connection ID,后续通过该ID向特定客户端发送消息并等待响应。

触发异常的代码

调用客户端方法时抛出异常的代码行:

return await Clients.Client(connectionMetadata.ConnectionId).InvokeAsync<bool>("CreateConnection", message, CancellationToken.None);

完整Hub类代码:

public class ConnectionCreationHub : Hub
{
    private readonly List<ConnectionMetadata> connectionIdsCache;
    private readonly ITokenValidator2 validator;

    public ConnectionCreationHub(
        ITokenValidator2 validator,
        List<ConnectionMetadata> connectionIdsCache)
    {
        this.validator = validator;
        this.connectionIdsCache = connectionIdsCache;
    }

    public async Task<bool> SendCreateConnectionMessage(
        Guid userId,
        string message)
    {
        var connectionMetadata =
            connectionIdsCache
                .FirstOrDefault(entry => entry.UserId.Equals(userId));

        if (connectionMetadata == null)
        {
            throw new Exception($"Client with userId {userId} not connected");
        }

        // 此行抛出错误:System.InvalidOperationException: Client results inside OnConnectedAsync Hub methods are not allowed.
        return await Clients.Client(connectionMetadata.ConnectionId).InvokeAsync<bool>("CreateConnection", message, CancellationToken.None);
    }

    public override async Task OnConnectedAsync()
    {
        // 验证授权令牌并缓存Connection ID
        var tokenClaims = await this.AuthorizeOrThrow(validator);

        var userId = tokenClaims.UserId;

        connectionIdsCache.Add(new(userId, Context.ConnectionId));

        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        // 客户端断开连接时清理缓存
        var connectionMetadata =
            connectionIdsCache
                .FirstOrDefault(entry => entry.ConnectionId == Context.ConnectionId);

        if (connectionMetadata != null)
        {
            connectionIdsCache.Remove(connectionMetadata);
        }

        await base.OnDisconnectedAsync(exception);
    }
}

异常详情

仅使用InvokeAsync时触发错误,SendAsync可正常运行,异常信息如下:

System.InvalidOperationException
  HResult=0x80131509
  Message=Client results inside OnConnectedAsync Hub methods are not allowed.
  Source=Microsoft.AspNetCore.SignalR.Core
  StackTrace:
   at Microsoft.AspNetCore.SignalR.Internal.HubCallerClients.NoInvokeSingleClientProxy.InvokeCoreAsync[T](String method, Object[] args, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.SignalR.ClientProxyExtensions.InvokeAsync[T](ISingleClientProxy clientProxy, String method, Object arg1, CancellationToken cancellationToken)
   at ConnectionCreationHub.<SendCreateConnectionMessage>d__3.MoveNext() in *\ConnectionCreationHub.cs:line 42

解决方案

1. 调整缓存添加时机

将Connection ID的缓存操作放到base.OnConnectedAsync()之后执行,确保客户端连接完全建立后再记录信息:

public override async Task OnConnectedAsync()
{
    var tokenClaims = await this.AuthorizeOrThrow(validator);
    var userId = tokenClaims.UserId;
    
    // 先完成SignalR基础连接流程
    await base.OnConnectedAsync();
    
    // 再缓存Connection ID
    connectionIdsCache.Add(new(userId, Context.ConnectionId));
}

2. 确保调用时机正确

避免在客户端连接初始化阶段(OnConnectedAsync执行过程中)调用SendCreateConnectionMessage方法。要求客户端在连接成功后(例如收到连接成功的通知),再发起对该Hub方法的调用,此时客户端已完全就绪,可以处理需要返回结果的InvokeAsync请求。

3. 替换线程不安全的缓存容器

当前使用的List<ConnectionMetadata>不是线程安全的,多客户端并发连接时可能出现数据冲突。建议改用ConcurrentDictionary<Guid, ConnectionMetadata>存储用户与Connection ID的映射:

// 注入时替换为ConcurrentDictionary
private readonly ConcurrentDictionary<Guid, ConnectionMetadata> connectionIdsCache;

// 添加缓存的代码改为
connectionIdsCache.TryAdd(userId, new(userId, Context.ConnectionId));

// 清理缓存的代码改为
connectionIdsCache.TryRemove(userId, out _);

4. 调整交互模式(可选)

如果需要在客户端连接时触发需要响应的操作,可以改为客户端主动发起请求的模式:客户端连接完成后,主动调用Hub的某个方法,服务器再根据业务逻辑返回结果或触发后续流程,避免服务器在连接初始化阶段主动调用客户端的InvokeAsync。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 15:15:10