JSON Toolkit

JSON Patch dalam Pengeluaran: Sifat Atomik, Idempotensi, dan Konflik Serentak

Inti pati: JSON Patch hanyalah format perubahan; sifat atomik, idempotensi dan keserentakan masih menjadi tanggungjawab anda. Merangkumi prasyarat test, pelepasan laluan, rollback dan penyelarasan ETag.

Lanjutan~10 min bacaanDiterbitkan 2026-10-31Dikemas kini 2026-10-31
Kulit untuk JSON Patch dalam Pengeluaran

Intinya: sahkan dahulu laluan sasaran dan kebenaran patch, kemudian tambah penegasan keserentakan dengan test atau If-Match; gunakan patch pada salinan supaya kegagalan tidak meninggalkan apa-apa yang separuh tertulis.

Bahaya sebenar permintaan PATCH bukanlah menulis medan yang salah. Ia adalah mempercayai bahawa tulisan itu berjaya sambil secara senyap menimpa perubahan orang lain. JSON Patch memberikan anda alatnya, tetapi reka bentuk keselamatan masih tanggungjawab anda.

1. Ulasan Ringkas Set Operasi RFC 6902

JSON Patch menerangkan perubahan sebagai tatasusunan operasi, dengan jenis media application/json-patch+json. Enam operasi dilaksanakan mengikut urutan tatasusunan:

Operasi Kesan Kekangan utama
add Tambah anggota atau sisip elemen tatasusunan Indeks tatasusunan boleh sama dengan panjang; - bermaksud penghujung
remove Buang anggota atau elemen Laluan sasaran mesti wujud
replace Ganti nilai sedia ada Laluan sasaran mesti wujud
move Pindahkan nod Tidak boleh memindahkan nod ke dalam laluan anaknya sendiri
copy Salin nod Laluan sumber mesti wujud
test Tegaskan kesamaan nilai Jika gagal, keseluruhan patch tidak digunakan

Ramai orang hanya mengingati tiga yang pertama dan lupa bahawa test itulah yang menukar tulisan buta menjadi tulisan bersyarat. Semantik operasi penuh dan sempadan tatasusunan dibincangkan dalam JSON Pointer, Patch, dan Merge Patch.

2. Menggunakan test untuk Menjadikan Patch Kemas Kini Bersyarat

Patch penguncian optimistik biasa tidak bermula dengan tulisan, tetapi dengan penegasan versi:

{"op":"test","path":"/version","value":1}

Letakkannya dalam patch lengkap:

[
  { "op": "test", "path": "/version", "value": 1 },
  { "op": "replace", "path": "/version", "value": 2 },
  { "op": "replace", "path": "/status", "value": "paid" }
]

Logiknya: sahkan bahawa versi dokumen pada masa ini ialah 1, kemudian tukar kepada 2. Jika permintaan lain telah pun menaikkan versi kepada 2, test pertama gagal, keseluruhan patch tidak digunakan, dan pelayan memulangkan konflik. Ini menghalang kemas kini yang hilang tanpa memperkenalkan kunci teragih.

Beberapa perkara reka bentuk:

  • Letakkan test sebelum setiap tulisan. Jika tulisan terdahulu sudah berkesan, penegasan itu tidak bermakna;
  • Tegaskan nombor versi atau ringkasan yang dijana oleh pelayan, bukan medan biasa yang boleh diubah bebas oleh klien;
  • Naikkan versi pada setiap tulisan yang berjaya; jika tidak dua permintaan yang menggunakan versi lama yang sama akan kedua-duanya lulus;
  • value bagi test mesti mempunyai jenis JSON yang sama tepat dengan nilai dokumen. 1 dan "1" tidak sama.

3. Pelepasan Laluan dan Pencemaran Prototaip

Peraturan pelepasan JSON Pointer mudah: tulis ~ sebagai ~0 dan / sebagai ~1, dan gantikan ~ sebelum /, jika tidak anda akan mendapat pelepasan berganda.

Key "a/b"    → /a~1b
Key "m~n"    → /m~0n
Key "a~/b"   → /a~0~1b

Risiko dengan patch luaran biasanya bukan pelepasan tetapi kunci berbahaya. Dalam JavaScript, kunci seperti __proto__, constructor, dan prototype boleh mencetuskan pencemaran prototaip, walaupun melalui add biasa:

[
  { "op": "add", "path": "/__proto__/polluted", "value": true }
]

Gunakan senarai kebenaran bukan senarai larangan:

  1. Benarkan patch mengubah hanya medan perniagaan, seperti /status atau /items/0/quantity;
  2. Sahkan setiap path dan from terhadap ungkapan biasa atau awalan laluan, tolak tahap yang tidak dijangka selain indeks tatasusunan;
  3. Huraikan JSON ke dalam bekas Object.create(null) atau Map untuk mengurangkan kesan rantaian prototaip yang tercemar;
  4. Hadkan bilangan operasi dan saiz bait setiap value.

