Gebruik van PUT vs PATCH-methoden in REST API real-life scenario’s

Allereerst enkele definities:

PUT is gedefinieerd in Sectie 9.6 RFC 2616:

De PUT-methode vraagt ​​dat de bijgevoegde entiteit wordt opgeslagen onder de opgegeven Request-URI. Als de Request-URI verwijst naar een reeds bestaande bron, moet de bijgevoegde entiteit MOET worden beschouwd als een gewijzigde versie van degene die zich op de oorspronkelijke server bevindt. Als de Request-URI niet naar een bestaande bron verwijst en die URI door de verzoekende user-agent als een nieuwe bron kan worden gedefinieerd, kan de oorspronkelijke server de bron met die URI maken.

PATCH is gedefinieerd in RFC 5789:

De PATCH-methode verzoekt om een reeks wijzigingenbeschreven in de
aanvraagentiteit worden toegepast op de bron die is geïdentificeerd door de aanvraag-
URI.

Ook volgens RFC 2616 Sectie 9.1.2PUT is idempotent en PATCH niet.

Laten we nu eens naar een echt voorbeeld kijken. Wanneer ik POST naar /usersmet de gegevens {username: 'skwee357', email: '[email protected]'}en de server in staat is om een ​​bron aan te maken , zal het antwoorden met 201 en resource locatie (laten we aannemen /users/1) en elke volgende aanroep naar GET /users/1zal {id: 1, username: 'skwee357', email: '[email protected]'}.

Laten we nu zeggen dat ik mijn e-mailadres wil wijzigen. E-mailwijziging wordt beschouwd als “een reeks wijzigingen” en daarom moet ik /users/1PATCHEN met “patchdocument“. In mijn geval zou dat het json-document zijn: {email: '[email protected]'}. De server retourneert vervolgens 200 (ervan uitgaande dat de toestemming in orde is). Dit brengt me bij de eerste vraag:

  • PATCH is NIET idempotent. Dat staat in RFC 2616 en RFC 5789. Als ik echter hetzelfde PATCH-verzoek doe (met mijn nieuwe e-mailadres), krijg ik dezelfde bronstatus (waarbij mijn e-mailadres wordt gewijzigd in de gevraagde waarde). Waarom is PATCH dan niet idempotent?

PATCH is een relatief nieuw werkwoord (RFC geïntroduceerd in maart 2010), en het gaat om het probleem van het “patchen” of wijzigen van een reeks velden. Voordat PATCH werd geïntroduceerd, gebruikte iedereen PUT om bronnen bij te werken. Maar nadat PATCH was geïntroduceerd, laat het me in de war over waar PUT voor wordt gebruikt. En dit brengt me bij mijn tweede (en de belangrijkste) vraag:

  • Wat is het echte verschil tussen PUT en PATCH? Ik heb ergens gelezen dat PUT kan worden gebruikt om de hele entiteit vervangenonder een specifieke bron, dus je moet de volledige entiteit verzenden (in plaats van een set attributen zoals bij PATCH). Wat is het echte praktische gebruik voor een dergelijk geval? Wanneer wilt u een entiteit vervangen/overschrijven op een specifieke resource-URI en waarom wordt een dergelijke bewerking niet overwogen om de entiteit bij te werken/patchen? De enige praktische use case die ik zie voor PUT is het uitgeven van een PUT op een collectie, d.w.z. /usersom de hele collectie te vervangen. Het uitgeven van een PUT op een specifieke entiteit heeft geen zin nadat PATCH is geïntroduceerd. Heb ik het mis?

Antwoord 1, autoriteit 100%

OPMERKING: toen ik voor het eerst over RUST las, was idempotentie een verwarrend concept om te proberen het goed te doen. Ik had het nog steeds niet helemaal goed in mijn oorspronkelijke antwoord, zoals blijkt uit verdere opmerkingen (en Jason Hoetger’s antwoord). Een tijdje heb ik me verzet tegen het uitgebreid bijwerken van dit antwoord, om te voorkomen dat Jason effectief plagiaat pleegt, maar ik ben het nu aan het bewerken omdat, nou ja, mij werd gevraagd (in de opmerkingen).

