Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этой статье приводятся рекомендации по созданию API на основе SignalR.
Использование параметров пользовательского объекта для обеспечения обратной совместимости
Добавление параметров в метод концентратора SignalR (на клиенте или сервере) является критическим изменением. Это означает, что старые клиенты и серверы получат ошибки при попытке вызвать метод без соответствующего количества параметров. Однако добавление свойств в параметр пользовательского объекта не является критическим изменением. Это можно использовать для разработки совместимых API, устойчивых к изменениям на клиенте или сервере.
Например, рассмотрим серверный API, как показано ниже:
public int GetTotalLength(string param1)
{
return param1.Length;
}
Клиент JavaScript вызывает этот метод invoke следующим образом:
connection.invoke("GetTotalLength", "value1");
Если позже добавить второй параметр в метод сервера, старые клиенты не будут предоставлять это значение параметра. Рассмотрим пример.
public int GetTotalLength(string param1, string param2)
{
return param1.Length + param2.Length;
}
Когда старый клиент пытается вызвать этот метод, он получит ошибку следующим образом:
Microsoft.AspNetCore.SignalR.HubException: Failed to invoke 'GetTotalLength' due to an error on the server.
На сервере появится следующее сообщение журнала:
System.IO.InvalidDataException: Invocation provides 1 argument(s) but target expects 2.
Старый клиент отправил только один параметр, но более новый API сервера требует двух параметров. Использование пользовательских объектов в качестве параметров обеспечивает большую гибкость. Давайте перепроектируем исходный API для использования пользовательского объекта:
public class TotalLengthRequest
{
public string Param1 { get; set; }
}
public int GetTotalLength(TotalLengthRequest req)
{
return req.Param1.Length;
}
Теперь клиент использует объект для вызова метода:
connection.invoke("GetTotalLength", { param1: "value1" });
Вместо добавления параметра добавьте свойство в TotalLengthRequest объект:
public class TotalLengthRequest
{
public string Param1 { get; set; }
public string Param2 { get; set; }
}
public int GetTotalLength(TotalLengthRequest req)
{
var length = req.Param1.Length;
if (req.Param2 != null)
{
length += req.Param2.Length;
}
return length;
}
Когда старый клиент отправляет один параметр, дополнительное Param2 свойство останется null. Вы можете обнаружить сообщение, отправленное старым клиентом, проверив Param2 и null применив значение по умолчанию. Новый клиент может отправлять оба параметра.
connection.invoke("GetTotalLength", { param1: "value1", param2: "value2" });
Тот же метод работает для методов, определенных на клиенте. Вы можете отправить пользовательский объект на стороне сервера:
public async Task Broadcast(string message)
{
await Clients.All.SendAsync("ReceiveMessage", new
{
Message = message
});
}
На стороне клиента вы обращаетесь к свойству Message , а не используете параметр:
connection.on("ReceiveMessage", (req) => {
appendMessageToChatWindow(req.message);
});
Если позже вы решите добавить отправителя сообщения в полезную нагрузку, добавьте свойство в объект:
public async Task Broadcast(string message)
{
await Clients.All.SendAsync("ReceiveMessage", new
{
Sender = Context.User.Identity.Name,
Message = message
});
}
Старые клиенты не ожидают значения Sender и проигнорируют его. Новый клиент может принять это, обновив настройку для использования нового свойства.
connection.on("ReceiveMessage", (req) => {
let message = req.message;
if (req.sender) {
message = req.sender + ": " + message;
}
appendMessageToChatWindow(message);
});
В этом случае новый клиент также допускает работу со старым сервером, который не предоставляет значение Sender. Поскольку старый сервер не предоставит значение Sender, клиент проверяет его наличие, прежде чем получить к нему доступ.
Дополнительные ресурсы
ASP.NET Core