72

Suppose a REST API, in response to a HTTP GET request, returns some additional data in a sub-object owner:

{
  id: 'xyz',
  ... some other data ...
  owner: {
    name: 'Jo Bloggs',
    role: 'Programmer'
  }
}

Clearly, we don't want anyone to be able to PUT back

{
  id: 'xyz',
  ... some other data ...
  owner: {
    name: 'Jo Bloggs',
    role: 'CEO'
  }
}

and have that succeed. Indeed, we probably aren't going to even implement a way for that to even potentially succeed, in this case.

But this question is not just about sub-objects: what, in general, should be done with data that should not be modifiable in a PUT request?

Should it be required to be missing from the PUT request?

Should it be silently discarded?

Should it be checked, and if it differs from the old value of that attribute, return a HTTP error code in the response?

Or should we use RFC 6902 JSON patches instead of sending the whole JSON?

4
  • 2
    All of these would work. I guess it depends on your requirements. Commented Aug 14, 2013 at 15:28
  • I would say that principle of least surprise would indicate it should be missing from PUT request. If it is not possible then check and if it is different and return with error code. Silent discarding is worst (user sending is expecting that it will change and you telling them "200 OK"). Commented Aug 14, 2013 at 15:44
  • 2
    @MaciejPiechotka the problem with that is you don't get to use the same model on the put as on the insert or get etc, I would prefer the same model get used and there simple be field authorization rules so if they enter a value for a field they shouldn't change, they get back a 403 Forbidden, and if later on authorization is setup to allow it they get a 401 Unauthorized if they aren't authorized Commented Aug 14, 2013 at 16:10
  • @JimmyHoffa: By model you mean the data format (as it might be possible to reuse the model in MVC Rest framework depending on choice of it, if any is used - OP did not mentioned any)? I would go with discoverability if I wasn't constraint by framework and early error is slightly more discoverable/easy to implement then checking for change (ok - I should not touch field XYZ). In any case discarding is worst. Commented Aug 14, 2013 at 19:15

2 Answers 2

69

There is no rule, either in the W3C spec or the unofficial rules of REST, that says that a PUT must use the same schema/model as its corresponding GET.

It's nice if they're similar, but it's not unusual for PUT to do things slightly differently. For example, I've seen a lot of APIs that include some kind of ID in the content returned by a GET, for convenience. But with a PUT, that ID is determined exclusively by the URI and has no meaning in the content. Any ID found in the body will be silently ignored.

REST and the web in general is heavily tied to the Robustness Principle: "Be conservative in what you do [send], be liberal in what you accept." If you agree philosophically with this, then the solution is obvious: Ignore any invalid data in PUT requests. That applies to both immutable data, as in your example, and actual nonsense, e.g. unknown fields.

PATCH is potentially another option, but you shouldn't implement PATCH unless you're actually going to support partial updates. PATCH means only update the specific attributes I include in the content; it does not mean replace the entire entity but exclude some specific fields. What you're actually talking about is not really a partial update, it's a full update, idempotent and all, it's just that part of the resource is read-only.

A nice thing to do if you choose this option would be to send back a 200 (OK) with the actual updated entity in the response, so that clients can clearly see that the read-only fields were not updated.

There are certainly some people who think the other way - that it should be an error to attempt to update a read-only portion of a resource. There is some justification for this, primarily on the basis that you would definitely return an error if the entire resource was read-only and the user tried to update it. It definitely goes against the robustness principle, but you might consider it to be more "self-documenting" for users of your API.

There are two conventions for this, both of which correspond to your original ideas, but I'll expand on them. The first is to prohibit the read-only fields from appearing in the content, and return an HTTP 400 (Bad Request) if they do. APIs of this sort should also return an HTTP 400 if there are any other unrecognized/unusable fields. The second is to require the read-only fields to be identical to the current content, and return a 409 (Conflict) if the values do not match.

I really dislike the equality check with 409 because it invariably requires the client to do a GET in order to retrieve the current data before being able to do a PUT. That's just not nice and is probably going to lead to poor performance, for somebody, somewhere. I also really don't like 403 (Forbidden) for this as it implies that the entire resource is protected, not just a part of it. So my opinion is, if you absolutely must validate instead of following the robustness principle, validate all of your requests and return a 400 for any that have extra or non-writable fields.

Make sure your 400/409/whatever includes information about what the specific problem is and how to fix it.

Both of these approaches are valid, but I prefer the former one in keeping with the robustness principle. If you've ever experienced working with a large REST API, you'll appreciate the value of backward compatibility. If you ever decide to remove an existing field or make it read-only, it is a backward compatible change if the server just ignores those fields, and old clients will still work. However, if you do strict validation on the content, it is not backward compatible anymore, and old clients will cease to work. The former generally means less work for both the maintainer of an API and its clients.

