使用SignalR構建聊天應用程式

dingshi7798發表於2015-07-16

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:<port>/signalr/hubs指令碼:

<script src="assets/vendor.js"></script>
<script src="http://localhost:53246/signalr/hubs"></script>
<script src="assets/chatr.js"></script>

在此階段中,請注意元素的順序,這很重要,因為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:<port>/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的有用資源:

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

相關文章