Na het lezen van mijn antwoord, raad ik je aan om ook Jason Hoetger’s uitstekende antwoordop deze vraag te lezen, en ik zal proberen maak mijn antwoord beter zonder gewoon van Jason te stelen.

Waarom is PUT idempotent?

Zoals je hebt opgemerkt in je RFC 2616-citatie, wordt PUT als idempotent beschouwd. Wanneer je een grondstof PUT, zijn deze twee veronderstellingen in het spel:

  1. U verwijst naar een entiteit, niet naar een verzameling.

  2. De entiteit die u levert is compleet (de geheleentiteit).

Laten we eens naar een van uw voorbeelden kijken.

{ "username": "skwee357", "email": "[email protected]" }

Als u dit document POST naar /users, zoals u voorstelt, krijgt u mogelijk een entiteit terug zoals

## /users/1
{
    "username": "skwee357",
    "email": "[email protected]"
}

Als u deze entiteit later wilt wijzigen, kiest u tussen PUT en PATCH. Een PUT kan er als volgt uitzien:

PUT /users/1
{
    "username": "skwee357",
    "email": "[email protected]"       // new email address
}

Je kunt hetzelfde bereiken met PATCH. Dat kan er als volgt uitzien:

PATCH /users/1
{
    "email": "[email protected]"       // new email address
}

Je merkt meteen het verschil tussen deze twee. De PUT bevatte alle parameters van deze gebruiker, maar PATCH bevatte alleen de parameter die werd gewijzigd (email).

Als u PUT gebruikt, wordt aangenomen dat u de volledige entiteit verzendt, en die volledige entiteit vervangtelke bestaande entiteit op die URI. In het bovenstaande voorbeeld bereiken de PUT en PATCH hetzelfde doel: ze wijzigen allebei het e-mailadres van deze gebruiker. Maar PUT handelt het af door de hele entiteit te vervangen, terwijl PATCH alleen de velden bijwerkt die zijn opgegeven, en de anderen met rust laat.

Aangezien PUT-verzoeken de hele entiteit omvatten, zou het, als u hetzelfde verzoek herhaaldelijk doet, altijd hetzelfde resultaat moeten hebben (de gegevens die u hebt verzonden, zijn nu de volledige gegevens van de entiteit). Daarom is PUT idempotent.

PUT verkeerd gebruiken

Wat gebeurt er als u de bovenstaande PATCH-gegevens gebruikt in een PUT-verzoek?

GET /users/1
{
    "username": "skwee357",
    "email": "[email protected]"
}
PUT /users/1
{
    "email": "[email protected]"       // new email address
}
GET /users/1
{
    "email": "[email protected]"      // new email address... and nothing else!
}

(Voor de doeleinden van deze vraag ga ik ervan uit dat de server geen specifieke verplichte velden heeft en dat dit zou kunnen gebeuren… dat is in werkelijkheid misschien niet het geval.)

Aangezien we PUT gebruikten, maar alleen emailgaven, is dat nu het enige in deze entiteit. Dit heeft geleid tot gegevensverlies.

Dit voorbeeld is hier ter illustratie — doe dit nooit. Dit PUT-verzoek is technisch gezien idempotent, maar dat betekent niet dat het geen vreselijk, gebroken idee is.

Hoe kan PATCH idempotent zijn?

In het bovenstaande voorbeeld was PATCH idempotent. Je hebt een wijziging aangebracht, maar als je dezelfde wijziging steeds opnieuw aanbrengt, geeft dit altijd hetzelfde resultaat: je hebt het e-mailadres gewijzigd in de nieuwe waarde.

GET /users/1
{
    "username": "skwee357",
    "email": "[email protected]"
}
PATCH /users/1
{
    "email": "[email protected]"       // new email address
}
GET /users/1
{
    "username": "skwee357",
    "email": "[email protected]"       // email address was changed
}
PATCH /users/1
{
    "email": "[email protected]"       // new email address... again
}
GET /users/1
{
    "username": "skwee357",
    "email": "[email protected]"       // nothing changed since last GET
}

