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 async Task<string> 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 async Task<string> 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 async Task 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 async Task 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 nulldalší Param2 vlastnost . Zprávu poslanou starším klientem můžete zjistit tak, že zkontrolujete Param2null 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.

Další prostředky