ASP.NET Core Web API 中的 JsonPatch
本文說明如何在 ASP.NET Core Web API 中處理 JS ON Patch 要求。
套件安裝
JSASP.NET Core Web API 中的 ON Patch 支援是以 且需要 Microsoft.AspNetCore.Mvc.NewtonsoftJson
NuGet 套件為基礎 Newtonsoft.Json
。 若要啟用 JS ON Patch 支援:
安裝
Microsoft.AspNetCore.Mvc.NewtonsoftJson
NuGet 套件。呼叫 AddNewtonsoftJson。 例如:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers() .AddNewtonsoftJson(); var app = builder.Build(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
AddNewtonsoftJson
會取代用於格式化所有JS ON 內容的預設 System.Text.Json
型輸入和輸出格式器。 此擴充方法與下列 MVC 服務註冊方法相容:
使用 System.Text.Json 時新增 JS ON Patch 的支援
System.Text.Json
型輸入格式器不支援 JS ON Patch。 若要使用 Newtonsoft.Json
新增 ON Patch 的支援 JS ,同時讓其他輸入和輸出格式器保持不變:
安裝
Microsoft.AspNetCore.Mvc.NewtonsoftJson
NuGet 套件。更新
Program.cs
:using JsonPatchSample; using Microsoft.AspNetCore.Mvc.Formatters; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers(options => { options.InputFormatters.Insert(0, MyJPIF.GetJsonPatchInputFormatter()); }); var app = builder.Build(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Extensions.Options; namespace JsonPatchSample; public static class MyJPIF { public static NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter() { var builder = new ServiceCollection() .AddLogging() .AddMvc() .AddNewtonsoftJson() .Services.BuildServiceProvider(); return builder .GetRequiredService<IOptions<MvcOptions>>() .Value .InputFormatters .OfType<NewtonsoftJsonPatchInputFormatter>() .First(); } }
上述程式碼會建立 的 NewtonsoftJsonPatchInputFormatter 實例,並將它插入為集合中的 MvcOptions.InputFormatters 第一個專案。 此註冊順序可確保:
NewtonsoftJsonPatchInputFormatter
處理 JS ON Patch 要求。- 現有的
System.Text.Json
型輸入和格式器會處理所有其他 JS ON 要求和回應。
Newtonsoft.Json.JsonConvert.SerializeObject
使用 方法來序列化 JsonPatchDocument 。
PATCH HTTP 要求方法
PUT 和 PATCH \(英文\) 方法均用來更新現有的資源。 它們之間的差異是 PUT 會取代整個資源,而 PATCH 只會指定變更。
JSON Patch
JS ON Patch是指定要套用至資源之更新的格式。 JSON Patch 檔具有作業陣列。 每個作業都會識別特定類型的變更。 這類變更的範例包括新增陣列專案或取代屬性值。
例如,下列 JS ON 檔代表資源、 JS 資源的 ON Patch 檔,以及套用 Patch 作業的結果。
資源範例
{
"customerName": "John",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
}
]
}
JSON 修補程式範例
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
在上述 JS ON 中:
op
屬性會指出作業的類型。path
屬性會指出要更新的元素。value
屬性會提供新值。
修補之後的資源
以下是套用上述 JS ON Patch 檔之後的資源:
{
"customerName": "Barry",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
},
{
"orderName": "Order2",
"orderType": null
}
]
}
將 ON Patch 檔套用 JS 至資源所做的變更是不可部分完成的。 如果清單中有任何作業失敗,則不會套用清單中的作業。
路徑語法
作業物件的 path \(英文\) 屬性在層級之間有斜線。 例如: "/address/zipCode"
。
以零為起始的索引可用來指定陣列元素。 addresses
陣列的第一個元素會在 /addresses/0
上。 若要 add
到陣列結尾,請使用連字號 (-
) ,而不是索引編號: /addresses/-
。
Operations
下表顯示ON Patch 規格中所 JS定義的支援作業:
作業 | 備註 |
---|---|
add |
加入屬性或陣列元素。 針對現有的屬性:設定值。 |
remove |
移除屬性或陣列元素。 |
replace |
與 remove 之後接著在同一個位置上 add 相同。 |
move |
與從來源 remove 之後接著使用來源的值 add 到目的地相同。 |
copy |
與使用來源的值 add 到目的地相同。 |
test |
如果 path 上的值 = 所提供的 value ,即會傳回成功狀態碼。 |
JSASP.NET Core 中的 ON Patch
MICROSOFT.AspNetCore.JsonPatch NuGet 套件提供 ON Patch 的 ASP.NET Core實 JS 作。
動作方法程式碼
在 API 控制器中,ON Patch 的 JS 動作方法:
- 使用
HttpPatch
屬性來標註。 - JsonPatchDocument<TModel>接受 ,通常使用
[FromBody]
。 - 呼叫修補文件上的 ApplyTo(Object) 以套用變更。
以下為範例:
[HttpPatch]
public IActionResult JsonPatchWithModelState(
[FromBody] JsonPatchDocument<Customer> patchDoc)
{
if (patchDoc != null)
{
var customer = CreateCustomer();
patchDoc.ApplyTo(customer, ModelState);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return new ObjectResult(customer);
}
else
{
return BadRequest(ModelState);
}
}
來自範例應用程式的此程式碼適用于下列 Customer
模型:
namespace JsonPatchSample.Models;
public class Customer
{
public string? CustomerName { get; set; }
public List<Order>? Orders { get; set; }
}
namespace JsonPatchSample.Models;
public class Order
{
public string OrderName { get; set; }
public string OrderType { get; set; }
}
範例動作方法:
- 建構
Customer
。 - 套用修補檔案。
- 在回應本文中傳回結果。
在實際的應用程式中,程式碼會從資料庫之類的存放區擷取資料,並在套用修補檔案之後更新資料庫。
模型狀態
上述動作方法範例會呼叫 ApplyTo
的多載,以取得模型狀態作為它的其中一個參數。 使用此選項,您就能在回應中收到錯誤訊息。 下列範例會針對 test
作業顯示「400 不正確的要求」回應的本文:
{
"Customer": [
"The current value 'John' at path 'customerName' != test value 'Nancy'."
]
}
動態物件
下列動作方法範例示範如何將修補程式套用至動態物件:
[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
dynamic obj = new ExpandoObject();
patch.ApplyTo(obj);
return Ok(obj);
}
新增作業
- 如果
path
指向陣列元素:將新元素插入至path
所指定的元素之前。 - 如果
path
指向屬性:設定屬性值。 - 如果
path
指向不存在的位置:- 如果要修補的資源是動態物件:加入屬性。
- 如果要修補的資源是靜態物件:要求失敗。
下列範例修補文件會設定 CustomerName
的值,並將 Order
物件加入至 Orders
陣列的結尾處。
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
移除作業
- 如果
path
指向陣列元素:移除該元素。 - 如果
path
指向屬性:- 如果要修補的資源是動態物件:移除屬性。
- 如果要修補的資源是靜態物件:
- 如果屬性可為 Null:將它設定為 Null。
- 如果屬性不可為 Null,則將它設定為
default<T>
。
下列範例修補檔集 CustomerName
為 null 並刪除 Orders[0]
:
[
{
"op": "remove",
"path": "/customerName"
},
{
"op": "remove",
"path": "/orders/0"
}
]
取代作業
此作業在功能上與 remove
之後接著 add
相同。
下列修補程式檔範例會設定 的值 CustomerName
,並將 取代 Orders[0]
為新的 Order
物件:
[
{
"op": "replace",
"path": "/customerName",
"value": "Barry"
},
{
"op": "replace",
"path": "/orders/0",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
移動作業
- 如果
path
指向陣列元素:將from
元素複製到path
元素的位置,然後在from
元素上執行remove
作業。 - 如果
path
指向屬性:將from
屬性的值複製到path
屬性,然後在from
屬性上執行remove
作業。 - 如果
path
指向不存在的屬性:- 如果要修補的資源是靜態物件:要求失敗。
- 如果要修補的資源是動態物件:將
from
屬性複製到path
所指出的位置,然後在from
屬性上執行remove
作業。
下列範例修補文件:
- 將
Orders[0].OrderName
的值複製到CustomerName
。 - 將
Orders[0].OrderName
設定為 Null。 - 將
Orders[1]
移到Orders[0]
前面。
[
{
"op": "move",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "move",
"from": "/orders/1",
"path": "/orders/0"
}
]
複製作業
此作業在功能上與不含最後 remove
步驟的 move
作業相同。
下列範例修補文件:
- 將
Orders[0].OrderName
的值複製到CustomerName
。 - 在
Orders[0]
前面插入Orders[1]
的複本。
[
{
"op": "copy",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "copy",
"from": "/orders/1",
"path": "/orders/0"
}
]
測試作業
如果 path
所指出位置上的值與 value
中所提供的值不同,則要求會失敗。 在該情況下,整個 PATCH 要求會失敗,即使修補文件中的所有其他作業都成功也一樣。
test
作業通常會用來防止在發生並行衝突時進行更新。
如果 CustomerName
的初始值是 "John",則下列範例修補文件不會有任何作用,因為測試失敗:
[
{
"op": "test",
"path": "/customerName",
"value": "Nancy"
},
{
"op": "add",
"path": "/customerName",
"value": "Barry"
}
]
取得程式碼
檢視或下載範例程式碼。 (如何下載)。
若要測試範例,請執行應用程式,並使用下列設定來傳送 HTTP 要求:
- URL:
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
- HTTP 方法:
PATCH
- 標題:
Content-Type: application/json-patch+json
- 本文:從 ON 專案資料夾複製並貼上 JSJS其中一個 ON 修補程式檔範例。
其他資源
- IETF RFC 5789 PATCH 方法規格 \(英文\)
- IETF RFC 6902 JS ON Patch 規格
- IETF RFC 6901 JS ON 修補程式路徑格式規格
- JS ON Patch 檔。 包含建立 JS ON Patch 檔之資源的連結。
- ASP.NET Core JS ON Patch 原始程式碼
本文說明如何在 ASP.NET Core Web API 中處理 JS ON Patch 要求。
套件安裝
若要在應用程式中啟用 JS ON Patch 支援,請完成下列步驟:
安裝
Microsoft.AspNetCore.Mvc.NewtonsoftJson
NuGet 套件。更新專案的
Startup.ConfigureServices
方法來呼叫 AddNewtonsoftJson 。 例如:services .AddControllersWithViews() .AddNewtonsoftJson();
AddNewtonsoftJson
與 MVC 服務註冊方法相容:
JSON Patch、AddNewtonsoftJson 和 System.Text.Json
AddNewtonsoftJson
System.Text.Json
會取代用於格式化所有JS ON 內容的型輸入和輸出格式器。 若要使用 Newtonsoft.Json
新增 ON Patch 的支援 JS ,同時讓其他格式器保持不變,請更新專案的 Startup.ConfigureServices
方法,如下所示:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.InputFormatters.Insert(0, GetJsonPatchInputFormatter());
});
}
private static NewtonsoftJsonPatchInputFormatter GetJsonPatchInputFormatter()
{
var builder = new ServiceCollection()
.AddLogging()
.AddMvc()
.AddNewtonsoftJson()
.Services.BuildServiceProvider();
return builder
.GetRequiredService<IOptions<MvcOptions>>()
.Value
.InputFormatters
.OfType<NewtonsoftJsonPatchInputFormatter>()
.First();
}
上述程式碼需要 Microsoft.AspNetCore.Mvc.NewtonsoftJson
套件和下列 using
語句:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using System.Linq;
Newtonsoft.Json.JsonConvert.SerializeObject
使用 方法來序列化 JsonPatchDocument。
PATCH HTTP 要求方法
PUT 和 PATCH \(英文\) 方法均用來更新現有的資源。 它們之間的差異是 PUT 會取代整個資源,而 PATCH 只會指定變更。
JSON Patch
JS ON Patch是指定要套用至資源之更新的格式。 JSON Patch 檔具有作業陣列。 每個作業都會識別特定類型的變更。 這類變更的範例包括新增陣列元素或取代屬性值。
例如,下列 JS ON 檔代表資源、 JS 資源的 ON Patch 檔,以及套用 Patch 作業的結果。
資源範例
{
"customerName": "John",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
}
]
}
JSON 修補程式範例
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
在上述 JS ON 中:
op
屬性會指出作業的類型。path
屬性會指出要更新的元素。value
屬性會提供新值。
修補之後的資源
以下是套用上述 JS ON Patch 檔之後的資源:
{
"customerName": "Barry",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
},
{
"orderName": "Order2",
"orderType": null
}
]
}
將 ON Patch 檔套用 JS 至資源所做的變更是不可部分完成的。 如果清單中有任何作業失敗,則不會套用清單中的作業。
路徑語法
作業物件的 path \(英文\) 屬性在層級之間有斜線。 例如: "/address/zipCode"
。
以零為起始的索引可用來指定陣列元素。 addresses
陣列的第一個元素會在 /addresses/0
上。 若要 add
到陣列結尾,請使用連字號 (-
) ,而不是索引編號: /addresses/-
。
Operations
下表顯示ON Patch 規格中所 JS定義的支援作業:
作業 | 備註 |
---|---|
add |
加入屬性或陣列元素。 針對現有的屬性:設定值。 |
remove |
移除屬性或陣列元素。 |
replace |
與 remove 之後接著在同一個位置上 add 相同。 |
move |
與從來源 remove 之後接著使用來源的值 add 到目的地相同。 |
copy |
與使用來源的值 add 到目的地相同。 |
test |
如果 path 上的值 = 所提供的 value ,即會傳回成功狀態碼。 |
JSASP.NET Core 中的 ON 修補程式
ON Patch 的 ASP.NET Core實作 JS 是在Microsoft.AspNetCore.JsonPatch NuGet 套件中提供。
動作方法程式碼
在 API 控制器中,ON Patch 的 JS 動作方法:
- 使用
HttpPatch
屬性來標註。 JsonPatchDocument<T>
接受 ,通常是使用[FromBody]
。- 呼叫修補文件上的
ApplyTo
以套用變更。
以下為範例:
[HttpPatch]
public IActionResult JsonPatchWithModelState(
[FromBody] JsonPatchDocument<Customer> patchDoc)
{
if (patchDoc != null)
{
var customer = CreateCustomer();
patchDoc.ApplyTo(customer, ModelState);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return new ObjectResult(customer);
}
else
{
return BadRequest(ModelState);
}
}
來自範例應用程式的此程式碼可搭配下列 Customer
模型使用:
using System.Collections.Generic;
namespace JsonPatchSample.Models
{
public class Customer
{
public string CustomerName { get; set; }
public List<Order> Orders { get; set; }
}
}
namespace JsonPatchSample.Models
{
public class Order
{
public string OrderName { get; set; }
public string OrderType { get; set; }
}
}
範例動作方法:
- 建構
Customer
。 - 套用修補檔案。
- 在回應本文中傳回結果。
在實際的應用程式中,程式碼會從資料庫之類的存放區擷取資料,並在套用修補檔案之後更新資料庫。
模型狀態
上述動作方法範例會呼叫 ApplyTo
的多載,以取得模型狀態作為它的其中一個參數。 使用此選項,您就能在回應中收到錯誤訊息。 下列範例會針對 test
作業顯示「400 不正確的要求」回應的本文:
{
"Customer": [
"The current value 'John' at path 'customerName' is not equal to the test value 'Nancy'."
]
}
動態物件
下列動作方法範例示範如何將修補程式套用至動態物件:
[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
dynamic obj = new ExpandoObject();
patch.ApplyTo(obj);
return Ok(obj);
}
新增作業
- 如果
path
指向陣列元素:將新元素插入至path
所指定的元素之前。 - 如果
path
指向屬性:設定屬性值。 - 如果
path
指向不存在的位置:- 如果要修補的資源是動態物件:加入屬性。
- 如果要修補的資源是靜態物件:要求失敗。
下列範例修補文件會設定 CustomerName
的值,並將 Order
物件加入至 Orders
陣列的結尾處。
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
移除作業
- 如果
path
指向陣列元素:移除該元素。 - 如果
path
指向屬性:- 如果要修補的資源是動態物件:移除屬性。
- 如果要修補的資源是靜態物件:
- 如果屬性可為 Null:將它設定為 Null。
- 如果屬性不可為 Null,則將它設定為
default<T>
。
下列範例修補檔集 CustomerName
為 null 並刪除 Orders[0]
:
[
{
"op": "remove",
"path": "/customerName"
},
{
"op": "remove",
"path": "/orders/0"
}
]
取代作業
此作業在功能上與 remove
之後接著 add
相同。
下列修補程式檔範例會設定 的值 CustomerName
,並將 取代 Orders[0]
為新的 Order
物件:
[
{
"op": "replace",
"path": "/customerName",
"value": "Barry"
},
{
"op": "replace",
"path": "/orders/0",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
移動作業
- 如果
path
指向陣列元素:將from
元素複製到path
元素的位置,然後在from
元素上執行remove
作業。 - 如果
path
指向屬性:將from
屬性的值複製到path
屬性,然後在from
屬性上執行remove
作業。 - 如果
path
指向不存在的屬性:- 如果要修補的資源是靜態物件:要求失敗。
- 如果要修補的資源是動態物件:將
from
屬性複製到path
所指出的位置,然後在from
屬性上執行remove
作業。
下列範例修補文件:
- 將
Orders[0].OrderName
的值複製到CustomerName
。 - 將
Orders[0].OrderName
設定為 Null。 - 將
Orders[1]
移到Orders[0]
前面。
[
{
"op": "move",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "move",
"from": "/orders/1",
"path": "/orders/0"
}
]
複製作業
此作業在功能上與不含最後 remove
步驟的 move
作業相同。
下列範例修補文件:
- 將
Orders[0].OrderName
的值複製到CustomerName
。 - 在
Orders[0]
前面插入Orders[1]
的複本。
[
{
"op": "copy",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "copy",
"from": "/orders/1",
"path": "/orders/0"
}
]
測試作業
如果 path
所指出位置上的值與 value
中所提供的值不同,則要求會失敗。 在該情況下,整個 PATCH 要求會失敗,即使修補文件中的所有其他作業都成功也一樣。
test
作業通常會用來防止在發生並行衝突時進行更新。
如果 CustomerName
的初始值是 "John",則下列範例修補文件不會有任何作用,因為測試失敗:
[
{
"op": "test",
"path": "/customerName",
"value": "Nancy"
},
{
"op": "add",
"path": "/customerName",
"value": "Barry"
}
]
取得程式碼
檢視或下載範例程式碼。 (如何下載)。
若要測試範例,請執行應用程式,並使用下列設定來傳送 HTTP 要求:
- URL:
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
- HTTP 方法:
PATCH
- 標題:
Content-Type: application/json-patch+json
- 本文:從JS ON專案資料夾複製並貼上其中一個 JS ON 修補程式檔範例。
其他資源
- IETF RFC 5789 PATCH 方法規格 \(英文\)
- IETF RFC 6902 JS ON Patch 規格
- IETF RFC 6901 JS ON 修補程式路徑格式規格
- JS ON Patch 檔。 包含用來建立 JS ON Patch 檔的資源連結。
- JSASP.NET Core ON Patch 原始程式碼