4. Penggunaan Atomik dan Rollback

RFC 6902 memerlukan pelaksanaan atomik, tetapi sifat atomik ialah keperluan semantik, bukan lalai dalam setiap pustaka. Pustaka seperti fast-json-patch mengubah dokumen di tempat apabila mutateDocument benar, dan kegagalan mungkin meninggalkan perubahan separa.

import { applyPatch, validate } from 'fast-json-patch';

/**
 * Apply a patch immutably and return the original document on failure.
 * @param document The original document, which callers must not mutate
 * @param patch The JSON Patch operation array to apply
 * @returns The result, including a success flag and the new document
 */
function applySafely(document: unknown, patch: unknown[]) {
  const errors = validate(patch, document);

  if (errors) {
    return { ok: false, document };
  }

  try {
    const result = applyPatch(document, patch, false, false);
    return { ok: true, document: result.newDocument };
  } catch {
    return { ok: false, document };
  }
}

Tiga cadangan pelaksanaan:

  • Salin secara mendalam atau structure-clone dahulu, gunakan pada salinan, dan tukar rujukan hanya selepas berjaya;
  • Jangan bergantung pada rollback automatik pustaka; tingkah laku pada pengecualian berbeza antara pelaksanaan;
  • Sahkan sebelum melaksanakan: validate hanya menyemak struktur, sedangkan konflik semantik masih memerlukan test; kedua-duanya tidak saling menggantikan.

Jika patch merangkumi berbilang sumber, seperti mengubah pesanan dan inventori serentak, sifat atomik JSON Patch hanya terpakai dalam satu dokumen. Merentas dokumen anda memerlukan transaksi pangkalan data atau tindakan pampasan.

5. Idempotensi: PATCH Tidak Sememangnya Idempoten

Semantik HTTP mentakrifkan PUT dan DELETE sebagai idempoten, tetapi PATCH tidak semestinya begitu. Mengulang JSON Patch yang sama boleh menghasilkan keputusan yang sangat berbeza:

  • add /items/- menambahkan ke penghujung tatasusunan setiap kali, jadi cubaan semula mencipta pendua;
  • replace /version menukar versi 1 kepada 2 dan berjaya semula pada cubaan semula, walaupun operasi itu sudah pun digunakan;
  • remove /temp gagal pada larian kedua kerana laluan itu tidak lagi wujud.

Terdapat tiga cara biasa untuk mencapai idempotensi:

Pendekatan Cara ia berfungsi Paling sesuai untuk
Prasyarat versi Operasi pertama ialah test /version, kemudian naikkan Sumber dengan versi eksplisit
Kunci idempotensi Klien menghantar Idempotency-Key; pelayan menyimpan hasil dalam cache Pembayaran, pesanan, dan tulisan lain
Patch keadaan sasaran Gunakan replace bukan add, supaya hasil bergantung hanya pada nilai akhir Medan konfigurasi dan status

Corak terburuk ialah patch yang mencampurkan add dan remove sambil menganggap cubaan semula selamat. Jika patch menambah atau memadam apa-apa, ia mesti digandingkan dengan penegasan versi atau kunci idempotensi.

6. Konflik Serentak: ETag dan If-Match

Selain test di dalam patch, HTTP menawarkan mekanisme standard: pelayan memulangkan ETag, dan klien menghantar If-Match pada permintaan seterusnya.

GET /orders/42 HTTP/1.1
Host: api.example.com

HTTP/1.1 200 OK
ETag: "a3f9c1"
Content-Type: application/json
PATCH /orders/42 HTTP/1.1
Host: api.example.com
If-Match: "a3f9c1"
Content-Type: application/json-patch+json

[{"op":"replace","path":"/status","value":"paid"}]

Jika sumber telah berubah, pelayan memulangkan 412 Precondition Failed, dan klien mengambil semula sebelum mencuba lagi. If-Match: * bermaksud "laksanakan selagi sumber wujud", yang tidak sesuai apabila anda perlu menghalang penimpaan.

Perbezaan antara test dan If-Match:

  • If-Match disemak pada lapisan HTTP sebelum penghuraian badan, jadi ia lebih murah;
  • test disemak pada lapisan semantik patch dan boleh menegaskan mana-mana medan tanpa bergantung pada sokongan ETag pelayan;
  • Kedua-duanya boleh digabungkan, tetapi jangan laksanakan hanya salah satu dan mendakwa endpoint itu selamat di bawah keserentakan.

