Бекенд на C# Web Api
Дополнение: SignalR
SignalR — это библиотека для ASP.NET, которая упрощает процесс добавления функциональности реального времени в приложения. Она дает возможность реализовать модель «publisher-subscriber» на стороне сервера. Клиенты подписываются на так называемый хаб, а он, в свою очередь, может отправлять им обновления.

Таким образом, наш сервер на C# WebApi может отправлять уведомления всем клиентам, или каким-то определенным клиентам, в реальном времени. Это особенно может быть полезно в бекенде для игровых приложений.
Сперва необходимо установить пакет
Microsoft.AspNetCore.SignalR.Common.
Далее создаем наш хаб. Это класс, унаследованный от
Hub:
using Microsoft.AspNetCore.SignalR;
public class NotificationHub : Hub
{
public async Task SendMessageToUser(string userId, string message)
{
await Clients.User(userId).SendAsync("ChatMessage", message);
}
public async Task SendMessageToAll(string message)
{
await Clients.All.SendAsync("ChatMessage", message);
}
}Этот класс может быть пустой, методы SendMessageToUser и SendMessageToAll представлены для примера.
Затем регистрируем хаб в Program:
// хаб доступен по адресу https://myservice.com/notificationHub
app.MapHub<NotificationHub>("/notificationHub");Чтобы отправить уведомление пользователям необходимо воспользоваться созданным классом
NotificationHub. В этом примере контроллер получает ссылку на хаб (с помощью DI) и внутри определенного запроса отправляет уведомление всем клиентам:
[ApiController]
[Route("api/[controller]")]
public class GameEventController : ControllerBase
{
private readonly IHubContext<NotificationHub> _hubContext;
public GameEventController(IHubContext<NotificationHub> hubContext)
{
_hubContext = hubContext;
}
[HttpPost]
public async Task<IActionResult> StartEvent([FromBody] MessageData data)
{
await _service.ChangeEventStatus(eventId, EventStatus.InProgress);
await _hubContext.Clients.All.SendAsync("ChatMessage", new NotificationData()
{
chatId = data.chatId,
message = data.Message,
});
return Ok();
}
}Первый параметр "ChatMessage" — название уведомления, второй — произвольные данные, которые отправляются вместе с этим ивентом.
Теперь рассмотрим как клиенты могут получить и обработать уведомление.
В качестве примера рассмотрим получение уведомлений клиентами на Unity.
Для этого необходимо написать небольшой JS плагин, а также класс, который использует плагин из Unity.
const library = {
// Class definition.
$signalRSdk: {
connection: null,
initializeSignalR: function(url, chatMessageCallbackPtr) {
var hubUrl = UTF8ToString(url);
var tmpConnection = new signalR.HubConnectionBuilder().withUrl(hubUrl);
connection = tmpConnection.build();
connection.on("ChatMessage", function (message) {
signalRSdk.jsonCallback(message, chatMessageCallbackPtr);
});
connection.start().catch(function (err) {
console.error(err.toString());
});
},
sendMessageToUser: function(userId, message) {
connection.invoke("SendMessageToUser", userId, message).catch(function (err) {
console.error(err.toString());
});
},
jsonCallback: function (result, callbackPtr) {
const entriesJson = JSON.stringify(result);
const stringBufferSize = lengthBytesUTF8(entriesJson) + 1;
const stringBufferPtr = _malloc(stringBufferSize);
stringToUTF8(entriesJson, stringBufferPtr, stringBufferSize);
dynCall('vi', callbackPtr, [stringBufferPtr]);
_free(stringBufferPtr);
},
},
// External C# calls.
InitializeSignalR: function(url, chatMessageCallbackPtr) {
signalRSdk.initializeSignalR(url, chatMessageCallbackPtr);
},
SendMessageToUser: function(userId, message) {
signalRSdk.sendMessageToUser(userId, message);
},
}
autoAddDeps(library, '$signalRSdk');
mergeInto(LibraryManager.library, library);public static class SignalRNotifications
{
private static event Action<NotificationData> s_onChatMessageReceived;
public static void Initialize(string url, Action<NotificationData> onChatMessageReceived = null)
{
s_onChatMessageReceived = onChatMessageReceived;
#if UNITY_WEBGL && !UNITY_EDITOR
InitializeSignalR(url, OnChatMessageReceived);
#else
Debug.LogError($"{nameof(SignalRNotifications)} is only supported on WebGL.");
#endif
}
[DllImport("__Internal")]
private static extern void InitializeSignalR(string url, Action<string> onChatMessageReceived);
[DllImport("__Internal")]
private static extern void SendMessageToUser(string userId, string message);
[MonoPInvokeCallback(typeof(Action<string>))]
private static void OnChatMessageReceived(string message)
{
NotificationData notification = JsonUtility.FromJson<NotificationData>(message);
s_onChatMessageReceived?.Invoke(notification);
}
}Если плагин используется в WebGL, то в WebGL шаблон нужно добавить такую строчку внутри тега
<head>:
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.18/signalr.min.js"></script>
В результате можно пользоваться классом
SignalRNotificationsи подписываться на события, которые могут отправлять другие клиенты.
Хабр — SignalR Core. «Hello Habr!»
Автор документа: Артём Ветик