PUT vs PATCH vs JSON-PATCH

Otázka, která je položena s rostoucí pravidelností v API, která jste nenáviděli, je skupina, která byla položena roky, ale ne vždy má dobrou odpověď. Otázkou je:

Jaký je rozdíl mezi PUT a PATCH a kdy je mám použít? A WTF je JSON-PATCH?

Pro začátek jsou PUT a PATCH dvě různé metody HTTP, které se běžně používají v REST API. U lidí, kteří považují rozhraní REST API za pouze CRUD (Vytvořit, Číst, Aktualizovat, Odstranit), může dojít ke zmatku při pokusu o nalezení toho, který je „nejlepší“. Lidé mají preference, lidé tvrdí, a ve skutečnosti je konverzace zřídka vedena rozumným způsobem.

Ve svém API můžete naprosto mít PUT i PATCH, a ne, neměly by být aliasem navzájem (při pohledu na vás Rails). Jednoduše řečeno, dělají různé věci. RFC pro PATCH (RFC 5789) vlastně vysvětluje rozdíl poněkud elegantně svým abstraktem:

Stávající metoda HTTP PUT umožňuje pouze úplné nahrazení dokumentu. Tento návrh přidává novou metodu HTTP, PATCH, pro úpravu existujícího prostředku HTTP.

Jeden je pro, když znáte všechny odpovědi, a druhý je pro aktualizaci malých bitů najednou. Někteří to považují za výhodu výkonu (odesílání méně věcí je rychlejší než odesílání spoustu věcí), ale existují i ​​další výhody v podobě odměny.

Konflikty

