Статьи

Создание приложения для чата с SignalR

SignalR — это библиотека с открытым исходным кодом для разработчиков ASP.NET. Это эквивалентно тому, что Socket.IO для Node.js (если вы хотите, вы можете проверить сравнение переполнения стека ). SignalR может использоваться для предоставления веб-функций в реальном времени вашим приложениям. Обычно, если вы знакомы с Ember и JavaScript, вы можете выбрать Socket.IO и придерживаться JavaScript. Одна из причин, по которой я выбрал SignalR, заключается в том, что он имеет более расширенную документацию и несколько ресурсов для ссылок. Более того, вы можете получить все преимущества мира ASP.NET бесплатно.

В этой статье я покажу вам, как создать простое приложение для чата с использованием SignalR. Если вы еще этого не сделали, я предлагаю вам прочитать мое предыдущее приложение под названием « Приложение чата с использованием Socket.IO », чтобы получить более полный обзор этих тем, а затем сравнить плюсы и минусы обоих подходов.

Начало работы с ember-cli

Мы начнем с создания нового приложения Ember и будем использовать ember-cli . В качестве первой задачи давайте установим некоторые зависимости:

$ ember new chatr $ ember install semantic-ui-ember 

Здесь мы устанавливаем semantic-ui, который является средой разработки, которая помогает создавать красивые адаптивные макеты с использованием удобного для человека HTML. Это очень похоже на Bootstrap , и это поможет нам с макетом и темой сайта. После этого мы должны импортировать его в приложение.

Теперь добавьте следующие строки в файл Brocfile.js :

 app.import('bower_components/semantic-ui/dist/semantic.css'); app.import('bower_components/semantic-ui/dist/semantic.js'); 

Теперь мы готовы создать наши маршруты и добавить шаблон. Для этого выполните следующую команду:

 $ ember g route chat 

Это создаст наше 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

Еще один полезный инструмент, который нам нужно использовать при работе с 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. Мы добавляем промежуточное программное обеспечение SignalR в OWIN, но при необходимости вы можете добавить другое промежуточное программное обеспечение (например, аутентификацию или веб-API). Теперь наше приложение SignalR можно запустить, нажав F5 . Мы не размещаем никаких данных, поэтому браузер не покажет ничего полезного для нас. Этот код JavaScript динамически генерируется SignalR и готов к использованию нашим приложением Ember. Он предоставляет нам методы JavaScript, которые в дальнейшем будут вызывать методы на стороне сервера.

Создание Лобби Хаба

Класс Hub используется для связи с клиентом. Он может вызывать методы на клиенте и определять методы, которые вызываются из клиента. SignalR создает новый класс-концентратор каждый раз, когда новый клиент подключается к серверу или вызывает метод. На данный момент, давайте посмотрим, как мы можем создать лобби 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.Client(Context.ConnectionId).someMethod() : определенный клиент

Как видите, он имеет интуитивно понятный API. someMethod() отправляется динамически, поэтому это может быть что угодно. Для получения дополнительной информации о Hubs API, пожалуйста, обратитесь к руководствам .

Возвращаясь к нашему примеру, у нас есть два метода Hub: Join() и SendChat() . Когда клиент подключается, он вызывает Join() с username . Мы добавляем пользователя в наш репозиторий и вызываем lobbyEntered() в Clients.Caller .

Метод SendChat() вызывается, когда клиент отправляет сообщение чата. Мы извлекаем абонента из репозитория и транслируем сообщение всем подключенным клиентам, вызывая метод Clients.All.chatSent() . В свою очередь, он вызывает метод chatSent() на всех подключенных клиентах.

Наконец, есть некоторые методы, такие как OnConnected() и OnDisconnected() которые мы можем переопределить, чтобы получать уведомления, когда пользователь подключается / отключается. Для получения дополнительной информации об SignalR API посмотрите Руководство SignalR .

Настройка SignalR на стороне клиента с Ember

Теперь вернемся к нашему клиентскому приложению и интегрируем SignalR. Во-первых, установите SignalR, используя Bower :

 $ bower install signalr --save 

Затем импортируйте его в наше приложение. Для этого снова Brocfile.js файл Brocfile.js и добавьте следующую строку:

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

Наконец, http://localhost:<port>/signalr/hubs скрипт http://localhost:<port>/signalr/hubs в свою страницу app/index.html :

 <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 ).

Внедрение SignalR в маршрут чата с использованием инициализатора Ember

Когда наше приложение запускается, мы должны создать соединение 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'); } 

SignalRConnection — это класс-оболочка для SignalR, который, несомненно, сделает нашу жизнь проще. Мы создаем его и внедряем в маршрут чата, используя внедрение зависимостей. Опять же, если вам нужна дополнительная информация, пожалуйста, обратитесь к полному доступному руководству 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() . Этот метод вызовет метод Join() на Lobby хабе на стороне сервера. Для получения дополнительной информации о клиентском API SignalR, пожалуйста, посетите Руководства 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: