本文提供關於如何建立基於 SignalR 的 API 的指導。
使用自定義物件參數來確保回溯相容性
將參數新增至 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 值,因此用戶端會先檢查是否存在,再存取它。