无法使 SignalR、Web API 和 SystemJS 协同工作
Unable to make SignalR, Web API, and SystemJS work together
我正在尝试在我的 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"]
}
}
});
尽管有这些配置,我仍然有以下问题:
- WebApp 对
signalr/hubs
的请求是 https://localhost:44304/signalr/hubs.js
,返回的是 HTTP 404
。请注意,浏览 https://localhost:44304/signalr/hubs
returns hubs
脚本。
- 在
$.connection("https://localhost:44304/signalr").start()
,我收到以下错误:
XMLHttpRequest cannot load https://localhost:44304/signalr/negotiate?clientProtocol=1.5&_=1471505254387. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9000' is therefore not allowed access.
请让我知道我在这里遗漏了什么?
更新:
使用适当的 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
这只是为了分享我的发现以及为我解决问题的方法。
所以首先,signalr/hubs
只是服务器端代码自动生成的代理。如果您可以创建自己的 SignalR 代理客户端,则无需使用该代理。下面是一个这样简单的 SignalR 客户端,它是基于@kabaehr 提到的 gist 创建的。到目前为止,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 相关的部分。更多详细信息,您可以参考以下链接:
我正在尝试在我的 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"]
}
}
});
尽管有这些配置,我仍然有以下问题:
- WebApp 对
signalr/hubs
的请求是https://localhost:44304/signalr/hubs.js
,返回的是HTTP 404
。请注意,浏览https://localhost:44304/signalr/hubs
returnshubs
脚本。 - 在
$.connection("https://localhost:44304/signalr").start()
,我收到以下错误:
XMLHttpRequest cannot load https://localhost:44304/signalr/negotiate?clientProtocol=1.5&_=1471505254387. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9000' is therefore not allowed access.
请让我知道我在这里遗漏了什么?
更新: 使用适当的 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
这只是为了分享我的发现以及为我解决问题的方法。
所以首先,signalr/hubs
只是服务器端代码自动生成的代理。如果您可以创建自己的 SignalR 代理客户端,则无需使用该代理。下面是一个这样简单的 SignalR 客户端,它是基于@kabaehr 提到的 gist 创建的。到目前为止,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 相关的部分。更多详细信息,您可以参考以下链接: