Should I allow sending complete structures when us

2019-05-13 05:05发布

问题:

I am designing a REST API and I wonder what the recommended way to handle updates to resources would be. More specifically, I would allow updates through a PUT on the resource, but what should I allow in the body of the PUT request?

  1. Always the complete structure of the resource?
  2. Always the subpart (that changed) of the structure of the resource?
  3. A combination of both?

For example, take the resource http://example.org/api/v1/dogs/packs/p1. A GET on this resource would give the following:

Request:
GET http://example.org/api/v1/dogs/packs/p1
Accept: application/xml

Response:
<pack>
  <owner>David</owner>
  <dogs>
    <dog>
      <name>Woofer</name>
      <breed>Basset Hound</breed>
    </dog>
    <dog>
      <name>Mr. Bones</name>
      <breed>Basset Hound</breed>
    </dog>
  </dogs>
</pack>

Suppose I want to add a dog (Sniffers the Basset Hound) to the pack, would I support either:

Request:
PUT http://example.org/api/v1/dogs/packs/p1
<dog>
  <name>Sniffers</name>
  <breed>Basset Hound</breed>
</dog>

Response:
HTTP/1.1 200 OK

or

Request:
PUT http://example.org/api/v1/dogs/packs/p1
<pack>
  <owner>David</owner>
  <dogs>
    <dog>
      <name>Woofer</name>
      <breed>Basset Hound</breed>
    </dog>
    <dog>
      <name>Mr. Bones</name>
      <breed>Basset Hound</breed>
    </dog>
    <dog>
      <name>Sniffers</name>
      <breed>Basset Hound</breed>
    </dog>
  </dogs>
</pack>

Response:
HTTP/1.1 200 OK

or both? If supporting updates through subsections of the structure is recommended, how would I handle deletes (such as when a dog dies)? Through query parameters?

回答1:

Yes, always send the complete representation of the resource. Otherwise, you would (according to common definition and usage of PUT) replace the pack with just this one dog.

However, you might want to consider the following:

  • use XML namespaces or other means of versioning (e.g. ?version=2) outside of the URL path
  • POST the dog you want to add to /dogs/packs/p1. POST by definition creates a subordinate resource, so that would add the dog to the pack.
  • Refurbish your URLs a bit. It seems to me you really want to have /dogs/1234, /dogs/1235 and so forth, and then /packs/p1, /packs/p2. Then you could also simply POST <dog id="1"> to the pack, too.

Keep in mind that REST requires you to properly identify resources. packs are not really a subordinate resource to the dogs, and each dog should have some sort of unique identifier. Then, when accessing /packs/p1/1234, you would probably want to redirect to /dogs/1234. Or, alternatively, you would simply not make that URL available, despite accepting POSTing of subordinate resources to the respective pack.

The more I think about it, the more sense the POST approach makes. Maybe you could even have a /packs/p1/dogs/ resource for all the dogs, separate from the pack. Then, you can PUT stuff like owner information etc to /packs/p1, GET a list of all dogs via /packs/p1/dogs/ (which should contain a list of URLs to each dog in the pack, e.g. /packs/p1/dogs/1234, see HATEOAS), add a new dog to the pack by POSTing to /packs/p1/dogs/, and remove a dog by DELETEing /packs/p1/dogs/1235. Each dog could either be the full representation, maybe even with a redirect to /dogs/1234 etc, or a different representation of the dog in the context of this pack, but again with a link to the "full" dog. Depends on how you want to represent a single dog in a pack, and that will of course also influence what you actually POST to /packs/p1/dogs/. Full dog feels wrong, should really be just an ID as I showed above, maybe with additional data pertaining to the relationship with the pack.



回答2:

PUT's effect is 'replace'. So, yes, you need to supply the complete representation with a PUT request.

If you want to do update only parts of the resource you have the following choices:

  • Define a resource that represents the part and PUT there
  • Use POST to a sub resource and have the server return 303 See Other with a Location header pointing to the main resource
  • Use PATCH

In your case, it would likely be best to make the dogs a sub resource (the collection of dogs of the pack) and POST to that in the usual POST-as-append manner