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

Angular中使用SignalR调用invoke发送数据报服务端错误如何解决

问题根因

报错由两个必现的参数/返回值不匹配问题直接导致,另外存在一个高概率触发的并发隐患:

  • 入参类型不匹配:后端SendValues方法明确定义入参为string类型的序列化JSON字符串,但前端调用时直接传入了未做序列化的JS数组/对象,SignalR参数绑定阶段就会校验失败,根本进不到方法逻辑直接抛服务端错误。
  • 返回值约定不匹配:后端SendValues是无返回值的async Task方法,但前端invoke声明了泛型返回类型,等待接收服务端返回结果,方法执行完成后无内容返回会直接触发协议层错误。
  • 静态集合线程不安全:Startup中定义的两个全局静态List<TagItemModel>没有做任何并发控制,SignalR默认多线程处理请求,并发调用时随时会触发「集合已修改,枚举操作无法执行」的异常。

修复步骤

1. 后端代码调整

首先修正Hub方法的返回值,增加参数校验和并发控制,建议开发环境先开SignalR详细错误方便排查:

// 1. Startup.ConfigureServices中配置SignalR时开启详细错误(仅开发环境使用)
services.AddSignalR()
    .AddHubOptions<你的Hub类名>(options =>
    {
        options.EnableDetailedErrors = true;
    });

// 2. 修正Hub中的SendValues方法
public async Task<List<TagItemModel>> SendValues(string strValueList) 
{
    // 基础参数校验
    if (string.IsNullOrWhiteSpace(strValueList))
        throw new ArgumentException("传入参数不能为空");
    
    List<TagItemModel> currentData;
    // 捕获反序列化异常
    try
    {
        currentData = JsonConvert.DeserializeObject<List<TagItemModel>>(strValueList);
    }
    catch
    {
        throw new ArgumentException("传入参数不是合法的TagItemModel列表JSON格式");
    }

    // 加锁操作静态集合,避免并发冲突
    lock (Startup.lastValues)
    {
        Startup.tempValues.Clear();
        Startup.tempValues.AddRange(currentData);
        foreach (var item in Startup.tempValues) 
        {
            var tempValue = Startup.lastValues.FirstOrDefault(x => x.Name == item.Name);
            if (tempValue == null) 
            {
                Startup.lastValues.Add(item);
            }
            else
            {
                tempValue.Value = item.Value;
            }
        }
    }

    // 业务逻辑处理完成后再广播消息,保证推送数据和存储数据一致
    await Clients.All.SendAsync("ReceiveValues", strValueList);

    // 返回处理后的结果,匹配前端invoke的返回值约定
    return Startup.lastValues;
}

不建议长期用静态变量存储全局共享业务数据,应用池回收时静态数据会全部丢失,并发场景下锁的性能开销也很高,正式环境建议替换为内存缓存、分布式缓存或者数据库存储。

2. 前端Angular代码调整

修正参数序列化逻辑、类型定义,去掉多余的JSON解析操作,遵循Angular的开发规范不要直接操作DOM:

// 注意参数类型是数组,和你定义的anlikDegerler类型对齐
sendHubConn(message: AnlikSicaklikDegerleri[]) {
    // 把对象序列化成JSON字符串再传给后端,匹配后端string类型入参
    const messageStr = JSON.stringify(message);
    // 泛型类型和后端返回的List<TagItemModel>对齐,这里是数组类型
    this.hubConn.invoke<AnlikSicaklikDegerleri[]>('SendValues', messageStr)
        .then((resp) => {
            // SignalR会自动完成反序列化,不需要再手动JSON.parse
            if (resp?.length > 0) {
                // 不要直接操作document.getElementById,用Angular属性绑定,比如定义demoText成员变量
                this.demoText = resp[0].Name;
            }
        })
        .catch(err => {
            console.error('SignalR调用失败详情:', err);
        });
}

// 测试调用直接传入你定义的数组即可
// this.sendHubConn(this.anlikDegerler);

快速排查技巧

如果调整后仍有问题,按以下顺序定位:

  1. 先在后端SendValues方法第一行加断点:
    • 断点进不去:检查方法名拼写、参数数量/类型是否和前端传的一致,SignalR方法名默认大小写不敏感,但不要出现多余参数
    • 断点能进去:逐行调试,看是反序列化报错、集合操作报错还是客户端推送报错
  2. 开启EnableDetailedErrors后,前端控制台会打印服务端的完整错误栈,不需要盲猜问题点。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 03:51:19