Přemýšlejte o zdroji, který má dvě pole, pole1 a pole2. Dva různé požadavky (požadavek A a (požadavek B) se pokusí aktualizovat jednu z těchto hodnot pole jako PUT po získání počáteční hodnoty zdroje pomocí požadavku GET.

Žádost A

Aktualizace pole1 je pravdivá.

PUT / foos / 123
{
  "field1": true,
  "field2": false
}

Žádost B

Aktualizace pole2 je pravdivá.

PUT / foos / 123
{
  "field1": false,
  "field2": true
}

Pokud obě pole začínají nepravdivě a každá žádost má v úmyslu aktualizovat pouze jedno pole, jen málo ví, že výsledky clobberují a pokaždé je vrací. Místo toho, abyste skončili s pravdivostí obou hodnot, budete mít jednoduše cokoli, co byl poslední požadavek, který bude „field1“: false a „field2“: true.

Pro některé je to funkce, ale jiní ji považují za chybu, protože pokud chtějí pouze aktualizovat jedno pole, proč musí poslat všechno?

Tito lidé se rozhodnou poslat pouze příslušná pole, která chtějí změnit, což je zjevné zneužití toho, jak má PUT fungovat, a vede k mnoha problémům.

Očekávání

Při vytváření API nejste vy a vaši spolupracovníci jediní lidé, kteří potřebují mít spravedlivá očekávání, jak to bude fungovat. Jiné systémy, například EmberJS, budou mít určitá očekávání, jak bude fungovat požadavek na PUT, a pokud začnete jít proti obilí a necháte PUT posílat částečné aktualizace, budete mít špatný čas.

Měli jsme případ, kdy aplikace EmberJS měla pouze některé modely představující existující zdroje v API, které byly místně naplněny částečnými daty. Chtěli změnit hodnotu jednoho pole v tomto modelu a uložit zdroj zpět do API.

Když si je EmberData uložili, všimli by si, že jde o PUT a snaží se odeslat co nejvíce dat. Protože měla pouze některé z hodnot polí, nakonec by poslala tělo s každým neznámým polem jako null, což zase vyprazdňovalo hodnoty z databáze a / nebo spouštělo chyby ověření u polí, která nechtěla vyprázdnit. .

PATCHing the problem

Něco, co hodně pomohlo v práci, bylo implementace PATCH. Nyní můžeme jednoduše odeslat pole, která hodláme aktualizovat, a všechno ostatní zůstane samo.

Předpokládejme, že používáme JSON-API a vytváříme něco pro spoluzakladatelskou společnost, jako je Ride. Chceme být schopni „zahájit“ cestu změnou stavu z „čekající“ na „in_progress“.

V v1.0 jsme používali PUT. Fungovalo to takto:

PUT / výlety / 123
{
  "data": [{
    "typ": "výlety",
    "id": "123",
    "atributy": {
      "status": "in_progress",
      "begin_at": null,
      "skončil_at": null
    }
    "vztahy": {
      "Řidič": {
        "data": {"type": "users", "id": "999"}
      }
    }
  }
}

Zde jsme změnili stav z jakéhokoli na in_progress.

Sidenote: Mám v úmyslu aktualizovat start_at sám nebo nechat API to udělat? Kdo ví!

Hlavním problémem je zpět k výše uvedenému příkladu konfliktu. Pokud někdo jiný měnil jinou hodnotu, jako je kdo je řidič, a omylem změnil jeho stav, zpět do čekající doby, pak by dostali chybovou zprávu s nápisem „Cesta již začala, nemůže se vrátit zpět do čekající“ a budou přemýšlel: „Ani jsem nevěděl, že to začalo, jen jsem chtěl, aby dnes Gary řídil.“ a všichni jsou zmatení.

Stejný požadavek jako PATCH může vypadat asi takto:

PATCH / výlety / 123
{
  "data": [{
    "typ": "výlety",
    "id": "123",
    "atributy": {
      "status": "in_progress"
    }
  }
}

Tím se vyřešil problém, že nedopatřením nedocházelo k dalším hodnotám, protože už kvůli tomu neposíláme věci. Ověřeny by měly být pouze atributy, které posíláme, a všechno chybějící by mělo být zcela ignorováno.

No, vlastně

V RFC 5789 (RFC pro metodu PATCH) příklad ukazuje věci fungující takto:

PATCH /file.txt HTTP / 1.1
Hostitel: www.example.com
Typ obsahu: aplikace / příklad
If-Match: "e0023aa4e"
Délka obsahu: 100
[popis změn]

Tento [popis změn] je některými považován za sled operací, které mají být provedeny s daným zdrojem, projevující se v seznamu podobných objektů JSON:

PATCH / my / data HTTP / 1.1
Hostitel: example.org
Délka obsahu: 326
Content-Type: application / json-patch + json
If-Match: "abc123"
[
  {"op": "test", "path": "/ a / b / c", "value": "foo"},
  {"op": "remove", "path": "/ a / b / c"},
  {"op": "add", "path": "/ a / b / c", "value": ["foo", "bar"]},
  {"op": "nahradit", "cesta": "/ a / b / c", "value": 42},
  {"op": "move", "from": "/ a / b / c", "path": "/ a / b / d"},
  {"op": "copy", "from": "/ a / b / d", "path": "/ a / b / e"}
]

Tento příklad je převzat z RFC 6902, který staví na samotné metodě PATCH, aby poskytl standardizovaný přístup k poskytování více atomových změn. Tento přístup se nazývá JSON PATCH a má svůj vlastní typ obsahu, aby bylo jasné, kdy se používá.

Nalezení informací o tom je poněkud zbytečné, ale existuje několik článků, bohužel #WellActually začíná být docela těžký.

Jeden z nejplodnějších článků o používání PATCH je od Williama Duranda. Je to skvělý technický článek, ale tvrzení „dělat tuto opravdu komplikovanou věc, kterou možná nebudete potřebovat nebo že to děláte špatně“ mě trochu vtírá špatně. Přečtěte si tento článek, zvažte jeho implementaci a pokud to nechcete, nedělejte to. Jsi fajn.

Ano, JSON PATCH je krásný a možná jej budete potřebovat pro své API, ale také to může být komplikace, kterou si nemusíte dělat starosti, v závislosti na složitosti vašich akcí JSON-API dříve doporučuje používat JSON PATCH, ale protože se rozhodl místo toho „zaslat, co potřebujete“. Něco velmi podobného přístupu „stačí poslat, co potřebujete“ bylo standardizováno jako RFC 7396.

Jednou z nejlepších věcí na rozhraních API založených na protokolu HTTP je schopnost reagovat na záhlaví typu obsahu. Se svými žádostmi o PATCH můžete prozatím pracovat s JSONem a v budoucnu přidat podporu JSON PATCH, pokud zjistíte, že ji potřebujete. Pomocí nástroje Accept-Patch (více zde) můžete inzerovat dostupnost těchto dvou různých režimů.

[žádost]
MOŽNOSTI / želvy / 123 HTTP / 1.1
Hostitel: www.amazingpets.com
[Odezva]
HTTP / 1,1 200 OK
Povolit: GET, PUT, POST, OPTIONS, HEAD, DELETE, PATCH
Accept-Patch: application / json, application / json-patch + json

Můžete tam také nechat PUT, pokud se vám líbí nápad mít idempotentní úspory. Nepoužívám PUT pro JSON v letech, protože téměř nikdy nechci idempotentní koncový bod pro tyto věci, když buduji CRUDish API, ale jsou skvělé pro nahrávání souborů, které by mohly selhat a vyžadovaly idempotentní opakování.