Mijn oorspronkelijke voorbeeld, gecorrigeerd voor nauwkeurigheid

Ik had oorspronkelijk voorbeelden waarvan ik dacht dat ze niet-idempotentieel vertoonden, maar ze waren misleidend/onjuist. Ik ga de voorbeelden behouden, maar gebruik ze om iets anders te illustreren: dat meerdere PATCH-documenten tegen dezelfde entiteit, waarbij verschillende attributen worden gewijzigd, de PATCH’s niet niet-idempotent maken.

Stel dat er in het verleden een gebruiker is toegevoegd. Dit is de staat van waaruit u begint.

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "10001"
}

Na een PATCH heeft u een gewijzigde entiteit:

PATCH /users/1
{"email": "[email protected]"}
{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",    // the email changed, yay!
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "10001"
}

Als u uw PATCH vervolgens herhaaldelijk toepast, krijgt u hetzelfde resultaat: de e-mail is gewijzigd in de nieuwe waarde. A gaat erin, A komt eruit, dus dit is idempotent.

Een uur later, nadat je koffie bent gaan zetten en een pauze hebt genomen, komt er iemand anders langs met zijn eigen PATCH. Het lijkt erop dat het postkantoor enkele wijzigingen heeft aangebracht.

PATCH /users/1
{"zip": "12345"}
{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",  // still the new email you set
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "12345"                      // and this change as well
}

Aangezien deze PATCH van het postkantoor zich niet bezighoudt met e-mail, alleen met postcode, krijgt het, als het herhaaldelijk wordt toegepast, ook hetzelfde resultaat: de postcode wordt ingesteld op de nieuwe waarde. A gaat erin, A komt eruit, daarom is dit ookidempotent.

De volgende dag besluit je je PATCH opnieuw te verzenden.

PATCH /users/1
{"email": "[email protected]"}
{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "12345"
}

Uw patch heeft hetzelfde effect als gisteren: het heeft het e-mailadres ingesteld. A ging erin, A kwam eruit, dus dit is ook idempotent.

Wat ik fout had in mijn oorspronkelijke antwoord

Ik wil een belangrijk onderscheid maken (iets dat ik fout had in mijn oorspronkelijke antwoord). Veel servers reageren op uw REST-verzoeken door de nieuwe entiteitsstatus terug te sturen, met uw eventuele wijzigingen. Dus als je dit antwoordterugkrijgt, is het anders dan het antwoord dat je gisteren hebt gekregen, omdat de postcode niet de postcode is die je de vorige keer hebt ontvangen. Uw verzoek ging echter niet over de postcode, alleen over de e-mail. Uw PATCH-document is dus nog steeds idempotent – de e-mail die u in PATCH hebt verzonden, is nu het e-mailadres van de entiteit.

Dus wanneer is PATCH dan niet idempotent?

Voor een volledige behandeling van deze vraag verwijs ik u nogmaals naar Het antwoord van Jason Hoetger. Ik laat het hierbij, want ik denk eerlijk gezegd niet dat ik dit deel beter kan beantwoorden dan hij al heeft gedaan.


Antwoord 2, autoriteit 35%

Hoewel Dan Lowe’s uitstekende antwoord de OP’s vraag over het verschil tussen PUT en PATCH zeer grondig beantwoordde, is het antwoord op de vraag waarom PATCH niet idempotent is niet helemaal correct.

Om te laten zien waarom PATCH niet idempotent is, helpt het om te beginnen met de definitie van idempotent (van Wikipedia):

De term idempotent wordt uitgebreider gebruikt om een ​​bewerking te beschrijven die dezelfde resultaten zal opleveren als deze één of meerdere keren wordt uitgevoerd […] Een idempotente functie is een functie met de eigenschap f(f(x)) = f( x) voor elke waarde x.