7. Perbezaan Pustaka dan Pemilihan

Pelaksanaan Bahasa Sifat atomik Sokongan test Nota
fast-json-patch JavaScript Pilihan (memulangkan dokumen baharu secara lalai) Ya Ekosistem luas; perhatikan mutateDocument
jsonpatch Python Rollback pada pengecualian Ya Hampir menepati standard, dengan ralat yang jelas
json-patch Go Beroperasi pada salinan Ya Selalu digunakan bersama JSON Merge Patch
zjsonpatch Java Ya Ya Integrasi Jackson yang mudah

Jangan pilih pustaka semata-mata berdasarkan "menyokong keenam-enam operasi". Sahkan tiga perkara: sama ada kegagalan melakukan rollback, sama ada test membandingkan secara ketat, dan sama ada add tatasusunan di luar julat menimbulkan ralat. Pelaksanaan yang longgar menjadikan patch yang sama berkelakuan berbeza merentas persekitaran.

8. Senarai Semak Keselamatan Sebelum Pelancaran

  • [ ] Endpoint mengisytiharkan application/json-patch+json;
  • [ ] Patch digunakan pada salinan, dan kegagalan tidak pernah mencemarkan dokumen asal;
  • [ ] Setiap path dan from dalam patch luaran lulus semakan senarai kebenaran;
  • [ ] Kunci berbahaya seperti __proto__, constructor, dan prototype ditolak;
  • [ ] Bilangan operasi dan saiz nilai tunggal dihadkan;
  • [ ] test atau If-Match menyediakan kawalan keserentakan;
  • [ ] Patch ditulis ke log audit dengan pelaku, cap masa, dan tatasusunan operasi penuh;
  • [ ] Sempadan transaksi adalah jelas apabila patch merangkumi berbilang dokumen.

Patch secara semula jadi ialah penerangan lengkap tentang "apa yang berubah", yang menjadikannya rekod audit semula jadi. Untuk kod ralat dan idempotensi pada lapisan API, teruskan dengan Reka Bentuk API JSON. Jika patch datang daripada penggabungan dua cabang, anda juga memerlukan semantik konflik, yang dibincangkan dalam Penggabungan Git untuk Fail JSON.

FAQ

Kod status apa yang perlu dipulangkan oleh operasi test yang gagal?

Jika konflik berpunca daripada penegasan versi, kedua-dua 409 Conflict dan 412 Precondition Failed boleh dipertahankan. Utamakan 412 untuk kekal konsisten dengan If-Match, dan sertakan versi semasa dalam badan respons supaya klien boleh mencuba semula.

Bolehkah patch yang mengandungi add dan remove dicuba semula dengan selamat?

Tidak secara langsung. add ke penghujung tatasusunan menduplikasi data, dan remove gagal pada larian kedua kerana laluan itu sudah tiada. Tambah prasyarat versi atau kunci idempotensi supaya cubaan semula dikenali sebagai operasi yang sama.

Bolehkah JSON Patch menjamin transaksi merentas dokumen?

Tidak. Sifat atomik RFC 6902 terpakai pada satu dokumen JSON. Apabila pesanan, inventori, atau sumber lain terlibat, gunakan transaksi pangkalan data, pampasan Saga, atau baris gilir mesej untuk konsistensi.

Mengapa patch tidak boleh menulis ke __proto__?

Kerana kebanyakan pelaksanaan JavaScript menyimpan dokumen dalam objek biasa, menulis __proto__ boleh mengubah rantaian prototaip dan mempengaruhi setiap objek selepasnya. Senarai kebenaran laluan ialah pertahanan paling boleh dipercayai; jangan bergantung hanya pada penapisan dalaman pustaka.

Bagaimana saya menjadikan permintaan PATCH idempoten?

Terdapat tiga pendekatan biasa: mulakan patch dengan test /version dan naikkan versi apabila berjaya, biarkan pelayan menyimpan hasil untuk Idempotency-Key, atau gunakan replace bukan add supaya hasil bergantung hanya pada nilai akhir. Jika patch menambah atau memadam apa-apa, ia mesti digandingkan dengan penegasan versi atau kunci idempotensi; ia tidak boleh dicuba semula begitu sahaja.

Patutkah saya menggunakan kedua-dua test dan If-Match?

Anda tidak perlu menggunakan kedua-duanya, tetapi melaksanakan hanya salah satu tidak menjadikan endpoint selamat di bawah keserentakan. If-Match disemak pada lapisan HTTP sebelum penghuraian badan, jadi ia lebih murah; test disemak pada lapisan semantik patch dan boleh menegaskan mana-mana medan tanpa bergantung pada sokongan ETag pelayan.