官方文档地址:

ASP.NET Core SignalR configuration | Microsoft Learn

注意事项:

1. 浏览器中websocket 不支持自定义请求头, 如果需要自定义请求头, 只能使用 http 方式, 对应配置项: withUrl 第二个参数中增加 transport: SignalR.HttpTransportType.LongPolling 配置

.withUrl(`/ProductClientMsgHub}`, {
      transport: SignalR.HttpTransportType.LongPolling, // 使用 LongPolling 
      headers: defaultHeaders,
    })

2. token 携带可以使用官方推荐的 accessTokenFactory

.withUrl(`/ProductClientMsgHub}`, {
      skipNegotiation: true,
      transport: SignalR.HttpTransportType.WebSockets, 
      accessTokenFactory: () => util.cookies.get('token'), // 自动附加 Authorization Bearer
    })

3. withUrl 可以使用的配置项

示例代码:

import * as SignalR from "@microsoft/signalr";
import { Notification } from "element-ui";
import util from "@/libs/util.js";
import store from "@/store/index";

let connection = null;
const baseUrl = '192.168.x.x:xxxx'

// 创建连接
const createConnection = (headers = {}) => {
  // 设置连接头信息
  const defaultHeaders = {
    'LineId': headers.LineId || '',
    'OperationId': headers.OperationId || '',
    'ClientType': headers.ClientType || 'PC',
    'OrganizeId': headers.OrganizeId || Number(localStorage.getItem('organizeId')),
    'Authorization': `Bearer ${util.cookies.get('token')}`
  }

  console.log('创建连接,headers:', defaultHeaders)

  connection = new SignalR.HubConnectionBuilder()
    .configureLogging(SignalR.LogLevel.Information)
    .withUrl(`${baseUrl}/ProductClientMsgHub?LineId=${headers.LineId}&OperationId=${headers.OperationId}&ClientType=${headers.ClientType}&OrganizeId=${headers.OrganizeId}`, {
      skipNegotiation: true, // 跳过协商
      transport: SignalR.HttpTransportType.WebSockets,  
      // transport: SignalR.HttpTransportType.LongPolling,
      withCredentials: true,
      // headers: defaultHeaders,
      accessTokenFactory: () => util.cookies.get('token'), // 自动附加 Authorization Bearer
    })
    .withAutomaticReconnect({
      nextRetryDelayInMilliseconds: () => {
        return 5000; // 每5秒重连一次
      },
    })
    .build();

  connection.keepAliveIntervalInMilliseconds = 15 * 1000; // 心跳检测15s
  connection.serverTimeoutInMilliseconds = 30 * 60 * 1000; // 超时时间30m

  // 断开连接
  connection.onclose(async (error) => {
    console.log('SignalR 连接断开')
    if (error) {
      console.error('断开原因:', error)
    }
  })

  // 重连中
  connection.onreconnecting((error) => {
    console.log('SignalR 正在重连...')
    if (error) {
      console.error('重连原因:', error)
    }
    Notification({
      title: "提示",
      message: "服务器已断线,正在重连...",
      type: "warning",
      position: "bottom-right",
    })
  })

  // 重连成功
  connection.onreconnected((connectionId) => {
    console.log('SignalR 重连成功!')
    console.log('新的连接ID:', connectionId)
    Notification({
      title: "提示",
      message: "服务器重连成功",
      type: "success",
      position: "bottom-right",
    })
  })

  return connection;
}

// 启动连接
const startConnection = async (headers = {}) => {
  try {
    // 如果连接不存在或已断开,创建新连接
    if (!connection || connection.state === SignalR.HubConnectionState.Disconnected) {
      console.log('正在连接 SignalR 服务...', headers)
      connection = createConnection(headers)
      await connection.start()
      console.log('SignalR 连接成功!')
      console.log('连接状态:', connection.state)
      console.log('连接ID:', connection.connectionId)
      return connection
    } else {
      console.log('当前连接状态:', connection.state)
      return connection
    }
  } catch (err) {
    console.error('SignalR 连接失败:', err)
    console.error('错误详情:', {
      message: err.message,
      stack: err.stack
    })
  }
}

// 获取连接实例
const getConnection = () => {
  if (!connection) {
    console.warn('SignalR 连接未创建,请先调用 startConnection')
    return null
  }
  return connection
}

export { getConnection as signalR, startConnection };

最终效果:

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