SignalR API の設計に関する考慮事項

提供 : Andrew AndrewTon-Nurse

この記事では、ベースの API を SignalR 構築する方法について説明します。

カスタム オブジェクト パラメーターを使用して下位互換性を確保する

(クライアントまたはサーバー上の) ハブ メソッドにパラメーターを SignalR 追加すると、大 きな変更になります。 つまり、古いクライアント/サーバーは、適切な数のパラメーターを指定せずにメソッドを呼び出しようとするときにエラーを受け取ります。 ただし、カスタム オブジェクト パラメーターにプロパティを追加する 方法は 、大きな変更ではありません。 これは、クライアントまたはサーバー上の変更に対する回復性がある互換性のある API を設計するために使用できます。

たとえば、次のようなサーバー側 API を考え考え、

public int GetTotalLength(string param1)
{
    return param1.Length;
}

JavaScript クライアントは、次のように を使用してこのメソッド invoke を呼び出します。

connection.invoke("GetTotalLength", "value1");

後で 2 番目のパラメーターをサーバー メソッドに追加した場合、古いクライアントは、このパラメーター値を提供しません。 次に例を示します。

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.

古いクライアントは 1 つのパラメーターのみを送信しましたが、新しいサーバー API には 2 つのパラメーターが必要でした。 カスタム オブジェクトをパラメーターとして使用すると、柔軟性が向上します。 カスタム オブジェクトを使用するために元の 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;
}

古いクライアントが 1 つのパラメーターを送信すると、追加の Param2 プロパティは 残ります null 。 をチェックして既定値を適用することで、古いクライアントから送信されたメッセージ Param2null を検出できます。 新しいクライアントは両方のパラメーターを送信できます。

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 するかどうかを確認します。

その他のリソース