ChatGPT解决这个技术问题 Extra ChatGPT

Should I use PATCH or PUT in my REST API?

I want to design my rest endpoint with the appropriate method for the following scenario.

There is a group. Each group has a status. The group can be activated or inactivated by the admin.

Should I design my end point as

PUT /groups/api/v1/groups/{group id}/status/activate

OR

PATCH /groups/api/v1/groups/{group id}

with request body like 
{action:activate|deactivate}
Both are fine. But do take a look at the RFC for the JSON PATCH format (tools.ietf.org/html/rfc6902). PATCH expects to get some kind of diff/patch document for the payload (and raw JSON is not one of them).
@JørnWildt no, PUT would be a horrible choice. What are you putting there? PATCH is the only sensible option. Well, in this case you could use the PATCH format presented in the question, and just use the PUT method; the PUT example is just wrong.
There is nothing wrong in exposing one or more properties as standalone resources that a client can GET and modify with PUT. But, yes, the URL should then be /groups/api/v1/groups/{group id}/status to which you can PUT "active" or "inactive" or GET to read the current state.
Here's a good explanation of how PATCH should really be used: williamdurand.fr/2014/02/14/please-do-not-patch-like-an-idiot
"activate" is not adequate RESTful construction. You're probably trying to update the status to "active" or "deactive". in which case you can PATCH to .../status with the "active" or "deactive" string in the body. Or if you are trying to update a boolean at status.active, you can PATCH to .../status/active with the boolean in the body

C
Community

The PATCH method is the correct choice here as you're updating an existing resource - the group ID. PUT should only be used if you're replacing a resource in its entirety.

Further information on partial resource modification is available in RFC 5789. Specifically, the PUT method is described as follows:

Several applications extending the Hypertext Transfer Protocol (HTTP) require a feature to do partial resource modification. The existing HTTP PUT method only allows a complete replacement of a document. This proposal adds a new HTTP method, PATCH, to modify an existing HTTP resource.


To be fair, you could PUT the string 'activate' or 'deactivate' to the resource. As there (seems) to only be the one thing to toggle, completely replacing it is not such a huge deal. And it does allow for a (insignificantly) smaller request.
It is important to note that RFC 5789 is still in proposal phase and has not been officially accepted and is currently flagged as 'irrata exist'. This 'best practice' is highly debated and technically PATCH is not yet part of the HTTP standard.
Just my 2 cents a few years later: you could consider the status itself to be a resource, and if so, using PUT against /status would technically be replacing the status resource at that end point.
I would dare to argue against the docs, even though it's "the" RFC. The docs state that you should use PATCH to modify just a part of a resource, but it omitted the important thing that PATCH method is defined as a non-idempotent method. Why? If the PUT method was created with the update/replacement of the whole resource in mind, then why wasn't PATCH method created as an idempotent method like PUT, if its purpose was to just update the part of a resource? To me, it looks more of a difference in idempotency of update, like "a=5" (PUT) and "a=a+5" (PATCH). Both can update the entire resource.
j
jhhwilliams

The R in REST stands for resource

(Which isn't true, because it stands for Representational, but it's a good trick to remember the importance of Resources in REST).

About PUT /groups/api/v1/groups/{group id}/status/activate: you are not updating an "activate". An "activate" is not a thing, it's a verb. Verbs are never good resources. A rule of thumb: if the action, a verb, is in the URL, it probably is not RESTful.

What are you doing instead? Either you are "adding", "removing" or "updating" an activation on a Group, or if you prefer: manipulating a "status"-resource on a Group. Personally, I'd use "activations" because they are less ambiguous than the concept "status": creating a status is ambiguous, creating an activation is not.

POST /groups/{group id}/activation Creates (or requests the creation of) an activation.

PATCH /groups/{group id}/activation Updates some details of an existing activation. Since a group has only one activation, we know what activation-resource we are referring to.

PUT /groups/{group id}/activation Inserts-or-replaces the old activation. Since a group has only one activation, we know what activation-resource we are referring to.

DELETE /groups/{group id}/activation Will cancel, or remove the activation.

This pattern is useful when the "activation" of a Group has side-effects, such as payments being made, mails being sent and so on. Only POST and PATCH may have such side-effects. When e.g. a deletion of an activation needs to, say, notify users over mail, DELETE is not the right choice; in that case you probably want to create a deactivation resource: POST /groups/{group_id}/deactivation.

It is a good idea to follow these guidelines, because this standard contract makes it very clear for your clients, and all the proxies and layers between the client and you, know when it is safe to retry, and when not. Let's say the client is somewhere with flaky wifi, and its user clicks on "deactivate", which triggers a DELETE: If that fails, the client can simply retry, until it gets a 404, 200 or anything else it can handle. But if it triggers a POST to deactivation it knows not to retry: the POST implies this.
Any client now has a contract, which, when followed, will protect against sending out 42 emails "your group has been deactivated", simply because its HTTP-library kept retrying the call to the backend.

Updating a single attribute: use PATCH

PATCH /groups/{group id}

In case you wish to update an attribute. E.g. the "status" could be an attribute on Groups that can be set. An attribute such as "status" is often a good candidate to limit to a whitelist of values. Examples use some undefined JSON-scheme:

PATCH /groups/{group id} { "attributes": { "status": "active" } }
response: 200 OK

PATCH /groups/{group id} { "attributes": { "status": "deleted" } }
response: 406 Not Acceptable

Replacing the resource, without side-effects use PUT.

PUT /groups/{group id}

In case you wish to replace an entire Group. This does not necessarily mean that the server actually creates a new group and throws the old one out, e.g. the ids might remain the same. But for the clients, this is what PUT can mean: the client should assume he gets an entirely new item, based on the server's response.