In meer toegankelijke taal kan een idempotente PATCH worden gedefinieerd als: Na het PATCHEN van een bron met een patchdocument, zullen alle volgende PATCH-aanroepen naar dezelfde bron met hetzelfde patchdocument de bron niet wijzigen.

Omgekeerd is een niet-idempotente bewerking een bewerking waarbij f(f(x)) != f(x), wat voor PATCH kan worden aangegeven als: Na het PATCHEN van een bron met een patchdocument, daaropvolgende PATCH-aanroepen naar hetzelfde bron met hetzelfde patchdocument wijzigde bron.

Om een ​​niet-idempotente PATCH te illustreren, veronderstel dat er een /users bron is, en veronderstel dat het aanroepen van GET /userseen lijst met gebruikers retourneert, momenteel:

[{ "id": 1, "username": "firstuser", "email": "[email protected]" }]

Stel in plaats van /users/{id} te PATCHEN, zoals in het OP’s voorbeeld, dat de server PATCHing van /users toestaat. Laten we dit PATCH-verzoek indienen:

PATCH /users
[{ "op": "add", "username": "newuser", "email": "[email protected]" }]

Ons patchdocument geeft de server de opdracht om een ​​nieuwe gebruiker met de naam newusertoe te voegen aan de lijst met gebruikers. Nadat ik dit de eerste keer had aangeroepen, zou GET /usersterugkeren:

[{ "id": 1, "username": "firstuser", "email": "[email protected]" },
 { "id": 2, "username": "newuser", "email": "[email protected]" }]

Als we nu het exact dezelfdePATCH-verzoek als hierboven indienen, wat gebeurt er dan? (Laten we voor dit voorbeeld aannemen dat de /users-bron dubbele gebruikersnamen toestaat.) De “op” is “add”, dus een nieuwe gebruiker wordt toegevoegd aan de lijst, en een volgende GET /usersgeeft als resultaat:

[{ "id": 1, "username": "firstuser", "email": "[email protected]" },
 { "id": 2, "username": "newuser", "email": "[email protected]" },
 { "id": 3, "username": "newuser", "email": "[email protected]" }]

De bron /users is opnieuwgewijzigd, ook al hebben we de exact dezelfdePATCH uitgegeven tegen het exact hetzelfdeeindpunt. Als onze PATCH f(x) is, is f(f(x)) niet hetzelfde als f(x), en daarom is deze specifieke PATCH niet idempotent.

Hoewel PATCH niet gegarandeerdidempotent is, is er niets in de PATCH-specificatie die u ervan weerhoudt om alle PATCH-bewerkingen op uw specifieke server idempotent uit te voeren. RFC 5789 verwacht zelfs voordelen van idempotente PATCH-verzoeken:

Een PATCH-verzoek kan op een zodanige manier worden uitgegeven dat het idempotent is,
wat ook helpt om slechte resultaten van botsingen tussen twee te voorkomen
PATCH-verzoeken op dezelfde bron in een vergelijkbaar tijdsbestek.

In het voorbeeld van Dan is zijn PATCH-operatie in feite idempotent. In dat voorbeeld veranderde de entiteit /users/1 tussen onze PATCH-verzoeken, maar niet vanwegeonze PATCH-verzoeken; het was eigenlijk het anderepatchdocument van het postkantoor dat ervoor zorgde dat de postcode veranderde. De andere PATCH van het postkantoor is een andere operatie; als onze PATCH f(x) is, is de PATCH van het postkantoor g(x). Idempotence stelt dat f(f(f(x))) = f(x), maar geeft geen garanties over f(g(f(x))).


Antwoord 3, autoriteit 7%

Ik was hier ook benieuwd naar en vond een paar interessante artikelen. Ik zal je vraag misschien niet volledig beantwoorden, maar dit geeft in ieder geval wat meer informatie.

http://restful-api-design.readthedocs.org/en/latest/methods .html

De HTTP RFC specificeert dat PUT een volledig nieuwe bron moet gebruiken
vertegenwoordiging als de verzoekende entiteit. Dit betekent dat als bijvoorbeeld
alleen bepaalde attributen worden verstrekt, die moeten worden verwijderd (d.w.z. set
naar nul).

