SignalR Aspekty návrhu rozhraní API
Andrew Stanton-Nurse
Tento článek obsahuje pokyny k vytváření rozhraní API založených na nich SignalR.
Použití vlastních parametrů objektu k zajištění zpětné kompatibility
Přidání parametrů do SignalR metody centra (na klientovi nebo na serveru) je zásadní změnou. To znamená, že při pokusu o vyvolání metody se při pokusu o vyvolání metody zobrazí starší klienti nebo servery bez odpovídajícího počtu parametrů. Přidání vlastností do vlastního parametru objektu ale není zásadní změnou. Dá se použít k návrhu kompatibilních rozhraní API, která jsou odolná vůči změnám na klientovi nebo serveru.
Představte si například rozhraní API na straně serveru, jako je následující:
public int GetTotalLength(string param1)
{
return param1.Length;
}
Klient JavaScriptu volá tuto metodu následujícím způsobem invoke
:
connection.invoke("GetTotalLength", "value1");
Pokud později do metody serveru přidáte druhý parametr, starší klienti tuto hodnotu parametru nezadají. Příklad:
public int GetTotalLength(string param1, string param2)
{
return param1.Length + param2.Length;
}
Když se starý klient pokusí vyvolat tuto metodu, zobrazí se chyba podobná této:
Microsoft.AspNetCore.SignalR.HubException: Failed to invoke 'GetTotalLength' due to an error on the server.
Na serveru se zobrazí zpráva protokolu podobná této:
System.IO.InvalidDataException: Invocation provides 1 argument(s) but target expects 2.
Starý klient odeslal pouze jeden parametr, ale novější rozhraní API serveru vyžadovalo dva parametry. Použití vlastních objektů jako parametrů poskytuje větší flexibilitu. Přepracujme původní rozhraní API tak, aby používalo vlastní objekt:
public class TotalLengthRequest
{
public string Param1 { get; set; }
}
public int GetTotalLength(TotalLengthRequest req)
{
return req.Param1.Length;
}
Nyní klient používá objekt k volání metody:
connection.invoke("GetTotalLength", { param1: "value1" });
Místo přidání parametru přidejte do objektu TotalLengthRequest
vlastnost:
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;
}
Když starý klient odešle jeden parametr, zůstane null
další Param2
vlastnost . Zprávu poslanou starším klientem můžete zjistit tak, že zkontrolujete Param2
null
a použijete výchozí hodnotu. Nový klient může odeslat oba parametry.
connection.invoke("GetTotalLength", { param1: "value1", param2: "value2" });
Stejná technika funguje u metod definovaných v klientovi. Vlastní objekt můžete odeslat ze strany serveru:
public async Task Broadcast(string message)
{
await Clients.All.SendAsync("ReceiveMessage", new
{
Message = message
});
}
Na straně klienta přistupujete k Message
vlastnosti místo použití parametru:
connection.on("ReceiveMessage", (req) => {
appendMessageToChatWindow(req.message);
});
Pokud se později rozhodnete přidat odesílatele zprávy do datové části, přidejte do objektu vlastnost:
public async Task Broadcast(string message)
{
await Clients.All.SendAsync("ReceiveMessage", new
{
Sender = Context.User.Identity.Name,
Message = message
});
}
Starší klienti nebudou Sender
očekávat hodnotu, takže ji budou ignorovat. Nový klient ho může přijmout aktualizací, aby si přečetl novou vlastnost:
connection.on("ReceiveMessage", (req) => {
let message = req.message;
if (req.sender) {
message = req.sender + ": " + message;
}
appendMessageToChatWindow(message);
});
V tomto případě je nový klient odolný také proti starému serveru, který hodnotu neposkytuje Sender
. Vzhledem k tomu, že starý server hodnotu nezadá Sender
, klient před přístupem zkontroluje, jestli existuje.