歡迎光臨
每天分享高質量文章

如何在ASP.NET Core中使用JSON Patch

原文: JSON Patch With ASP.NET Core

作者:.NET Core Tutorials

譯文:如何在ASP.NET Core中使用JSON Patch

地址:https://www.cnblogs.com/lwqlun/p/10433615.html

譯者:Lamond Lu

JSON Patch是一種使用API顯式更新文件的方法。它本身是一種契約,用於描述如何修改文件(例如:將欄位的值替換成另外一個值),而不必同時發送其他未更改的屬性值。

一個JSON Patch請求是什麼樣的?

你可以在以下鏈接(http://jsonpatch.com/)中找到JSON Patch的官方文件,但是這裡我們將進一步研究一下如何在ASP.NET Core中實現JSON Patch。

為了演示JSON Patch, 我創建了以下C#物件類, 後續我將使用JSON Patch請求來操作這個物件類的實體。

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public List<string> Friends { get; set; }
}

每個JSON Patch請求都是遵循一個相似的結構。它有一個固定的“操作”串列。每個操作本身擁有3個屬性:

  • “op” – 定義了你要執行何種操作,例如add, replace, test等。

  • “path” – 定義了你要操作物件屬性路徑。用前面的Person類為例,如果你希望修改FirstName屬性,那麼你使用的操作路徑應該是”/FirstName”。

  • “value” – 在大部分情況下,這個屬性表示你希望在操作中使用的值。

現在讓我們來看一下每一個的操作如何使用。

Add

Add操作通常意味著你要向物件中添加屬性,或者向陣列中添加專案。對於前者,在C#中是沒有用的,因為C#是強型別語言,所以不能將屬性添加到編譯時尚未定義的物件上。

所以這裡如果想往陣列中添加專案,PATCH請求的內容應該如下所示。

{ "op": "add", "path": "/Friends/1", "value": "Mike" }

這將在Friends陣列的索引1處插入一個”Mike”值。

或者你還可以使用”-“在陣列尾部插入記錄。

{ "op": "add", "path": "/Friends/-", "value": "Mike" }

Remove

與Add操作類似,刪除操作意味著你希望刪除物件中屬性,或者從資料中刪除某一項。但是因為在C#中你無法移除屬性,實際操作時,它會將屬性的值變更為default(T)。在某些情況下,如果屬性是可空的,則會設置屬性值為NULL。但是需要小心,因為當在值型別上使用時,例如int, 則該值實際上會重置為”0″。

如果要在物件上刪除某一屬性以達到重置的效果,你可以使用一下命令。

{ "op": "remove", "path": "/FirstName"}

當然你也可以使用刪除命令刪除陣列中的某一項。

{ "op": "remove", "path": "/Friends/1" }

這將刪除陣列索引為1的專案。但是有時候使用索引從陣列中刪除資料是非常危險的,因為這裡沒有一個”where”條件來控制刪除, 有可能在刪除的時候,資料庫中對應陣列已經發生變化了。實際上有一個JSON Patch操作可以幫助解決這個問題,後面我會描述它。

Replace

Replace操作和它的字面意思完全一樣,可以使用它來替換已有值。針對簡單屬性,你可以使用如下的命令。

{ "op": "replace", "path": "/FirstName", "value": "Jim" }

你同樣可以使用它來替換陣列中的物件。

{ "op": "replace", "path": "/Friends/1", "value": "Bob" }

你甚至可以用它來替換整個陣列。

{ "op": "replace", "path": "/Friends", "value": ["Bob", "Bill"] }

Copy

Copy操作可以將值從一個路徑複製到另一個路徑。這個值可以是屬性,物件,或者資料。在下麵的例子中,我們將FirstName屬性的值複製到了LastName屬性上。這個命令的使用場景不是很多。

{ "op": "copy", "from": "/FirstName", "path" : "/LastName" }

Move

Move操作非常類似於Copy操作,但是正如它的字面意思,”from”欄位的值將被移除。如果你看一下ASP.NET Core的JSON Patch的底層代碼,你會發現,它實際上它會在”from”路徑上執行Remove操作,在”path”路徑上執行Add操作。

{ "op": "move", "from": "/FirstName", "path" : "/LastName" }

Test

在當前的ASP.NET Core公開發行版中沒有Test操作,但是如果你在Github上查看原始碼,你會發現微軟已經處理了Test操作。Test操作是一種樂觀鎖定的方法,或者更簡單的說,它會檢測資料物件從服務器讀取之後,是否發生了更改。