Gezien dat zou een PUT het hele object moeten verzenden. Bijvoorbeeld

/users/1
PUT {id: 1, username: 'skwee357', email: '[email protected]'}

Dit zou de e-mail effectief bijwerken. De reden dat PUT misschien niet al te effectief is, is dat het eigenlijk nutteloos is om één veld aan te passen en de gebruikersnaam op te nemen. Het volgende voorbeeld laat het verschil zien.

/users/1
PUT {id: 1, email: '[email protected]'}

Als de PUT was ontworpen volgens de specificaties, zou de PUT de gebruikersnaam op nul zetten en zou je het volgende terugkrijgen.

{id: 1, username: null, email: '[email protected]'}

Als u een PATCH gebruikt, werkt u alleen het veld bij dat u opgeeft en laat u de rest met rust zoals in uw voorbeeld.

De volgende kijk op de PATCH is een beetje anders dan ik nog nooit eerder heb gezien.

http://williamdurand.fr/2014/02/ 14/alsjeblieft-niet-patchen-als-een-idioot/

Het verschil tussen de PUT- en PATCH-verzoeken wordt weerspiegeld in de
manier waarop de server de ingesloten entiteit verwerkt om de bron te wijzigen
geïdentificeerd door de Request-URI. In een PUT-verzoek, de bijgevoegde entiteit
wordt beschouwd als een gewijzigde versie van de bron die is opgeslagen op de
origin server, en de client vraagt ​​om de opgeslagen versie
vervangen. Met PATCH bevat de bijgevoegde entiteit echter een set van
instructies die beschrijven hoe een bron die momenteel op de
origin server moet worden aangepast om een ​​nieuwe versie te produceren. De PATCH
methode beïnvloedt de bron die wordt geïdentificeerd door de Request-URI, en het ook
KAN bijwerkingen hebben op andere bronnen; d.w.z. nieuwe bronnen kunnen zijn:
gemaakt of bestaande gewijzigd door de toepassing van een PATCH.

PATCH /users/123
[
    { "op": "replace", "path": "/email", "value": "[email protected]" }
]

U behandelt de PATCH min of meer als een manier om een ​​veld bij te werken. Dus in plaats van het gedeeltelijke object te verzenden, verzend je de bewerking. d.w.z. Vervang e-mail door waarde.

Hiermee eindigt het artikel.

Het is vermeldenswaard dat PATCH niet echt is ontworpen voor echte REST
API’s, aangezien het proefschrift van Fielding geen enkele manier definieert om gedeeltelijk
middelen wijzigen. Maar Roy Fielding zelf zei dat PATCH was…
iets dat [hij] heeft gemaakt voor het oorspronkelijke HTTP/1.1-voorstel omdat
gedeeltelijke PUT is nooit REST. Zeker dat je geen volledige overdraagt
representatie, maar REST vereist niet dat representaties zijn
toch voltooien.

Ik weet niet of ik het helemaal eens ben met het artikel, zoals veel commentatoren aangeven. Het doorsturen van een gedeeltelijke weergave kan eenvoudig een beschrijving van de wijzigingen zijn.

Voor mij ben ik gemengd bij het gebruik van PATCH. Voor het grootste deel zal ik PUT behandelen als een PATCH, aangezien het enige echte verschil dat ik tot nu toe heb opgemerkt, is dat PUT ontbrekende waarden “zou” moeten instellen op null. Het is misschien niet de ‘meest correcte’ manier om het te doen, maar veel succes met het perfect coderen.


Antwoord 4, autoriteit 5%

TLDR – Domme versie

PUT=> Stel alle nieuwe kenmerken in voor een bestaande bron.

PATCH=> Een bestaande bron gedeeltelijk bijwerken (niet alle kenmerken vereist).


Antwoord 5, autoriteit 2%

Het verschil tussen PUT en PATCH is dat:

  1. PUT moet idempotent zijn. Om dat te bereiken, moet u de volledige bron in de hoofdtekst van het verzoek plaatsen.
  2. PATCH kan niet-idempotent zijn. Wat impliceert dat het in sommige gevallen ook idempotent kan zijn, zoals de gevallen die u beschreef.