The client should, in case of a PUT request, always send the entire resource, having all the data that is needed to create a new item: usually the same data as a POST-create would require.

PUT /groups/{group id} { "attributes": { "status": "active" } }
response: 406 Not Acceptable

PUT /groups/{group id} { "attributes": { "name": .... etc. "status": "active" } }
response: 201 Created or 200 OK, depending on whether we made a new one.

A very important requirement is that PUT is idempotent: if you require side-effects when updating a Group (or changing an activation), you should use PATCH. So, when the update results in e.g. sending out a mail, don't use PUT.


This was very informative to me. "This pattern is useful when the "activation" of a group has side-effects" - How come this pattern is useful, specifically in regards to when actions have side effects, as opposed to the OP initial endpoints
@Abdul, the pattern is useful for a lot of reasons, but wrt side-effects, it should be very clear to a client, what effects an action has. When, say, an iOS app decides to send the entire addressbook as "contacts" it should be extremely clear what side-effects the create, update, delete etc. of a Contact has. To avoid mass-mailing all contacts, for example.
In RESTfull PUT can also change the entities Identity - For example the PrimaryKey ID where it could cause a parallel request to fail. (for example updating the entire entity needs to delete some rows and add new ones, hence creating new entities) Where PATCH must never be able to do that, allowing for unlimited number of PATCH requests without affecting other "applications"
Very helpful answer. Thanks! I would also add a comment, just like in Luke's answer, pointing out that the difference between PUT/PATCH is not just the whole/partial update, it's also the idempotency that's different. This wasn't a mistake, it was an intentional decision and I think not a lot of people take this into the consideration, when deciding the HTTP method's usage.
I agree and disagree. RESTful API shouldn't reflect your domain. They tend to model the applications' use cases more than the business. There is a reason why RESTful apis follow RFC 2616. As a consumer I have no clue what the "side effects" of your business operations are. All I know is that your HTTP VERBS should reflect the operation on the RESOURCE. Therefore if a DELETE is idempotent, it means than the operation on the RESOURCE is so. Not the "side effects". Sending emails are not violations of "idempotency". That's a business concern, not a RESTful api.
C
Community

I would recommend using PATCH, because your resource 'group' has many properties but in this case, you are updating only the activation field(partial modification)

according to the RFC5789 (https://www.rfc-editor.org/rfc/rfc5789)

The existing HTTP PUT method only allows a complete replacement of a document. This proposal adds a new HTTP method, PATCH, to modify an existing HTTP resource.

Also, in more details,

The difference between the PUT and PATCH requests is reflected in the way the server processes the enclosed entity to modify the resource identified by the Request-URI. In a PUT request, the enclosed entity is considered to be a modified version of the resource stored on the origin server, and the client is requesting that the stored version be replaced. With PATCH, however, the enclosed entity contains a set of instructions describing how a resource currently residing on the origin server should be modified to produce a new version. The PATCH method affects the resource identified by the Request-URI, and it also MAY have side effects on other resources; i.e., new resources may be created, or existing ones modified, by the application of a PATCH. PATCH is neither safe nor idempotent as defined by [RFC2616], Section 9.1.

Clients need to choose when to use PATCH rather than PUT. For example, if the patch document size is larger than the size of the new resource data that would be used in a PUT, then it might make sense to use PUT instead of PATCH. A comparison to POST is even more difficult, because POST is used in widely varying ways and can encompass PUT and PATCH-like operations if the server chooses. If the operation does not modify the resource identified by the Request- URI in a predictable way, POST should be considered instead of PATCH or PUT.

The response code for PATCH is

The 204 response code is used because the response does not carry a message body (which a response with the 200 code would have). Note that other success codes could be used as well.

also refer thttp://restcookbook.com/HTTP%20Methods/patch/

Caveat: An API implementing PATCH must patch atomically. It MUST not be possible that resources are half-patched when requested by a GET.


A
Andrew Dobrowolski

Since you want to design an API using the REST architectural style you need to think about your use cases to decide which concepts are important enough to expose as resources. Should you decide to expose the status of a group as a sub-resource you could give it the following URI and implement support for both GET and PUT methods:

/groups/api/groups/{group id}/status

The downside of this approach over PATCH for modification is that you will not be able to make changes to more than one property of a group atomically and transactionally. If transactional changes are important then use PATCH.

If you do decide to expose the status as a sub-resource of a group it should be a link in the representation of the group. For example if the agent gets group 123 and accepts XML the response body could contain:

<group id="123">
  <status>Active</status>
  <link rel="/linkrels/groups/status" uri="/groups/api/groups/123/status"/>
  ...
</group>

A hyperlink is needed to fulfill the hypermedia as the engine of application state condition of the REST architectural style.


I
Ivan Sokalskiy

One possible option to implement such behavior is

PUT /groups/api/v1/groups/{group id}/status
{
    "Status":"Activated"
}

And obviously, if someone need to deactivate it, PUT will have Deactivated status in JSON.

In case of necessity of mass activation/deactivation, PATCH can step into the game (not for exact group, but for groups resource:

PATCH /groups/api/v1/groups
{
    { “op”: “replace”, “path”: “/group1/status”, “value”: “Activated” },
    { “op”: “replace”, “path”: “/group7/status”, “value”: “Activated” },
    { “op”: “replace”, “path”: “/group9/status”, “value”: “Deactivated” }
}

In general this is idea as @Andrew Dobrowolski suggesting, but with slight changes in exact realization.


r
rich remer

I would generally prefer something a bit simpler, like activate/deactivate sub-resource (linked by a Link header with rel=service).

POST /groups/api/v1/groups/{group id}/activate

or

POST /groups/api/v1/groups/{group id}/deactivate

For the consumer, this interface is dead-simple, and it follows REST principles without bogging you down in conceptualizing "activations" as individual resources.