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:
- Аутентификация на основе токенов с использованием ASP.NET Web API 2, Owin и Identity
- Добавление минимальной аутентификации OWIN Identity в существующее приложение ASP.NET MVC
- ASP.NET Identity Рекомендуемые ресурсы
- О чем этот Оуэн?
- Начало работы с проектом Katana
- Нэнси: легкая, простая церемония, инфраструктура для построения HTTP-сервисов на .Net и Mono