PATCH vereist enige “patchtaal” om de server te vertellen hoe de bron moet worden gewijzigd. De beller en de server moeten enkele “bewerkingen” definiëren, zoals “toevoegen”, “vervangen”, “verwijderen”. Bijvoorbeeld:

GET /contacts/1
{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",
  "state": "NY",
  "zip": "10001"
}
PATCH /contacts/1
{
 [{"operation": "add", "field": "address", "value": "123 main street"},
  {"operation": "replace", "field": "email", "value": "[email protected]"},
  {"operation": "delete", "field": "zip"}]
}
GET /contacts/1
{
  "id": 1,
  "name": "Sam Kwee",
  "email": "[email protected]",
  "state": "NY",
  "address": "123 main street",
}

In plaats van expliciete “bewerkings”-velden te gebruiken, kan de patchtaal deze impliciet maken door conventies te definiëren zoals:

in de hoofdtekst van het PATCH-verzoek:

  1. Het bestaan ​​van een veld betekent dat veld “vervangen” of “toevoegen”.
  2. Als de waarde van een veld null is, betekent dit dat je dat veld moet verwijderen.

Met de bovenstaande conventie kan de PATCH in het voorbeeld de volgende vorm aannemen:

PATCH /contacts/1
{
  "address": "123 main street",
  "email": "[email protected]",
  "zip":
}

Dat ziet er beknopter en gebruiksvriendelijker uit. Maar de gebruikers moeten zich bewust zijn van de onderliggende conventie.

Met de bewerkingen die ik hierboven noemde, is de PATCH nog steeds idempotent. Maar als je bewerkingen definieert zoals: “verhogen” of “toevoegen”, kun je gemakkelijk zien dat het niet meer idempotent zal zijn.


Antwoord 6

Naar mijn bescheiden mening betekent idempotentie:

  • ZET:

Ik stuur een concurrerende resourcedefinitie, dus – de resulterende resourcestatus is precies zoals gedefinieerd door PUT-parameters. Elke keer dat ik de bron bijwerk met dezelfde PUT-params – de resulterende status is precies hetzelfde.

  • PATCH:

Ik heb slechts een deel van de resourcedefinitie verzonden, dus het kan gebeuren dat andere gebruikers in de tussentijd de OTHER-parameters van deze resource bijwerken. Bijgevolg kunnen opeenvolgende patches met dezelfde parameters en hun waarden resulteren in een verschillende bronstatus. Bijvoorbeeld:

Veronderstel een object dat als volgt is gedefinieerd:

AUTO:
– de kleur zwart,
– type: sedan,
– zitplaatsen: 5

Ik patch het met:

{color: ‘red’}

Het resulterende object is:

AUTO:
– kleur rood,
– type: sedan,
– zitplaatsen: 5

Vervolgens patchen enkele andere gebruikers deze auto met:

{type: ‘hatchback’}

dus het resulterende object is:

AUTO:
– kleur rood,
– type: hatchback,
– zitplaatsen: 5

Als ik dit object nu opnieuw patch met:

{color: ‘red’}

het resulterende object is:

AUTO:
– kleur rood,
– type: hatchback,
– zitplaatsen: 5

Wat is er ANDERS aan wat ik eerder heb!

Dit is de reden waarom PATCH niet idempotent is terwijl PUT idempotent is.


Antwoord 7

Om de discussie over de idempotentie af te sluiten, moet ik opmerken dat men idempotentie in de REST-context op twee manieren kan definiëren. Laten we eerst een paar dingen formaliseren:

Een bronis een functie waarvan het codomain de klasse van strings is. Met andere woorden, een resource is een subset van String ? Any, waarbij alle sleutels uniek zijn. Laten we de klasse van de bronnen Resnoemen.

