SignalR是面向ASP.NET开发人员的开源库。 这等效于Socket.IO用于Node.js(如果需要,您可以在Stack Overflow上进行比较 )。 SignalR可用于为您的应用程序提供实时Web功能。 通常,如果您喜欢Ember和JavaScript,则可能会倾向于选择Socket.IO并坚持使用JavaScript。 我选择SignalR的原因之一是它具有更扩展的文档和一些参考资源。 此外,您可以免费获得ASP.NET world的所有好处。
在本文中,我将向您展示如何使用SignalR构建简单的聊天应用程序。 如果尚未完成,建议您阅读上一篇名为“ 使用Socket.IO的聊天应用程序 ”,以更全面地了解这些主题,然后比较两种方法的优缺点。
我们将首先创建一个新的Ember应用程序,然后使用ember-cli 。 首先,让我们安装一些依赖项:
$ ember new chatr
$ ember install semantic-ui-ember
在这里,我们将安装语义UI ,这是一个开发框架,可以使用人类友好的HTML帮助创建美观,响应式的布局。
它与Bootstrap非常相似,它将帮助我们确定网站的布局和主题。 完成后,我们必须将其导入到应用程序中。
现在, Brocfile.js
添加到Brocfile.js
文件中:
app.import('bower_components/semantic-ui/dist/semantic.css');
app.import('bower_components/semantic-ui/dist/semantic.js');
现在,我们准备创建路线并添加一些模板。 为此,请执行以下命令:
$ ember g route chat
这将创建我们的Ember路线app/routes/chat.js
和模板app/templates/chat.hbs
。 在向模板添加任何内容之前,我们将使用一些Ember组件来封装模板并使它们可重用。 让我们从chat-room
组件开始:
$ ember g component chat-room
$ ember g component chat-userlist
$ ember g component chat-area
$ ember g component chat-useritem
如您所见,我们有很多组件。 每个组件都有一个关联的模板文件( app/templates/components/chat-room.hbs
)和一个Ember组件脚本文件( app/components/chat-room.js
)。 这样,我们可以隔离聊天功能,从而易于测试和推理。 此时,我们可以将chat-room
添加到我们的聊天路由中:
{
{#chat-room
users=room.users
messages=room.messages
topic=room.topic
onSendChat="sendChat"}}{
{/chat-room}}
users
, messages
和topic
是我们传递给组件的数据,而onSendChat
是由组件触发的操作。 如果您想加深这些概念,可以阅读Ember指南以获得更多信息 。
最后,我们需要一个Ember控制器(以处理聊天路由的逻辑)和一些Ember模型。 为此,请执行以下命令:
$ ember g controller chat
$ ember g model chat-room
$ ember g model chat-user
$ ember g model chat-message
模型是从Ember.Object
继承的有用类。 他们将保存我们的数据并提供与模板的数据绑定。 控制器执行相同的操作,控制器装饰模型并也可以处理用户操作。
打开app/controllers/chat.js
文件,并向其中添加以下代码:
export default Ember.Controller.extend({
initRoom: function(users, messages, topic) {
var room = Room.create({
users: users,
messages: messages,
topic: topic
});
this.set('room', room);
},
addMessage: function(msg) {
var room = this.get('room');
room.get('messages').addObject(msg);
},
userJoin: function(user) {
var room = this.get('room');
room.get('users').addObject(user);
},
actions: {
sendChat: function(msg) {
// use these methods here to test if they are working
//this.addMessage('ME', msg);
//this.userJoin(msg);
}
}
});
addMessage()
和userJoin()
是在需要添加新聊天消息或新用户时可以调用的辅助方法。 sendChat
是用户要发送消息时触发的动作处理程序。 initRoom()
是用于设置绑定的构造函数。 您可以在我们的聊天路线的setupController()
挂钩中使用空数据调用它,以测试一切是否正常运行。
现在,通过添加以下代码来编辑app/routes/chat.js
文件:
export default Ember.Route.extend({
setupController: function(controller) {
// use this method to test everything is working when data is bound.
//controller.initRoom([],[], 'hello world');
}
});
我们在使用SignalR时需要使用的另一个有用的工具是Visual Studio 。 下载之后,打开它并创建一个新的Empty Web Application
项目,并使用“程序包管理器控制台”安装所需的程序包:
Install-Package Microsoft.AspNet.Signalr
该命令将安装SignalR及其所有依赖项,包括“ Microsoft.Owin”。 然后,我们继续创建OWIN启动类以引导我们的服务器。 为此,我们将在App_Start/Startup.cs
文件中包含以下代码:
public class Startup {
public void Configuration(IAppBuilder app) {
app.MapSignalR();
}
}
就是运行基于OWIN的Web服务器。 我们将SignalR中间件添加到OWIN,但是您可以根据需要添加其他中间件(例如身份验证或Web API)。 现在可以通过按F5键来启动我们的SignalR应用程序。 我们没有托管任何数据,因此浏览器不会显示任何对我们有用的信息。 该JavaScript代码由SignalR动态生成,并可供我们的Ember应用程序使用。 它为我们提供了JavaScript方法,这些方法将进一步在服务器端调用方法。
集线器类用于与客户端通信。 它可以在客户端上调用方法,并定义从客户端调用的方法。 每当新客户端连接或对服务器进行方法调用时,SignalR都会创建一个新的Hub类。 现在,让我们看看如何创建大厅Hub:
public class Lobby : Hub {
private IChatRRepository _repository;
public Lobby(IChatRRepository repository) {
_repository = repository;
}
public void Join(string name) {
ChatUser currentUser = new ChatUser(name, Context.ConnectionId);
_repository.AddUser(currentUser);
var users = _repository.Users.ToList();
var topic = "Welcome to EmberJS on SignalR";
Clients.Caller.lobbyEntered(topic, users);
}
public void SendChat(string msg) {
ChatUser user = _repository.GetUserById(Context.ConnectionId);
Clients.All.chatSent(user.Name, msg);
}
public override Task OnDisconnected(bool stopCalled) {
_repository.RemoveUser(Context.ConnectionId);
return base.OnDisconnected(stopCalled);
}
}
根据每个客户端请求,SignalR实例化一个新的Hub实例。 集线器不维护任何客户端状态。 因此,我们需要某种数据库来跟踪客户端状态。 IChatRepository
接口通过提供诸如AddUser()
, RemoveUser()
和Users()
来检索所有用户,从而为我们提供了所需的状态。 可以从客户端调用Hub方法,并且Hub类可以使用Clients
属性调用客户端方法。
在下面,您可以找到一个列表,该列表指定哪些客户端将接收该方法调用:
Clients.All.someMethod()
:所有连接的客户端 Clients.Caller.someMethod()
:仅主叫客户端 Clients.Others.someMethod()
:除调用Clients.Others.someMethod()
所有客户端 Clients.Client(Context.ConnectionId).someMethod()
:特定的客户端 如您所见,它具有直观的API。 someMethod()
是动态调度的,因此可以是任何东西。 有关Hubs API的更多信息,请参阅指南 。
回到我们的示例,我们有两个Hub方法: Join()
和SendChat()
。 客户端连接时,它将使用username
调用Join()
。 我们将用户添加到我们的仓库和呼叫lobbyEntered()
的方法Clients.Caller
。
客户端发送聊天消息时,将调用SendChat()
方法。 我们从存储库中检索调用方,并通过调用Clients.All.chatSent()
方法将消息广播到所有连接的客户端。 反过来,它在所有连接的客户端上调用chatSent()
方法。
最后,有些方法(例如OnConnected()
和OnDisconnected()
可以重写,以便在用户连接/断开连接时得到通知。 有关SignalR API的更多信息,请参阅《 SignalR指南》 。
现在,让我们回到客户端应用程序并集成SignalR。 首先,使用Bower安装SignalR:
$ bower install signalr --save
接下来,将其导入我们的应用程序。 为此,请再次打开Brocfile.js
文件并添加以下行:
app.import('bower_components/signalr/jquery.signalR.js');
最后,在您的app/index.html
页面中包含http://localhost:
脚本:
在此阶段中,请注意元素的顺序,这很重要,因为SignalR是作为jQuery插件导出的。 因此,我们需要首先包含jQuery(在assets/vendor.js
内部),然后在/signalr/hubs
包含动态脚本文件,最后在它之上包含我们的应用程序( assets/chatr.js
)。
当我们的应用程序启动时,我们必须创建一个SignalR连接,然后在我们的控制器中使用它。 这里的架构取决于您。 我们将使用Ember初始化程序将SignalR注入到我们的路由中。 让我们看看如何使用先前引用的ember-cli创建它。
$ ember g initializer signalr
现在让我们初始化SignalR并将其注入到我们的路由中。 以下代码片段进入app/initializer/signalr.js
文件:
import SignalRConnection from 'chatr/utils/net/chatr-realtime';
export function initialize(container, application) {
var realtime = new SignalRConnection('http:/localhost:/signalr');
application.register('realtime:signalr', realtime, { instantiate: false });
application.inject('route:chat', 'signalr', 'realtime:signalr');
}
SignalRConnection
是SignalRConnection
的包装类,它肯定会使我们的生活更轻松。 我们创建它,并使用依赖注入将其注入聊天路由。 同样, 如果您需要更多信息,请参阅完整的Ember指南 。
您可以检出SignalRConnection类以了解其实现方式。 这里有两种有趣的方法:
configureHubs(ctrl) {
this.OnLobby = new LobbyCallbacks(this, ctrl);
var lobby = Ember.$.connection.lobby;
lobby.client['lobbyEntered'] = this.OnLobby['lobbyEntered'];
lobby.client['chatSent'] = this.OnLobby['chatSent'];
}
在开始SignalR连接之前,我们需要设置服务器可以在大厅集线器上调用的客户端方法。 Ember.$.connection
是我们的SignalR连接,而Ember.$.connection.lobby
是我们的大厅枢纽。 这些是在动态生成的SignalR代码中定义的。 我们通过将方法分配给我们的大厅中心上的client
属性(即Ember.$.connection.lobby.client
属性)来设置方法。
在我们的示例中,它们在LobbyCallbacks类中定义:
start(name) {
var self = this;
var hub = Ember.$.connection.hub;
hub.error(function(reason) {
console.log('connection error: ' + reason);
});
return hub.start({
withCredentials: false
}).then(function() {
console.log('connected');
Ember.$.connection.lobby.server.join(name);
});
}
定义客户端方法后,我们可以使用此方法启动应用程序。 首先,我们获得对Ember.$.connection.hub
的引用,在这里我们设置error
钩子以获取有关任何连接错误的通知。 最后,我们运行一个start
调用以启动连接,并有一个诺言。
连接后,我们称为Ember.$.connection.lobby.server.join()
。 此方法将在服务器端Lobby
Hub上调用Join()
方法。 有关SignalR客户端API的更多信息,请访问SignalR指南 。
此时,我们可以从Ember应用程序连接到服务器。 但是,我们可能会遇到一些浏览器错误,如下所示:
XMLHttpRequest cannot load http://localhost:53246/signalr/negotiate?clientProtocol=1.5&connectionData=%5B%7B%22name%22%3A%22lobby%22%7D%5D&_=1433597715652. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://192.168.56.103:4200' is thus, so not allowed access.
此错误可能是由于您的服务器和客户端位于不同的域中引起的。 您需要允许服务器上的CORS
绕过它。 因此,让我们在Visual Studio软件包管理器控制台上安装软件包:
Install-Package Microsoft.Owin.Cors
然后,配置Owin中间件以允许跨域请求(编辑App_Start/Startup.cs
文件):
public void Configuration(IAppBuilder app) {
app.Map("/signalr", map =>
{
var corsPolicy = new CorsPolicy
{
AllowAnyHeader = true,
AllowAnyMethod = true
};
// Add the domain where your client is hosted on.
corsPolicy.Origins.Add("http://192.168.56.103:4200");
map.UseCors(new CorsOptions
{
PolicyProvider = new CorsPolicyProvider {
PolicyResolver =
r => Task.FromResult(corsPolicy)
}
});
map.RunSignalR(config);
});
}
在本文中,我们已经看到了如何通过几个简单的步骤将SignalR与Ember粘合在一起并创建聊天应用程序。 如果您想看到它的实际效果,可以在chatembar上观看一个有趣的现场演示 ,如果您想动手做项目,则可以在GitHub上找到完整的源代码,包括客户端和服务器端 。 此外,您可以参考另一个使用SignalR的协作聊天应用程序的出色示例,称为JabbR 。
我强烈建议您加深一些要点,我没有机会在本文中介绍:OWIN和身份验证。 好消息是SignalR不需要任何特殊的授权,因为它可以与现有的ASP.NET身份验证解决方案(例如ASP.NET Identity)一起使用。
如果您想了解更多,这里有一些有关Owin,SignalR和ASP.NET Identity的有用资源:
From: https://www.sitepoint.com/building-chat-application-signalr/