我們以如下操作為例。

[
{ "op": "test", "path": "/FirstName", "value": "Bob" }
{ "op": "replace", "path": "/FirstName", "value": "Jim" }
]

這個操作首先會檢查”/FirstName”路徑的值是否”Bob”, 如果是,就將它改為”Jim”。 如果不是,則什麼事情都不會發生。這裡你需要註意,在一個Test操作的請求體內可以包含多個Test操作,但是如果其中任何一個Test操作驗證失敗,所以的變更操作都不會被執行。

為什麼要使用JSON PATCH

JSON Patch的一大優勢在於它的請求操作體很小,只發送物件的更改內容。 但是在ASP.NET Core中使用JSON Patch還有另一個很大的好處,就是C#是一種強型別語言,無法區分是要將模型的值設置為NULL,還是忽略該屬性, 而使用JSON Patch可以解決這個問題。

這裡如果沒有好的例子,很難解釋。 所以想象一下我從API請求一個“Person”物件。 在C#中,模型可能如下所示:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

當從API傳回Json物件時,它看起來可能像這樣。

{
"firstName" : "James", 
"lastName" : "Smith"
}

現在在前端,如果不使用JSON Patch, 如果我只想更新FirstName, 我可能在請求中附帶一下請求體。

{
"firstName" : "Jim"
}

現在當我在C#中反序列化這個模型時,問題就出現了。不要看下麵的代碼,想一下此時我們的模型中的屬性值是什麼?

public class Person
{
    public string FirstName { get; set; } //Jim
    public string LastName { get; set; } //
}

因為我們發送LastName屬性的值,所以它被反序列化為Null。 但這很簡單,我們可以忽略NULL的值,只更新我們實際傳遞的欄位。 但這不一定是正確的,如果該欄位實際上可以為空呢? 如果我們發送了以下請求體怎麼辦?

{
"firstName" : "Jim", 
"lastName" : null
}

所以現在我們實際上已經指定我們想要取消該欄位。但是因為C#是強型別的,所以我們無法在服務器端進行模型系結的時候,我們無法確定它是否要將該欄位的值設置為NULL。

這似乎是一個奇怪的場景,因為前端可以始終發送完整的資料模型,永遠不會省略欄位。並且在大多數情況下,前端Web庫的模型將始終與API的模型匹配。但有一種情況並非如此,那就是移動應用程式。通常向蘋果應用商店提交手機應用,可能需要數周時間才能獲得批准。在這個時候,你可能還需要在Web或Android應用程式中使用新模型。在不同平臺之間實現同步非常困難,而且通常是不可能。雖然API版本確實對解決這個問題有很長的路要走,但我仍然認為JSON Patch在解決這個問題方面具有很大的實用性。

最後,讓我們使用JSON Patch!我們可以使用以下JSON Patch請求更新Person物件

[
    {
      "op": "replace",
      "path": "/FirstName",
      "value": "Jim"
    }
]

這明確表示我們想要更改名字而不是其他內容。 它準確的告訴我們到底將要發生什麼。

在ASP.NET Core專案中啟用JSON Patch

在Visual Studio中,我們可以在Package Manage Console中安裝官方的Json Patch庫(預設創建的ASP.NET Core專案中沒有該庫)。

Install-Package Microsoft.AspNetCore.JsonPatch

為了演示,我將添加如下的一個控制器類。這裡需要註意我們使用的HTTP verb是HttpPatch, 請求引數的型別是JsonPatchDocument。 為了更新物件,我們只需要簡單呼叫ApplyTo方法,並傳入了需要更新的物件。

在以上示例中,我們只是使用了存放在控制器中的簡單物件並對其進行了更新,但是在正式的API中,我們需要從資料庫中拉取資料物件,更新物件,並重新儲存到資料庫。

當我們使用如下請求體發送JSON Patch請求時:

[
{"op" : "replace", "path" : "/FirstName", "value" : "Bob"}
]

我們可以得到如下響應內容:

{
    "firstName": "Bob",
    "lastName": "Smith"
}

真棒! 我們的名字改為Bob! 使用JSON Patch啟動和運行真的很簡單。

使用Automapper處理JSON Patch請求

針對JSON Patch的使用,最大的問題是,你經常需要從API傳回View Model或者DTO, 並生成PATCH請求。但是如果將這些修改請求應用於資料庫物件上?大部分情況下,開發人員都掙扎在與此。這裡我們可以使用Automapper來幫助完成這個轉換的工作。

例如如下代碼:

 

    赞(0)

    分享創造快樂