Een REST-bewerking op resources is een functie f(x: Res, y: Res): Res. Twee voorbeelden van REST-bewerkingen zijn:

  • PUT(x: Res, y: Res): Res = x, en
  • PATCH(x: Res, y: Res): Res, wat werkt als PATCH({a: 2}, {a: 1, b: 3}) == {a: 2, b: 3}.

(Deze definitie is specifiek bedoeld om ruzie te maken over PUTen POST, en heeft bijvoorbeeld weinig zin in GETen POST, omdat het niet om doorzettingsvermogen geeft).

Nu, door x: Res(informatisch gesproken, met currying), PUT(x: Res)en PATCH(x: Res)zijn univariate functies van het type Res >Res.

  1. Een functie g: Res >Reswordt globaal idempotentgenoemd, wanneer g 0 g == g, dwz voor elke y: Res, g(g(y)) = g(y).

  2. Laat x: Reseen bron zijn, en k = x.keys. Een functie g = f(x)wordt links idempotentgenoemd, wanneer we voor elke y: Resg(g(y))|? == g(y)|?. Het betekent in feite dat het resultaat hetzelfde zou moeten zijn, als we kijken naar de toegepaste sleutels.

Dus PATCH(x)is niet globaal idempotent, maar wordt idempotent gelaten. En links idempotentie is het ding dat hier van belang is: als we een paar sleutels van de bron patchen, willen we dat die sleutels hetzelfde zijn als we hem opnieuw patchen, en de rest van de bron interesseert ons niet.

En als RFC het heeft over PATCH die niet idempotent is, heeft het het over wereldwijde idempotentie. Nou, het is maar goed dat het niet wereldwijd idempotent is, anders zou het een mislukte operatie zijn geweest.


Nu probeert Het antwoord van Jason Hoetgeraan te tonen dat PATCH niet eens idempotent blijft, maar het breekt te veel dingen om dit te doen:

  • Allereerst wordt PATCH op een set gebruikt, hoewel PATCH is gedefinieerd om te werken op kaarten/woordenboeken/sleutelwaarde-objecten.
  • Als iemand PATCH echt op sets wil toepassen, dan is er een natuurlijke vertaling die moet worden gebruikt: t: Set<T> >Map<T, Boolean>, gedefinieerd met x in A iff t(A)(x) == True. Met deze definitie wordt patchen idempotent gelaten.
  • In het voorbeeld werd deze vertaling niet gebruikt, in plaats daarvan werkt de PATCH als een POST. Allereerst, waarom wordt er een ID gegenereerd voor het object? En wanneer wordt het gegenereerd? Als het object eerst wordt vergeleken met de elementen van de set, en als er geen overeenkomend object wordt gevonden, wordt de ID gegenereerd, dan zou het programma opnieuw anders moeten werken ({id: 1, email: "[email protected]"}moet overeenkomen met {email: "[email protected]"}, anders is het programma altijd kapot en kan de PATCH onmogelijk worden gepatcht). Als de ID wordt gegenereerd voordat de set wordt vergeleken, is het programma opnieuw kapot.

Je kunt voorbeelden maken van PUT die niet-idempotent is door de helft van de dingen die in dit voorbeeld kapot zijn te breken:

  • Een voorbeeld met gegenereerde extra functiesis versiebeheer. Men kan het aantal wijzigingen op een enkel object bijhouden. In dit geval is PUT niet idempotent: PUT /user/12 {email: "[email protected]"}resulteert in {email: "...", version: 1}de eerste keer, en {email: "...", version: 2}de tweede keer.
  • Als je met de ID’s knoeit, kan men elke keer dat het object wordt bijgewerkt een nieuwe ID genereren, wat resulteert in een niet-idempotente PUT.

Alle bovenstaande voorbeelden zijn natuurlijke voorbeelden die men kan tegenkomen.


Mijn laatste punt is dat PATCH niet wereldwijd idempotentmag zijn, anders krijg je niet het gewenste effect. U wilt het e-mailadres van uw gebruiker wijzigen zonder de rest van de informatie aan te raken, en u wilt de wijzigingen van een andere partij die dezelfde bron gebruikt niet overschrijven.


Antwoord 8