4
  • 2
    Good answer, and upvoted. However, I'm not sure I agree with this: "If you ever decide to remove an existing field or make it read-only, it is a backward compatible change if the server just ignores those fields, and old clients will still work." If the client relied on this removed/newly-read-only field, wouldn't this still have an affect on the overall behavior of the app? In the case of removing fields, I'd argue it's probably better to explicitly generate an error instead of ignoring the data; otherwise, the client has no idea that its previously-working update is now failing.
    – rinogo
    Commented Aug 30, 2016 at 16:17
  • This answer is wrong. for 2 reasons from RFC2616: 1. (section 9.1.2) PUT has to be independent. Put many times and it will produce the same result as putting only once. 2. The a get to a resource should return the entity put if no other requests were made to change the resource
    – brunoais
    Commented Sep 6, 2016 at 20:32
  • 1
    What if you do the equality check only if the immutable value was sent in the request. I think this gives you the best of two worlds; you do not force clients to do a GET, and you still notify them something is wrong if they sent invalid value for an immutable. Commented Aug 1, 2017 at 10:11
  • Thanks, the in-depth comparison you did in the last paragraphs coming from experience is exactly what I was looking for.
    – dhill
    Commented Feb 14, 2019 at 10:24
13

Idem potency

Following the RFC, a PUT would have to deliver full object to the resource. The main reason of this, is that PUT should be idempotent. This means a request, which is repeated should evaluate to the same result on the server.

If you allow partial updates, it cannot be idem-potent anymore. If you have two clients. Client A and B, then the following scenario can evolve:

Client A gets a picture from resource images. This contains a description of the image, which is still valid. The client B puts a new image and update the description accordingly. The picture has changed. Client A sees, he doesn't have to change the description, because it is as he wishes and put only the image.

This will lead to an inconsistency, the image has the wrong metadata attached!

Even more annoying is that any intermediary can repeat the request. In case it decides somehow the PUT failed.

The meaning of PUT cannot be changed (although you can misuse it).

Other options

Luckily there is a another option, this is PATCH. PATCH is a method which allows you to partially update a structure. You can simply send an partial structure. For simple applications, this is fine. This method is not guaranteed to be idem potent. The client should send a request in the following form:

PATCH /file.txt HTTP/1.1
Host: www.example.com
Content-Type: application/example
If-Match: "e0023aa4e"
Content-Length: 20
{fielda: 1, fieldc: 2}

And the server can reply back with 204 (No content) to flag success. On error you cannot update a part of the structure. The PATCH method is atomic.

The disadvantage of this method is, is that not all browsers support this, but this is the most natural option in a REST-service.

Example patch request: https://www.rfc-editor.org/rfc/rfc5789#section-2.1

Json patching

The json option seems to be pretty comprehensive and an interesting option. But it can be difficult to implement for third parties. You have to decide if your user base can handle this.

It is also somewhat convoluted, because you need to build an small interpreter which converts the commands to an partial structure, which you are going to use to update your model. This interpreter should also check, if the provided commands make sense. Some commands cancel each other out. (write fielda, delete fielda). I think you want to report this back to the client to limit debug time on his/her side.

But if you have the time, this is a really elegant solution. You still should validate the fields of course. You can combine this with the PATCH method to stay into the REST model. But I think POST would be acceptable to here.

Going bad

If you decide to go with the PUT option, which is somewhat risky. Then you should at least not discard the error. The user has a certain expectancy (the data will be updated) and if you break this, you are going to give some developers not a good time.

You could chose for flagging back: 409 Conflict or a 403 Forbidden. It depends how you look at the update process. If you see it as a set of rules (system-centric), then conflict will be nicer. Something like, these fields are not updateable. (In conflict with the rules). If you see it as an authorization problem (user-centric), then you should return forbidden. With: you aren't authorized to change these fields.

You still should force users to send all the modifiable fields.

A reasonable option to enforce this is to set it to a sub-resource, which only offers the modifiable data.

Personal opinion

Personally I would go (if you don't have to work with browsers) for the simple PATCH model and then later extend it with a JSON patch processor. This can be done by differentiating on the mimetypes: The mime type of json patch:

application/json-patch

And json: application/json-patch

makes it easy to implement it in two phases.

4
  • 7
    Your idempotency example doesn't make sense. Either you change the description or you don't. Either way, you'll get the same result, every time. Commented Aug 14, 2013 at 22:51
  • 2
    You are right, I think it is time to go to bed. I cannot edit it. It is more an example about the rational of sending all data in a PUT request. Thanks for the pointer. Commented Aug 14, 2013 at 23:05
  • I know this was 3 years ago...but do you know where in the RFC I can find more information about "PUT would have to deliver full object to the resource." I've seen this mentioned elsewhere but would like to see how it is defined in the spec.
    – CSharper
    Commented Jun 2, 2016 at 16:48
  • I think I found it? tools.ietf.org/html/rfc5789#page-3
    – CSharper
    Commented Jun 2, 2016 at 17:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.