Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Artikel ini menjelaskan cara menangani permintaan JSON Patch di API web ASP.NET Core.
Dukungan Patch JSON di API web ASP.NET Core didasarkan pada System.Text.Json serialisasi, dan memerlukan Microsoft.AspNetCore.JsonPatch.SystemTextJson
paket NuGet.
Apa itu standar JSON Patch?
Standar JSON Patch:
Adalah format standar untuk menjelaskan perubahan yang akan diterapkan ke dokumen JSON.
Didefinisikan dalam RFC 6902 dan banyak digunakan dalam API RESTful untuk melakukan pembaruan parsial pada sumber daya JSON.
Menjelaskan urutan operasi yang mengubah dokumen JSON seperti:
add
remove
replace
move
copy
test
Di aplikasi web, JSON Patch umumnya digunakan dalam operasi PATCH untuk melakukan pembaruan parsial sumber daya. Daripada mengirim seluruh sumber daya untuk pembaruan, klien dapat mengirim dokumen Patch JSON yang hanya berisi perubahan. Patching mengurangi ukuran payload dan meningkatkan efisiensi.
Untuk gambaran umum standar JSON Patch, lihat jsonpatch.com.
Dukungan JSON Patch di API web ASP.NET Core
Dukungan JSON Patch di API web ASP.NET Core didasarkan pada System.Text.Json serialisasi, dimulai dengan .NET 10, menerapkan Microsoft.AspNetCore.JsonPatch berdasarkan System.Text.Json serialisasi. Fitur ini:
- Memerlukan
Microsoft.AspNetCore.JsonPatch.SystemTextJson
paket NuGet. - Selaras dengan praktik .NET modern dengan memanfaatkan pustaka System.Text.Json, yang telah dioptimalkan untuk .NET.
- Memberikan peningkatan performa dan pengurangan penggunaan memori dibandingkan dengan implementasi berbasis warisan
Newtonsoft.Json
. Untuk informasi selengkapnya tentang implementasi berbasis warisanNewtonsoft.Json
, lihat versi .NET 9 dari artikel ini.
Nota
Implementasi Microsoft.AspNetCore.JsonPatch berdasarkan serialisasi System.Text.Json bukanlah pengganti langsung untuk implementasi berbasis sistem lama Newtonsoft.Json
. Ini tidak mendukung jenis dinamis, misalnya ExpandoObject.
Penting
Standar Patch JSON memiliki risiko keamanan yang melekat. Karena risiko ini melekat pada standar Patch JSON, implementasi ASP.NET Core tidak mencoba mengurangi risiko keamanan yang melekat. Ini adalah tanggung jawab pengembang untuk memastikan bahwa dokumen Patch JSON aman untuk diterapkan ke objek target. Untuk informasi selengkapnya, lihat bagian Mitigasi Risiko Keamanan .
Mengaktifkan dukungan JSON Patch dengan System.Text.Json
Untuk mengaktifkan dukungan Patch JSON dengan System.Text.Json, instal Microsoft.AspNetCore.JsonPatch.SystemTextJson
paket NuGet.
dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson --prerelease
Paket ini menyediakan JsonPatchDocument<TModel> kelas untuk mewakili dokumen Patch JSON untuk objek jenis T
dan logika kustom untuk menserialisasikan dan mendeserialisasi dokumen JSON Patch menggunakan System.Text.Json. Metode kunci kelas JsonPatchDocument<TModel> adalah ApplyTo(Object), yang menerapkan operasi patch ke objek target jenis T
.
Kode metode tindakan menerapkan Patch JSON
Dalam pengontrol API, metode tindakan untuk JSON Patch:
- Diannotasikan dengan HttpPatchAttribute atribut .
- JsonPatchDocument<TModel>Menerima , biasanya dengan FromBodyAttribute.
- ApplyTo(Object) Panggilan pada dokumen patch untuk menerapkan perubahan.
Contoh metode Tindakan Pengontrol:
[HttpPatch("{id}", Name = "UpdateCustomer")]
public IActionResult Update(AppDb db, string id, [FromBody] JsonPatchDocument<Customer> patchDoc)
{
// Retrieve the customer by ID
var customer = db.Customers.FirstOrDefault(c => c.Id == id);
// Return 404 Not Found if customer doesn't exist
if (customer == null)
{
return NotFound();
}
patchDoc.ApplyTo(customer, jsonPatchError =>
{
var key = jsonPatchError.AffectedObject.GetType().Name;
ModelState.AddModelError(key, jsonPatchError.ErrorMessage);
}
);
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return new ObjectResult(customer);
}
Kode dari aplikasi sampel ini berfungsi dengan model-model berikut: Customer
dan Order
.
namespace App.Models;
public class Customer
{
public string Id { get; set; }
public string? Name { get; set; }
public string? Email { get; set; }
public string? PhoneNumber { get; set; }
public string? Address { get; set; }
public List<Order>? Orders { get; set; }
public Customer()
{
Id = Guid.NewGuid().ToString();
}
}
namespace App.Models;
public class Order
{
public string Id { get; set; }
public DateTime? OrderDate { get; set; }
public DateTime? ShipDate { get; set; }
public decimal TotalAmount { get; set; }
public Order()
{
Id = Guid.NewGuid().ToString();
}
}
Langkah-langkah kunci metode tindakan sampel:
-
Mengambil Data Pelanggan:
- Metode mengambil
Customer
objek dari databaseAppDb
menggunakan id yang disediakan. - Jika tidak ada
Customer
objek yang ditemukan, objek akan mengembalikan404 Not Found
respons.
- Metode mengambil
-
Terapkan Patch JSON:
- Metode ApplyTo(Object) menerapkan operasi JSON Patch dari patchDoc ke objek
Customer
yang diambil. - Jika kesalahan terjadi selama aplikasi patch, seperti operasi atau konflik yang tidak valid, kesalahan tersebut ditangkap oleh delegasi penanganan kesalahan. Delegasi ini menambahkan pesan kesalahan ke
ModelState
menggunakan nama jenis objek yang terpengaruh dan pesan kesalahan.
- Metode ApplyTo(Object) menerapkan operasi JSON Patch dari patchDoc ke objek
-
Validasi ModelState:
- Setelah menerapkan patch, metode memeriksa
ModelState
untuk kesalahan. - Jika
ModelState
tidak valid, seperti karena kesalahan patch, maka400 Bad Request
merespons dengan memberikan kesalahan validasi.
- Setelah menerapkan patch, metode memeriksa
-
Kembalikan Pelanggan yang Diperbarui
- Jika patch berhasil diterapkan dan
ModelState
valid, metode mengembalikan objek yang diperbaruiCustomer
dalam respons.
- Jika patch berhasil diterapkan dan
Contoh respons kesalahan:
Contoh berikut menunjukkan isi 400 Bad Request
respons untuk operasi Patch JSON saat jalur yang ditentukan tidak valid:
{
"Customer": [
"The target location specified by path segment 'foobar' was not found."
]
}
Menerapkan dokumen Patch JSON ke objek
Contoh berikut menunjukkan cara menggunakan ApplyTo(Object) metode untuk menerapkan dokumen Patch JSON ke objek.
Contoh: Menerapkan JsonPatchDocument<TModel> ke objek
Contoh berikut menunjukkan:
- Operasi
add
,replace
, danremove
. - Operasi pada properti berlapis.
- Menambahkan item baru ke array.
- Penggunaan Konverter Enum String JSON dalam dokumen patch JSON.
// Original object
var person = new Person {
FirstName = "John",
LastName = "Doe",
Email = "johndoe@gmail.com",
PhoneNumbers = [new() {Number = "123-456-7890", Type = PhoneNumberType.Mobile}],
Address = new Address
{
Street = "123 Main St",
City = "Anytown",
State = "TX"
}
};
// Raw JSON patch document
string jsonPatch = """
[
{ "op": "replace", "path": "/FirstName", "value": "Jane" },
{ "op": "remove", "path": "/Email"},
{ "op": "add", "path": "/Address/ZipCode", "value": "90210" },
{ "op": "add", "path": "/PhoneNumbers/-", "value": { "Number": "987-654-3210",
"Type": "Work" } }
]
""";
// Deserialize the JSON patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);
// Apply the JSON patch document
patchDoc!.ApplyTo(person);
// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));
Contoh sebelumnya menghasilkan output berikut dari objek yang diperbarui:
{
"firstName": "Jane",
"lastName": "Doe",
"address": {
"street": "123 Main St",
"city": "Anytown",
"state": "TX",
"zipCode": "90210"
},
"phoneNumbers": [
{
"number": "123-456-7890",
"type": "Mobile"
},
{
"number": "987-654-3210",
"type": "Work"
}
]
}
Metode ApplyTo(Object) ini umumnya mengikuti konvensi dan opsi System.Text.Json untuk memproses JsonPatchDocument<TModel>, termasuk perilaku yang dikontrol oleh opsi berikut:
- JsonNumberHandling: Apakah properti numerik ditafsirkan dari string.
- PropertyNameCaseInsensitive: Apakah nama properti peka huruf besar/kecil.
Perbedaan utama antara System.Text.Json dan implementasi baru JsonPatchDocument<TModel> :
- Jenis runtime objek target, bukan jenis yang dideklarasikan, menentukan patch properti ApplyTo(Object) mana.
- System.Text.Json deserialisasi bergantung pada jenis yang dinyatakan untuk mengidentifikasi properti yang memenuhi syarat.
Contoh: Menerapkan JsonPatchDocument dengan penanganan kesalahan
Ada berbagai kesalahan yang dapat terjadi saat menerapkan dokumen Patch JSON. Misalnya, objek target mungkin tidak memiliki properti yang ditentukan, atau nilai yang ditentukan mungkin tidak kompatibel dengan jenis properti.
JSON Patch
mendukung test
operasi, yang memeriksa apakah nilai tertentu sama dengan properti target. Jika tidak, itu akan mengembalikan kesalahan.
Contoh berikut menunjukkan cara menangani kesalahan ini dengan anggun.
Penting
Objek yang diteruskan ke metode ApplyTo(Object) dimodifikasi langsung di tempatnya. Pemanggil bertanggung jawab dalam mengabaikan perubahan jika ada operasi yang gagal.
// Original object
var person = new Person {
FirstName = "John",
LastName = "Doe",
Email = "johndoe@gmail.com"
};
// Raw JSON patch document
string jsonPatch = """
[
{ "op": "replace", "path": "/Email", "value": "janedoe@gmail.com"},
{ "op": "test", "path": "/FirstName", "value": "Jane" },
{ "op": "replace", "path": "/LastName", "value": "Smith" }
]
""";
// Deserialize the JSON patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);
// Apply the JSON patch document, catching any errors
Dictionary<string, string[]>? errors = null;
patchDoc!.ApplyTo(person, jsonPatchError =>
{
errors ??= new ();
var key = jsonPatchError.AffectedObject.GetType().Name;
if (!errors.ContainsKey(key))
{
errors.Add(key, new string[] { });
}
errors[key] = errors[key].Append(jsonPatchError.ErrorMessage).ToArray();
});
if (errors != null)
{
// Print the errors
foreach (var error in errors)
{
Console.WriteLine($"Error in {error.Key}: {string.Join(", ", error.Value)}");
}
}
// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));
Contoh sebelumnya menghasilkan output berikut:
Error in Person: The current value 'John' at path 'FirstName' is not equal
to the test value 'Jane'.
{
"firstName": "John",
"lastName": "Smith", <<< Modified!
"email": "janedoe@gmail.com", <<< Modified!
"phoneNumbers": []
}
Mengurangi risiko keamanan
Saat menggunakan Microsoft.AspNetCore.JsonPatch.SystemTextJson
paket, sangat penting untuk memahami dan mengurangi potensi risiko keamanan. Bagian berikut menguraikan risiko keamanan yang diidentifikasi yang terkait dengan Patch JSON dan memberikan mitigasi yang direkomendasikan untuk memastikan penggunaan paket yang aman.
Penting
Ini bukan daftar ancaman yang lengkap. Pengembang aplikasi harus melakukan tinjauan model ancaman mereka sendiri untuk menentukan daftar komprehensif khusus aplikasi dan membuat mitigasi yang sesuai kebutuhan. Misalnya, aplikasi yang mengekspos koleksi ke operasi patch harus mempertimbangkan potensi serangan kompleksitas algoritma jika operasi tersebut menyisipkan atau menghapus elemen di awal koleksi.
Untuk meminimalkan risiko keamanan saat mengintegrasikan fungsionalitas JSON Patch ke dalam aplikasi mereka, pengembang harus:
- Jalankan model ancaman komprehensif untuk aplikasi mereka sendiri.
- Mengatasi ancaman yang diidentifikasi.
- Ikuti mitigasi yang direkomendasikan di bagian berikut.
Penolakan Layanan (DoS) melalui amplifikasi memori
-
Skenario: Klien berbahaya mengirimkan
copy
operasi yang menduplikasi grafik objek besar beberapa kali, yang mengarah ke konsumsi memori yang berlebihan. - Dampak: Potensi kondisi Out-Of-Memory (OOM), menyebabkan gangguan layanan.
-
Mitigasi:
- Validasi dokumen JSON Patch masuk untuk ukuran dan struktur sebelum memanggil ApplyTo(Object).
- Validasi harus spesifik untuk aplikasi, tetapi contoh validasi dapat terlihat mirip dengan yang berikut ini:
public void Validate(JsonPatchDocument<T> patch)
{
// This is just an example. It's up to the developer to make sure that
// this case is handled properly, based on the app needs.
if (patch.Operations.Where(op=>op.OperationType == OperationType.Copy).Count()
> MaxCopyOperationsCount)
{
throw new InvalidOperationException();
}
}
Pengacauan Logika Bisnis
- Skenario: Operasi patch dapat memanipulasi bidang dengan invarian implisit (misalnya, bendera internal, ID, atau bidang komputasi), melanggar batasan bisnis.
- Dampak: Masalah integritas data dan perilaku aplikasi yang tidak diinginkan.
-
Mitigasi:
- Gunakan POCO (Objek CLR Lama Biasa) dengan properti yang ditentukan secara eksplisit yang aman untuk dimodifikasi.
- Hindari mengekspos properti sensitif atau kritis keamanan di objek target.
- Jika objek POCO tidak digunakan, validasi objek yang di-patch setelah menerapkan operasi untuk memastikan aturan bisnis dan invarian tidak dilanggar.
- Gunakan POCO (Objek CLR Lama Biasa) dengan properti yang ditentukan secara eksplisit yang aman untuk dimodifikasi.
Autentikasi dan otorisasi
- Skenario: Klien yang tidak diautentikasi atau tidak sah mengirim permintaan Patch JSON berbahaya.
- Dampak: Akses tidak sah untuk memodifikasi data sensitif atau mengganggu perilaku aplikasi.
-
Mitigasi:
- Lindungi titik akhir yang menerima permintaan JSON Patch dengan mekanisme autentikasi dan otorisasi yang tepat.
- Batasi akses ke klien atau pengguna tepercaya dengan izin yang sesuai.
Mendapatkan kode
Melihat atau mengunduh kode sampel. (Cara mengunduh).
Untuk menguji sampel, jalankan aplikasi dan kirim permintaan HTTP dengan pengaturan berikut:
- URL:
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
- Metode HTTP:
PATCH
- Judul:
Content-Type: application/json-patch+json
- Isi: Salin dan tempel salah satu sampel dokumen patch JSON dari folder proyek JSON .
Sumber Daya Tambahan:
Artikel ini menjelaskan cara menangani permintaan JSON Patch di API web ASP.NET Core.
Penting
Standar Patch JSON memiliki risiko keamanan yang melekat. Implementasi ini tidak mencoba mengurangi risiko keamanan yang melekat ini. Ini adalah tanggung jawab pengembang untuk memastikan bahwa dokumen Patch JSON aman untuk diterapkan ke objek target. Untuk informasi selengkapnya, lihat bagian Mitigasi Risiko Keamanan .
Penginstalan paket
Dukungan JSON Patch di API web ASP.NET Core didasarkan pada Newtonsoft.Json
dan memerlukan Microsoft.AspNetCore.Mvc.NewtonsoftJson
paket NuGet.
Untuk mengaktifkan dukungan JSON Patch:
Microsoft.AspNetCore.Mvc.NewtonsoftJson
Instal paket NuGet.Panggil AddNewtonsoftJson. Contohnya:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllers() .AddNewtonsoftJson(); var app = builder.Build(); app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
AddNewtonsoftJson
menggantikan pemformat input dan output berbasis default System.Text.Json
yang digunakan untuk memformat semua konten JSON. Metode ekstensi ini kompatibel dengan metode pendaftaran layanan MVC berikut:
JsonPatch memerlukan pengaturan Content-Type
header ke application/json-patch+json
.
Menambahkan dukungan untuk JSON Patch saat menggunakan System.Text.Json
Formatter System.Text.Json
input berbasis tidak mendukung JSON Patch. Untuk menambahkan dukungan untuk JSON Patch menggunakan Newtonsoft.Json
, sambil membiarkan pemformat input dan output lainnya tidak berubah:
Microsoft.AspNetCore.Mvc.NewtonsoftJson
Instal paket NuGet.Perbarui
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(); } }
Kode sebelumnya membuat instans NewtonsoftJsonPatchInputFormatter dan menyisipkannya sebagai entri pertama dalam MvcOptions.InputFormatters koleksi. Urutan pendaftaran ini memastikan bahwa:
-
NewtonsoftJsonPatchInputFormatter
memproses permintaan Patch JSON. - Input dan formatter berbasis yang ada
System.Text.Json
memproses semua permintaan dan respons JSON lainnya.
Newtonsoft.Json.JsonConvert.SerializeObject
Gunakan metode untuk membuat JsonPatchDocumentserialisasi .
Metode permintaan HTTP PATCH
Metode PUT dan PATCH digunakan untuk memperbarui sumber daya yang ada. Perbedaannya adalah PUT menggantikan seluruh sumber daya, sementara PATCH hanya menentukan perubahan.
JSON Patch
JSON Patch adalah format untuk menentukan pembaruan yang akan diterapkan ke sumber daya. Dokumen JSON Patch memiliki array operasi. Setiap operasi mengidentifikasi jenis perubahan tertentu. Contoh perubahan tersebut termasuk menambahkan elemen array atau mengganti nilai properti.
Misalnya, dokumen JSON berikut mewakili sumber daya, dokumen Patch JSON untuk sumber daya, dan hasil penerapan operasi Patch.
Contoh sumber daya
{
"customerName": "John",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
}
]
}
Contoh patch JSON
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
Dalam JSON sebelumnya:
- Properti
op
menunjukkan jenis operasi. - Properti
path
menunjukkan elemen yang akan diperbarui. - Properti
value
menyediakan nilai baru.
Sumber daya setelah patch
Berikut adalah sumber daya setelah menerapkan dokumen Patch JSON sebelumnya:
{
"customerName": "Barry",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
},
{
"orderName": "Order2",
"orderType": null
}
]
}
Perubahan yang dilakukan dengan menerapkan dokumen JSON Patch ke sumber daya adalah atomik. Jika ada operasi dalam daftar yang gagal, tidak ada operasi dalam daftar yang diterapkan.
Sintaksis jalur
Properti jalur objek operasi memiliki garis miring antar level. Contohnya,"/address/zipCode"
.
Indeks berbasis nol digunakan untuk menentukan elemen array. Elemen pertama dari addresses
array akan berada di /addresses/0
. Untuk add
ke akhir array, gunakan tanda hubung (-
) daripada nomor indeks: /addresses/-
.
Operasional
Tabel berikut ini memperlihatkan operasi yang didukung seperti yang didefinisikan dalam spesifikasi Patch JSON:
Operasi | Catatan |
---|---|
add |
Tambahkan elemen properti atau array. Untuk properti yang ada: tetapkan nilai. |
remove |
Menghapus elemen properti atau array. |
replace |
Sama seperti remove diikuti oleh add di lokasi yang sama. |
move |
Sama seperti remove dari sumber diikuti oleh add ke tujuan menggunakan nilai dari sumber. |
copy |
Sama seperti add tujuan menggunakan nilai dari sumber. |
test |
Mengembalikan kode status keberhasilan jika nilai di path = disediakan value . |
Patch JSON di ASP.NET Core
Implementasi ASP.NET Core dari JSON Patch disediakan dalam paket NuGet Microsoft.AspNetCore.JsonPatch .
Kode metode tindakan
Dalam pengontrol API, metode tindakan untuk JSON Patch:
- Diannotasikan dengan
HttpPatch
atribut . -
JsonPatchDocument<TModel>Menerima , biasanya dengan
[FromBody]
. - ApplyTo(Object) Panggilan pada dokumen patch untuk menerapkan perubahan.
Berikut contohnya:
[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);
}
}
Kode dari aplikasi sampel ini berfungsi dengan model berikut 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; }
}
Metode tindakan sampel:
- Membangun sebuah
Customer
. - Menerapkan patch.
- Mengembalikan hasil dalam isi respons.
Dalam aplikasi nyata, kode akan mengambil data dari penyimpanan seperti database dan memperbarui database setelah menerapkan patch.
Status model
Contoh metode tindakan sebelumnya memanggil kelebihan beban ApplyTo
yang mengambil status model sebagai salah satu parameternya. Dengan opsi ini, Anda bisa mendapatkan pesan kesalahan sebagai respons. Contoh berikut menunjukkan isi respons Permintaan Buruk 400 untuk test
operasi:
{
"Customer": [
"The current value 'John' at path 'customerName' != test value 'Nancy'."
]
}
Objek dinamis
Contoh metode tindakan berikut menunjukkan cara menerapkan patch ke objek dinamis:
[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
dynamic obj = new ExpandoObject();
patch.ApplyTo(obj);
return Ok(obj);
}
Operasi tambahkan
- Jika
path
menunjuk ke elemen array: menyisipkan elemen baru sebelum elemen yang ditentukan olehpath
. - Jika
path
menunjuk ke properti: mengatur nilai properti. - Jika
path
menunjuk ke lokasi yang tidak ada:- Jika sumber daya yang akan di-patch adalah objek dinamis: menambahkan properti.
- Jika sumber daya untuk di-patch adalah objek statis: permintaan gagal.
Contoh dokumen patch berikut mengatur nilai CustomerName
dan menambahkan Order
objek ke akhir Orders
array.
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
Operasi hapus
- Jika
path
menunjuk ke elemen array: menghapus elemen . - Jika
path
menunjuk ke properti:- Jika sumber daya untuk patch adalah objek dinamis: menghapus properti .
- Jika sumber daya untuk di-patch adalah objek statis:
- Jika properti nullable: mengaturnya ke null.
- Jika properti tidak dapat diubah ke null, atur ke
default<T>
.
Contoh kumpulan CustomerName
dokumen patch berikut ke null dan hapus Orders[0]
:
[
{
"op": "remove",
"path": "/customerName"
},
{
"op": "remove",
"path": "/orders/0"
}
]
Operasi ganti
Operasi ini secara fungsional sama remove
dengan diikuti oleh add
.
Contoh dokumen patch berikut menetapkan nilai CustomerName
dan mengganti Orders[0]
dengan objek baru Order
:
[
{
"op": "replace",
"path": "/customerName",
"value": "Barry"
},
{
"op": "replace",
"path": "/orders/0",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
Operasi pemindahan
- Jika
path
menunjuk ke elemen array: menyalinfrom
elemen ke lokasipath
elemen, maka menjalankanremove
operasi padafrom
elemen . - Jika
path
menunjuk ke properti: menyalin nilaifrom
properti kepath
properti, maka menjalankanremove
operasi padafrom
properti . - Jika
path
menunjuk ke properti yang tidak ada:- Jika sumber daya untuk di-patch adalah objek statis: permintaan gagal.
- Jika sumber daya untuk patch adalah objek dinamis: menyalin
from
properti ke lokasi yangpath
ditunjukkan olehremove
, maka menjalankan operasi padafrom
properti .
Contoh dokumen patch berikut:
- Menyalin nilai
Orders[0].OrderName
keCustomerName
. -
Orders[0].OrderName
Mengatur ke null. -
Orders[1]
Pindah ke sebelumOrders[0]
.
[
{
"op": "move",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "move",
"from": "/orders/1",
"path": "/orders/0"
}
]
Operasi salin
Operasi ini secara fungsional sama move
dengan operasi tanpa langkah terakhir remove
.
Contoh dokumen patch berikut:
- Menyalin nilai
Orders[0].OrderName
keCustomerName
. - Menyisipkan salinan
Orders[1]
sebelumOrders[0]
.
[
{
"op": "copy",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "copy",
"from": "/orders/1",
"path": "/orders/0"
}
]
Operasi pengujian
Jika nilai di lokasi yang ditunjukkan oleh path
berbeda dari nilai yang disediakan dalam value
, permintaan gagal. Dalam hal ini, seluruh permintaan PATCH gagal bahkan jika semua operasi lain dalam dokumen patch akan berhasil.
Operasi test
ini umumnya digunakan untuk mencegah pembaruan ketika ada konflik konkurensi.
Contoh dokumen patch berikut tidak berpengaruh jika nilai CustomerName
awal adalah "John", karena pengujian gagal:
[
{
"op": "test",
"path": "/customerName",
"value": "Nancy"
},
{
"op": "add",
"path": "/customerName",
"value": "Barry"
}
]
Mendapatkan kode
Melihat atau mengunduh kode sampel. (Cara mengunduh).
Untuk menguji sampel, jalankan aplikasi dan kirim permintaan HTTP dengan pengaturan berikut:
- URL:
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
- Metode HTTP:
PATCH
- Judul:
Content-Type: application/json-patch+json
- Isi: Salin dan tempel salah satu sampel dokumen patch JSON dari folder proyek JSON .
Mengurangi risiko keamanan
Saat menggunakan paket Microsoft.AspNetCore.JsonPatch
dengan implementasi berbasis Newtonsoft.Json
, memahami dan mengurangi potensi risiko keamanan adalah sangat penting. Bagian berikut menguraikan risiko keamanan yang diidentifikasi yang terkait dengan Patch JSON dan memberikan mitigasi yang direkomendasikan untuk memastikan penggunaan paket yang aman.
Penting
Ini bukan daftar ancaman yang lengkap. Pengembang aplikasi harus melakukan tinjauan model ancaman mereka sendiri untuk menentukan daftar komprehensif khusus aplikasi dan membuat mitigasi yang sesuai kebutuhan. Misalnya, aplikasi yang mengekspos koleksi ke operasi patch harus mempertimbangkan potensi serangan kompleksitas algoritma jika operasi tersebut menyisipkan atau menghapus elemen di awal koleksi.
Dengan menjalankan model ancaman komprehensif untuk aplikasi mereka sendiri dan mengatasi ancaman yang diidentifikasi saat mengikuti mitigasi yang direkomendasikan di bawah ini, konsumen paket ini dapat mengintegrasikan fungsionalitas JSON Patch ke dalam aplikasi mereka sambil meminimalkan risiko keamanan.
Penolakan Layanan (DoS) melalui amplifikasi memori
-
Skenario: Klien berbahaya mengirimkan
copy
operasi yang menduplikasi grafik objek besar beberapa kali, yang mengarah ke konsumsi memori yang berlebihan. - Dampak: Potensi kondisi Out-Of-Memory (OOM), menyebabkan gangguan layanan.
-
Mitigasi:
- Validasi dokumen JSON Patch masuk untuk ukuran dan struktur sebelum memanggil
ApplyTo
. - Validasi harus spesifik untuk aplikasi, tetapi contoh validasi dapat terlihat mirip dengan yang berikut ini:
- Validasi dokumen JSON Patch masuk untuk ukuran dan struktur sebelum memanggil
public void Validate(JsonPatchDocument patch)
{
// This is just an example. It's up to the developer to make sure that
// this case is handled properly, based on the app needs.
if (patch.Operations.Where(op => op.OperationType == OperationType.Copy).Count()
> MaxCopyOperationsCount)
{
throw new InvalidOperationException();
}
}
Pengacauan Logika Bisnis
- Skenario: Operasi patch dapat memanipulasi bidang dengan invarian implisit (misalnya, bendera internal, ID, atau bidang komputasi), melanggar batasan bisnis.
- Dampak: Masalah integritas data dan perilaku aplikasi yang tidak diinginkan.
-
Mitigasi:
- Gunakan objek POCO dengan properti yang ditentukan secara eksplisit yang aman untuk dimodifikasi.
- Hindari mengekspos properti sensitif atau kritis keamanan di objek target.
- Jika tidak ada objek POCO yang digunakan, validasi objek yang di-patch setelah menerapkan operasi untuk memastikan aturan bisnis dan invarian tidak dilanggar.
Autentikasi dan otorisasi
- Skenario: Klien yang tidak diautentikasi atau tidak sah mengirim permintaan Patch JSON berbahaya.
- Dampak: Akses tidak sah untuk memodifikasi data sensitif atau mengganggu perilaku aplikasi.
-
Mitigasi:
- Lindungi titik akhir yang menerima permintaan JSON Patch dengan mekanisme autentikasi dan otorisasi yang tepat.
- Batasi akses ke klien atau pengguna tepercaya dengan izin yang sesuai.
Sumber Daya Tambahan:
Artikel ini menjelaskan cara menangani permintaan JSON Patch di API web ASP.NET Core.
Penting
Standar Patch JSON memiliki risiko keamanan yang melekat. Karena risiko ini melekat pada standar Patch JSON, implementasi ini tidak mencoba mengurangi risiko keamanan yang melekat. Ini adalah tanggung jawab pengembang untuk memastikan bahwa dokumen Patch JSON aman untuk diterapkan ke objek target. Untuk informasi selengkapnya, lihat bagian Mitigasi Risiko Keamanan .
Penginstalan paket
Untuk mengaktifkan dukungan JSON Patch di aplikasi Anda, selesaikan langkah-langkah berikut:
Microsoft.AspNetCore.Mvc.NewtonsoftJson
Instal paket NuGet.Perbarui metode proyek
Startup.ConfigureServices
untuk memanggil AddNewtonsoftJson. Contohnya:services .AddControllersWithViews() .AddNewtonsoftJson();
AddNewtonsoftJson
kompatibel dengan metode pendaftaran layanan MVC:
JSON Patch, AddNewtonsoftJson, dan System.Text.Json
AddNewtonsoftJson
menggantikan System.Text.Json
pemformat input dan output berbasis yang digunakan untuk memformat semua konten JSON. Untuk menambahkan dukungan untuk JSON Patch menggunakan Newtonsoft.Json
, sambil membiarkan formatter lain tidak berubah, perbarui metode proyek Startup.ConfigureServices
sebagai berikut:
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();
}
Kode sebelumnya memerlukan Microsoft.AspNetCore.Mvc.NewtonsoftJson
paket dan pernyataan berikut 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
Gunakan metode untuk membuat serial JsonPatchDocument.
Metode permintaan HTTP PATCH
Metode PUT dan PATCH digunakan untuk memperbarui sumber daya yang ada. Perbedaannya adalah PUT menggantikan seluruh sumber daya, sementara PATCH hanya menentukan perubahan.
JSON Patch
JSON Patch adalah format untuk menentukan pembaruan yang akan diterapkan ke sumber daya. Dokumen JSON Patch memiliki array operasi. Setiap operasi mengidentifikasi jenis perubahan tertentu. Contoh perubahan tersebut termasuk menambahkan elemen array atau mengganti nilai properti.
Misalnya, dokumen JSON berikut mewakili sumber daya, dokumen Patch JSON untuk sumber daya, dan hasil penerapan operasi Patch.
Contoh sumber daya
{
"customerName": "John",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
}
]
}
Contoh patch JSON
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
Dalam JSON sebelumnya:
- Properti
op
menunjukkan jenis operasi. - Properti
path
menunjukkan elemen yang akan diperbarui. - Properti
value
menyediakan nilai baru.
Sumber daya setelah patch
Berikut adalah sumber daya setelah menerapkan dokumen Patch JSON sebelumnya:
{
"customerName": "Barry",
"orders": [
{
"orderName": "Order0",
"orderType": null
},
{
"orderName": "Order1",
"orderType": null
},
{
"orderName": "Order2",
"orderType": null
}
]
}
Perubahan yang dilakukan dengan menerapkan dokumen JSON Patch ke sumber daya adalah atomik. Jika ada operasi dalam daftar yang gagal, tidak ada operasi dalam daftar yang diterapkan.
Sintaksis jalur
Properti jalur objek operasi memiliki garis miring antar level. Contohnya,"/address/zipCode"
.
Indeks berbasis nol digunakan untuk menentukan elemen array. Elemen pertama dari addresses
array akan berada di /addresses/0
. Untuk add
ke akhir array, gunakan tanda hubung (-
) daripada nomor indeks: /addresses/-
.
Operasional
Tabel berikut ini memperlihatkan operasi yang didukung seperti yang didefinisikan dalam spesifikasi Patch JSON:
Operasi | Catatan |
---|---|
add |
Tambahkan elemen properti atau array. Untuk properti yang ada: tetapkan nilai. |
remove |
Menghapus elemen properti atau array. |
replace |
Sama seperti remove diikuti oleh add di lokasi yang sama. |
move |
Sama seperti remove dari sumber diikuti oleh add ke tujuan menggunakan nilai dari sumber. |
copy |
Sama seperti add tujuan menggunakan nilai dari sumber. |
test |
Mengembalikan kode status keberhasilan jika nilai di path = disediakan value . |
Patch JSON di ASP.NET Core
Implementasi ASP.NET Core dari JSON Patch disediakan dalam paket NuGet Microsoft.AspNetCore.JsonPatch .
Kode metode tindakan
Dalam pengontrol API, metode tindakan untuk JSON Patch:
- Diannotasikan dengan
HttpPatch
atribut . -
JsonPatchDocument<T>
Menerima , biasanya dengan[FromBody]
. -
ApplyTo
Panggilan pada dokumen patch untuk menerapkan perubahan.
Berikut contohnya:
[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);
}
}
Kode dari aplikasi sampel ini berfungsi dengan model berikut 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; }
}
}
Metode tindakan sampel:
- Membangun sebuah
Customer
. - Menerapkan patch.
- Mengembalikan hasil dalam isi respons.
Dalam aplikasi nyata, kode akan mengambil data dari penyimpanan seperti database dan memperbarui database setelah menerapkan patch.
Status model
Contoh metode tindakan sebelumnya memanggil kelebihan beban ApplyTo
yang mengambil status model sebagai salah satu parameternya. Dengan opsi ini, Anda bisa mendapatkan pesan kesalahan sebagai respons. Contoh berikut menunjukkan isi respons Permintaan Buruk 400 untuk test
operasi:
{
"Customer": [
"The current value 'John' at path 'customerName' is not equal to the test value 'Nancy'."
]
}
Objek dinamis
Contoh metode tindakan berikut menunjukkan cara menerapkan patch ke objek dinamis:
[HttpPatch]
public IActionResult JsonPatchForDynamic([FromBody]JsonPatchDocument patch)
{
dynamic obj = new ExpandoObject();
patch.ApplyTo(obj);
return Ok(obj);
}
Operasi tambahkan
- Jika
path
menunjuk ke elemen array: menyisipkan elemen baru sebelum elemen yang ditentukan olehpath
. - Jika
path
menunjuk ke properti: mengatur nilai properti. - Jika
path
menunjuk ke lokasi yang tidak ada:- Jika sumber daya yang akan di-patch adalah objek dinamis: menambahkan properti.
- Jika sumber daya untuk di-patch adalah objek statis: permintaan gagal.
Contoh dokumen patch berikut mengatur nilai CustomerName
dan menambahkan Order
objek ke akhir Orders
array.
[
{
"op": "add",
"path": "/customerName",
"value": "Barry"
},
{
"op": "add",
"path": "/orders/-",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
Operasi hapus
- Jika
path
menunjuk ke elemen array: menghapus elemen . - Jika
path
menunjuk ke properti:- Jika sumber daya untuk patch adalah objek dinamis: menghapus properti .
- Jika sumber daya untuk di-patch adalah objek statis:
- Jika properti nullable: mengaturnya ke null.
- Jika properti tidak dapat diubah ke null, atur ke
default<T>
.
Contoh kumpulan CustomerName
dokumen patch berikut ke null dan hapus Orders[0]
:
[
{
"op": "remove",
"path": "/customerName"
},
{
"op": "remove",
"path": "/orders/0"
}
]
Operasi ganti
Operasi ini secara fungsional sama remove
dengan diikuti oleh add
.
Contoh dokumen patch berikut menetapkan nilai CustomerName
dan mengganti Orders[0]
dengan objek baru Order
:
[
{
"op": "replace",
"path": "/customerName",
"value": "Barry"
},
{
"op": "replace",
"path": "/orders/0",
"value": {
"orderName": "Order2",
"orderType": null
}
}
]
Operasi pemindahan
- Jika
path
menunjuk ke elemen array: menyalinfrom
elemen ke lokasipath
elemen, maka menjalankanremove
operasi padafrom
elemen . - Jika
path
menunjuk ke properti: menyalin nilaifrom
properti kepath
properti, maka menjalankanremove
operasi padafrom
properti . - Jika
path
menunjuk ke properti yang tidak ada:- Jika sumber daya untuk di-patch adalah objek statis: permintaan gagal.
- Jika sumber daya untuk patch adalah objek dinamis: menyalin
from
properti ke lokasi yangpath
ditunjukkan olehremove
, maka menjalankan operasi padafrom
properti .
Contoh dokumen patch berikut:
- Menyalin nilai
Orders[0].OrderName
keCustomerName
. -
Orders[0].OrderName
Mengatur ke null. -
Orders[1]
Pindah ke sebelumOrders[0]
.
[
{
"op": "move",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "move",
"from": "/orders/1",
"path": "/orders/0"
}
]
Operasi salin
Operasi ini secara fungsional sama move
dengan operasi tanpa langkah terakhir remove
.
Contoh dokumen patch berikut:
- Menyalin nilai
Orders[0].OrderName
keCustomerName
. - Menyisipkan salinan
Orders[1]
sebelumOrders[0]
.
[
{
"op": "copy",
"from": "/orders/0/orderName",
"path": "/customerName"
},
{
"op": "copy",
"from": "/orders/1",
"path": "/orders/0"
}
]
Operasi pengujian
Jika nilai di lokasi yang ditunjukkan oleh path
berbeda dari nilai yang disediakan dalam value
, permintaan gagal. Dalam hal ini, seluruh permintaan PATCH gagal bahkan jika semua operasi lain dalam dokumen patch akan berhasil.
Operasi test
ini umumnya digunakan untuk mencegah pembaruan ketika ada konflik konkurensi.
Contoh dokumen patch berikut tidak berpengaruh jika nilai CustomerName
awal adalah "John", karena pengujian gagal:
[
{
"op": "test",
"path": "/customerName",
"value": "Nancy"
},
{
"op": "add",
"path": "/customerName",
"value": "Barry"
}
]
Mendapatkan kode
Melihat atau mengunduh kode sampel. (Cara mengunduh).
Untuk menguji sampel, jalankan aplikasi dan kirim permintaan HTTP dengan pengaturan berikut:
- URL:
http://localhost:{port}/jsonpatch/jsonpatchwithmodelstate
- Metode HTTP:
PATCH
- Judul:
Content-Type: application/json-patch+json
- Isi: Salin dan tempel salah satu sampel dokumen patch JSON dari folder proyek JSON .
ASP.NET Core