Iedereen heeft de PUT vs PATCH beantwoord. Ik wilde net beantwoorden welk deel van de titel van de oorspronkelijke vraag luidt: “… in REST API real-life scenario’s”. In de echte wereld overkwam mij dit met een internettoepassing die een RESTful-server had en een relationele database met een Customer-tabel die “breed” was (ongeveer 40 kolommen). Ik heb per ongeluk PUT gebruikt, maar was ervan uitgegaan dat het een SQL Update-opdracht was en had niet alle kolommen ingevuld. Problemen: 1) Sommige kolommen waren optioneel (dus blanco was een geldig antwoord), 2) veel kolommen veranderden zelden, 3) sommige kolommen mocht de gebruiker niet wijzigen, zoals de tijdstempel van de laatste aankoopdatum, 4) één kolom was gratis – formuliertekst “Opmerkingen” kolom die gebruikers ijverig vulden met opmerkingen van een halve pagina klantenservice, zoals de naam van de echtgenote om te vragen naar OF de gebruikelijke volgorde, 5) Ik werkte op dat moment aan een internet-app en ik maakte me zorgen over de pakketgrootte.

Het nadeel van PUT is dat het je dwingt een groot pakket informatie te verzenden (alle kolommen inclusief de hele kolom Opmerkingen, ook al zijn er maar een paar dingen veranderd) EN probleem met meerdere gebruikers van 2+ gebruikers die dezelfde klant tegelijkertijd bewerken (dus de laatste die op Update drukt, wint). Het nadeel van PATCH is dat je aan de kijk-/schermkant moet bijhouden wat er is veranderd en dat je enige intelligentie moet hebben om alleen de gewijzigde onderdelen te verzenden. Het probleem met meerdere gebruikers van Patch is beperkt tot het bewerken van dezelfde kolom(men) van dezelfde klant.


Antwoord 9

Een heel mooie uitleg is hier-

https://blog.segunolalive.com/posts/restful-api-design-%E2%80%94-put-vs-patch/#: ~:text=RFC%205789,not%20required%20to%20be%20idempotent.

Een normaal laadvermogen-
// Huis op perceel 1
{
adres: ‘perceel 1’,
eigenaar: ‘segun’,
type: ‘duplex’,
kleur groen’,
kamers: ‘5’,
keukens: ‘1’,
ramen: 20
}
PUT voor bijgewerkt-
// PUT verzoek payload om vensters van House op perceel 1 bij te werken
{
adres: ‘perceel 1’,
eigenaar: ‘segun’,
type: ‘duplex’,
kleur groen’,
kamers: ‘5’,
keukens: ‘1’,
ramen: 21
}
Opmerking: in bovenstaande payload proberen we de vensters bij te werken van 20 naar 21.

Bekijk nu de PATH-payload-
// Patch verzoek payload om vensters op het huis bij te werken
{
ramen: 21
}

Aangezien PATCH niet idempotent is, worden mislukte verzoeken niet automatisch opnieuw geprobeerd op het netwerk. Ook als een PATCH-verzoek wordt gedaan naar een niet-bestaande URL, bijvoorbeeld een poging om de voordeur van een niet-bestaand gebouw te vervangen, zou het gewoon moeten mislukken zonder een nieuwe bron te creëren in tegenstelling tot PUT, die een nieuwe zou creëren met behulp van de payload. Nu ik erover nadenk, het zal vreemd zijn om een ​​eenzame deur op een huisadres te hebben.


Antwoord 10

Een extra informatie die ik alleen wil toevoegen, is dat een PATCH-verzoek minder bandbreedte gebruikt in vergelijking met een PUT-verzoek, omdat slechts een deel van de gegevens wordt verzonden en niet de hele entiteit. Gebruik dus gewoon een PATCH-verzoek voor updates van specifieke records zoals (1-3 records) terwijl PUT-verzoeken voor het bijwerken van een grotere hoeveelheid gegevens. Dat is het, denk er niet te veel over na en maak je er niet te veel zorgen over.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

1 × 4 =

Other episodes