次の方法で共有


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

提供 : Andrew AndrewTon-Nurse

この記事では、 SignalR ベースの API を構築するためのガイダンスを提供します。

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

(クライアントまたはサーバー上の) 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 値を提供しないため、クライアントはアクセスする前に存在するかどうかを確認します。

その他のリソース