REST question: PUT one representation, GET a diffe

2019-04-08 08:42发布

问题:

Short version of the question:
Does "GET" at a particular URI need to match what was "PUT" to that URI?

I think not. Here's why:

Given that a resource is an abstract thing that is theoretically unknowable by the client, when we do a PUT, we must be only sending a representation. Based on combing over RFC2616, it doesn't seem entirely specified as to what that means for a resource that has many (potentially infinite?) representations, but here are my thoughts; please tell me if you agree:

My expectation is that if I PUT a representation to a resource, all other representations of the resource at that URI should be kept consistent (potentially updated) as necessary. In other words, you're telling the resource "use this representation to redefine yourself".

Thus, I should be able to do this:

PUT /resources/foo/myvacation
Content-type: image/jpg
...

And follow up with this:

GET /resources/foo/myvacation
Accept: image/png
...

and get the updated version of myvacation in a different format (assuming the server knows how to do that). Extrapolating from that, this composite atomic "image + metadata" PUT should also be legal:

PUT /resources/foo/myvacation
Content-type: multipart/form-data

Content-disposition: form-data; name="document"
Content-type: image/jpg
[..]
Content-disposition: form-data; name="iptc"
Content-type: application/iptc
[..]
Content-disposition: form-data; name="exif"
Content-type: application/exif
[..]

And then, because server-side content negotiation (RFC2616 section 12.1) can take place based on just about anything, we can default to the "document" content for this:

GET /resources/foo/myvacation
Content-type: image/jpg
[..]

or if you believe as I do that RFC 2396 section 3.4 "The query component is a string of information to be interpreted by the resource." means that a URI with a query string refers to the same resource as a URI without a query string (and is isomorphic with just sending application/x-form-urlencoded data to the resource), then this should also be legal:

GET /resources/foo/myvacation?content=exif
Content-type: application/exif
[..]

The description of PUT says:

The PUT method requests that the enclosed entity be stored under the supplied Request-URI.

To me, this is fairly anti-REST, unless you read it in a very liberal manner. My interpretation is "The PUT method requests that a resource be created or updated at the supplied Request-URI based on the representation of the enclosed entity."

Later on, we get:

The fundamental difference between the POST and PUT requests is reflected in the different meaning of the Request-URI. The URI in a POST request identifies the resource that will handle the enclosed entity. That resource might be a data-accepting process, a gateway to some other protocol, or a separate entity that accepts annotations. In contrast, the URI in a PUT request identifies the entity enclosed with the request -- the user agent knows what URI is intended and the server MUST NOT attempt to apply the request to some other resource.

We need to similarly read this creatively, but the key bits here are "knows what URI is intended" and "apply the request".

So, to me the representation returned by GET at a given URI does not necessarily have to be the same representation that was PUT to the given URI, it just has to be consistent.

True or false?

回答1:

Based on the fact that content negotiation can return different representations from the same URI, I am quite sure that what you PUT does not have to be the same as what you retrieve.



回答2:

Your assumptions are correct. The GET doesn't necessarily have to return the same representation as what you PUT, but it does have to be the same resource.

I'm currently working on a web application that will return any resource as XHTML, JSON, or a custom XML dialect, depending on what you ask for in the content negotiation headers. So a browser will see the HTML by default. Other HTTP clients, including XMLHttpRequest, can get the JSON, and so on. They are all representations of the same resource at the same URI.

Likewise, our application will accept a PUT or POST in any of the supported formats (subject to the semantics of the particular resource or collection in question.)



回答3:

I agree with the other answers that resource that you PUT is not required to be the same as the one that you later GET. I wanted to add some of my experience to this question around this area.

You need to be very careful when relying on content-negotiation. It is very tricky to get right and if you don't get it right leads to nasty user problems. Let's do an example based on images...

If Alice PUTs an image in a raw format, then Bob can GET the image as a jpeg(through a server side raw->jpeg transform), and Alice can GET the image in a raw format; No problems. However, if Bob PUTs a jpeg, then there is no way to get back to the raw format for Alice. In the case of vacation photos the lack of symmetric transforms may not be a big deal, but in medical images it would be.

Another area where the lack of symmetric transforms bites you is in representations where one has a schema and the other does not. In this case, on the server side you end up making conventions for how to transform between them. But you get into big problems when you are dealing with documents with schemas that change over time and are out of your control. Everytime the schema changes you have to update all of your transforms for the new schema shape, while still supporting resources using the old schema. Content negotiation quickly becomes more trouble than its worth except for a few limited circumstances. One of the areas where it can be manageable is if you fully control the resource representation and its underlying schema. Another area is if the resource formats are standards and its possible to make symmetric transformations between the different formats.



回答4:

If you are transforming then it would make sense that what you PUT is not what you GET, so I don't see why it is a problem.

But, if you PUT a user with certain information, then when you use GET then it should retrieve that person, just as, when I put my 4th vacation photo, when I call GET I expect that photo, but it may be transformed by converting to a different format, or have some other transforms, but, if I get the 5th photo instead, then that is a problem.