如何将自定义 headers 添加到 SignalR 的 Typescript 客户端?

How do I add custom headers to SignalR's Typescript client?

我正在尝试在 header 中传递不记名令牌以授权客户端连接。 SignalR 集线器通过从 header 获取承载令牌来授权客户端。我无法修改 SingalR 中心代码以使用查询字符串来获取令牌,也无法使用 built-in JWT 令牌功能。

如何向 SignalR typescript 客户端添加自定义 header?

我唯一能想到的就是重写 HttpClient 以添加 header 但是我不确定这是否可行或如何执行此操作。

我正在使用 Angular 8 中的 @microsoft/signalr 和最新的 @microsoft/signalr 包。

您可以在建立连接时在选项中添加自定义的HttpClient。

看起来像这样:

class CustomHttpClient extends DefaultHttpClient {
  public send(request: HttpRequest): Promise<HttpResponse> {
    request.headers = { ...request.headers, "customHeader": "value" };
    return super.send(request);
  }
}

let hubConnection = new HubConnectionBuilder()
    .withUrl(url, { httpClient: new CustomHttpClient() })
    .build();

可以从 DefaultHttpClient 继承并在您的发送中调用 DefaultHttpClient 的发送,因此您不需要自己实现 Http 调用。

第一个 class 支持已通过 IHttpConnectionOptions 上的 headers 属性 在 @microsoft/signalr 包的 5.0 或更高版本中实现,请参阅 https://github.com/dotnet/aspnetcore/issues/14588 已解决 GitHub 问题。

如果这只是为了传递不记名令牌,则无需进行自定义 headers,要传递访问令牌,您可以使用带有 IHttpConnectionOptions 的 withUrl 重载,此接口具有可以像这样使用的 accessTokenFactory :

yourAuthServicePromise.then(
  (data) => {
    if (data.['result']['accessToken']) {
      this.hubConnection = new signalR.HubConnectionBuilder()
        .withUrl('https://SIGNALR_SERVER_HUB_URL', {
          accessTokenFactory: () => {
            return data.['result']['accessToken'];
          }
        } as signalR.IHttpConnectionOptions)
        .build();
      this.hubConnection.start()
    }
  }
);

不要忘记在 hub 服务器中启用 cors 策略以允许 JS 客户端来源

我需要与@Brennan 的答案一模一样的东西。

我刚刚填补了一些缺失的空白,以获得更多的复制和粘贴答案。我正在使用它并且它有效,谢谢@Brennan

import * as signalR from "@aspnet/signalr";

// You can create a factory which signalR will used to generate an access token on each request
const getBearerToken = () => "MY TOKEN GETTING FUNCTION"

// This is a custom method which creates all the headers I need for my authentication
const getAuthHeaders = () => ({ collection: "", of: "", headers:""})

// As per @Brennan's answer I used his advice and extened the default one
class CustomHttpClient extends signalR.DefaultHttpClient {
  constructor() {
    super(console); // the base class wants an signalR.ILogger, I'm not sure if you're supposed to put *the console* into it, but I did and it seemed to work
  }

  // So far I have only overriden this method, and all seems to work well
  public async send(request: signalR.HttpRequest): Promise<signalR.HttpResponse> {
    var authHeaders = getAuthHeaders(); // These are the extra headers I needed to put in
    request.headers = { ...request.headers, ...authHeaders };
    // Now we have manipulated the request how we want we can just call the base class method
    return super.send(request);
  }
}

 let connection = new signalR.HubConnectionBuilder()
    .withUrl(`${apiUrl}/web-sockets/stripeHub`, {
        accessTokenFactory: getBearerToken, // I also needed this, but you may not need it
        httpClient: new CustomHttpClient(), // This is the CustomClient we defined above which adds the headers to each request going out
     }) 
    .build();

创建集线器连接构建器时有一个名为 headers 的选项。并且此选项仅在您使用 @microsoft/signalr npm 包时可用。这是 signalr 的活跃回购,旧的 (@aspnet/signalr) 已存档。

所以使用新的 npm 包,你可以像下面那样做

let connection = new signalR.HubConnectionBuilder()
.withUrl("/chat", { 
 headers: { 
   "custom-header-name": "value" 
 },
 withCredentials: (true/false) // if you dont mention this value (if undefined by default it will set to true (which i didnt wanted in my case)
})
.build();

注意: 请记住,默认情况下自动重新连接为 false,因此请添加 withUrl().withAutomaticReconnect() 如果你愿意。

更多关于 options interface