使用SignalR构建聊天应用程序

SignalR是面向ASP.NET开发人员的开源库。 这等效于Socket.IO用于Node.js(如果需要,您可以在Stack Overflow上进行比较 )。 SignalR可用于为您的应用程序提供实时Web功能。 通常,如果您喜欢Ember和JavaScript,则可能会倾向于选择Socket.IO并坚持使用JavaScript。 我选择SignalR的原因之一是它具有更扩展的文档和一些参考资源。 此外,您可以免费获得ASP.NET world的所有好处。

在本文中,我将向您展示如何使用SignalR构建简单的聊天应用程序。 如果尚未完成,建议您阅读上一篇名为“ 使用Socket.IO的聊天应用程序 ”,以更全面地了解这些主题,然后比较两种方法的优缺点。

ember-cli入门

我们将首先创建一个新的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}}

usersmessagestopic是我们传递给组件的数据,而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构建服务器端

我们在使用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指南》 。

使用Ember在客户端上设置SignalR

现在,让我们回到客户端应用程序并集成SignalR。 首先,使用Bower安装SignalR:

$ bower install signalr --save

接下来,将其导入我们的应用程序。 为此,请再次打开Brocfile.js文件并添加以下行:

app.import('bower_components/signalr/jquery.signalR.js');

最后,在您的app/index.html页面中包含http://localhost:/signalr/hubs脚本:



在此阶段中,请注意元素的顺序,这很重要,因为SignalR是作为jQuery插件导出的。 因此,我们需要首先包含jQuery(在assets/vendor.js内部),然后在/signalr/hubs包含动态脚本文件,最后在它之上包含我们的应用程序( assets/chatr.js )。

使用Ember初始化程序将SignalR注入聊天路由

当我们的应用程序启动时,我们必须创建一个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');
}

SignalRConnectionSignalRConnection的包装类,它肯定会使我们的生活更轻松。 我们创建它,并使用依赖注入将其注入聊天路由。 同样, 如果您需要更多信息,请参阅完整的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指南 。

与CORS打交道

此时,我们可以从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的有用资源:

  • 使用ASP.NET Web API 2,Owin和身份的基于令牌的身份验证
  • 向现有的ASP.NET MVC应用程序中添加最少的OWIN身份验证
  • ASP.NET Identity推荐资源
  • 这是Owin Stuff的事吗?
  • Katana项目入门
  • Nancy:轻便,低礼仪的框架,用于在.Net和Mono上构建基于HTTP的服务

From: https://www.sitepoint.com/building-chat-application-signalr/

你可能感兴趣的:(javascript,ui,测试)