【问题标题】:Unable to make SignalR, Web API, and SystemJS work together无法使 SignalR、Web API 和 SystemJS 一起工作
【发布时间】:2016-12-25 01:47:12
【问题描述】:

我正在尝试在我的 Web API 上构建一个通知服务来通知 JavaScript (Aurelia) 客户端 (WebApp)。我的 Web API 和 WebApp 位于不同的域中。

我有一个简单的NotificationHub

public class NotificationHub:Hub
{
    public void NotifyChange(string[] values)
    {
        Clients.All.broadcastChange(values);
    }
}

我在 Web API 的Startup 中配置 SignalR 如下:

public void Configuration(IAppBuilder app)
{
    HttpConfiguration httpConfig = new HttpConfiguration();

    var cors = new EnableCorsAttribute("http://localhost:9000", "*", "*");
    httpConfig.EnableCors(cors);

    app.MapSignalR();

    WebApiConfig.Register(httpConfig);
    ...
}

在我的 WebApp 中,我尝试从我的 Web API 访问 signalr/hubs,如下所示:

Promise.all([...
        System.import('jquery'),
        System.import('signalr'), ...
            ])
       .then((imports) => {
            return System.import("https://localhost:44304/signalr/hubs");
            });

我还在我的config.js 中添加了meta 部分:

System.config({
    ...
    meta: {
        "https://*/signalr/hubs": { //here I also tried with full url
            "format": "global",
            "defaultExtension": false,
            "defaultJSExtension": false,
            "deps": ["signalr"]
        }
    }
});

尽管有这些配置,我仍然有以下问题:

  1. 来自 WebApp 的对 signalr/hubs 的请求以 https://localhost:44304/signalr/hubs.js 的形式发出,并返回 HTTP 404。请注意,浏览 https://localhost:44304/signalr/hubs 会返回 hubs 脚本。
  2. $.connection("https://localhost:44304/signalr").start() 上,我收到以下错误:

XMLHttpRequest 无法加载 https://localhost:44304/signalr/negotiate?clientProtocol=1.5&_=1471505254387。请求的资源上不存在“Access-Control-Allow-Origin”标头。 Origin 'http://localhost:9000' 因此不允许访问。

请让我知道我在这里缺少什么?

更新: 使用适当的 CORS 配置和@kabaehr 建议的gist,我现在可以连接到 SignalR 集线器。但是,广播(推送通知)仍然不起作用。

我的 SignalR 配置:

public static class SignalRConfig
{
    public static void Register(IAppBuilder app, EnableCorsAttribute cors)
    {

        app.Map("/signalr", map =>
        {
            var corsOption = new CorsOptions
            {
                PolicyProvider = new CorsPolicyProvider
                {
                    PolicyResolver = context =>
                    {
                        var policy = new CorsPolicy { AllowAnyHeader = true, AllowAnyMethod = true, SupportsCredentials = true };

                        // Only allow CORS requests from the trusted domains.
                        cors.Origins.ForEach(o => policy.Origins.Add(o));

                        return Task.FromResult(policy);
                    }
                }
            };
            map.UseCors(corsOption).RunSignalR();
        });
    }
}

我在Startup使用如下:

var cors = new EnableCorsAttribute("http://localhost:9000", "*", "*");
httpConfig.EnableCors(cors);

SignalRConfig.Register(app, cors);

WebApiConfig.Register(httpConfig);

我正在尝试按如下方式推送通知:

GlobalHost.ConnectionManager.GetHubContext<NotificationHub>().Clients.All.broadcastChange(new[] { value });

但是,我的客户没有收到broadcastChange 的通知。我假设在使用gist 时不必显式导入https://localhost:44304/signalr/hubs

【问题讨论】:

  • 你的来源好像没有使用https?首先尝试"*" 来解决这个问题。
  • @MarkC。感谢您的评论。我试过"*",但是,我不能说它是否有效,因为SystemJSsignalr/hubs 的末尾附加了.js,我得到了一个404。我认为这需要以某种方式处理,但我不确定如何处理。
  • 有几个系统 js 插件可以防止像github.com/systemjs/plugin-text 这样附加“.js”,但我不知道这是否是问题。
  • @kabaehr 有点晚了 :) 但正如告知的那样,我已经发布了答案。想看的可以去看看。

标签: javascript c# asp.net-web-api signalr systemjs


【解决方案1】:

这只是为了分享我的发现以及为我解决问题的方法。

首先,signalr/hubs 只是从服务器端代码自动生成的代理。如果您可以创建自己的 SignalR 代理客户端,则无需使用该代理。下面是一个基于@kabaehr 提到的gist 创建的简单SignalR 客户端。到目前为止,SignalR 客户端本质上非常简单。

export class SignalRClient {

    public connection = undefined;
    private running: boolean = false;

    public getOrCreateHub(hubName: string) {
        hubName = hubName.toLowerCase();
        if (!this.connection) {
            this.connection = jQuery.hubConnection("https://myhost:myport");
        }

        if (!this.connection.proxies[hubName]) {
            this.connection.createHubProxy(hubName);
        }

        return this.connection.proxies[hubName];
    }

    public registerCallback(hubName: string, methodName: string, callback: (...msg: any[]) => void,
        startIfNotStarted: boolean = true) {

        var hubProxy = this.getOrCreateHub(hubName);
        hubProxy.on(methodName, callback);

        //Note: Unlike C# clients, for JavaScript clients, at least one callback 
        //      needs to be registered, prior to start the connection.
        if (!this.running && startIfNotStarted)
            this.start();
    }

    start() {
        const self = this;
        if (!self.running) {
            self.connection.start()
                .done(function () {
                    console.log('Now connected, connection Id=' + self.connection.id);
                    self.running = true;
                })
                .fail(function () {
                    console.log('Could not connect');
                });
        }
    }
}

这里需要注意的重要一点是,对于 JavaScript SignalR 客户端,我们需要在开始连接之前注册至少一个回调方法。

有了这样的客户端代理,您可以如下使用它。虽然下面的代码示例一般使用aurelia-framework,但attached()中的SignalR部分与Aurelia无关。

import {autoinject, bindable} from "aurelia-framework";
import {SignalRClient} from "./SignalRClient";

@autoinject
export class SomeClass{

    //Instantiate SignalRClient.
    constructor(private signalRClient: SignalRClient) {
    }

    attached() {
        //To register callback you can use lambda expression...
        this.signalRClient.registerCallback("notificationHub", "somethingChanged", (data) => {
            console.log("Notified in VM via signalr.", data);
        });

        //... or function name.
        this.signalRClient.registerCallback("notificationHub", "somethingChanged", this.somethingChanged);
    }

    somethingChanged(data) {
        console.log("Notified in VM, somethingChanged, via signalr.", data);
    }
}

这是解决方案的关键。对于问题中已经提到了与启用 CORS 相关的部分。更多详细信息,您可以参考以下链接:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-24
    • 1970-01-01
    相关资源
    最近更新 更多