REST: Best practice for partial update of subset of collection - rest

I am trying to understand a little bit more about the REST ways :). Let's say I have a collection of ProductTypes that one can retrive via url GET /productTypes and let's say that we can but don't have to apply any filter. So this fetches 500 records.
Now if I want to do full update of the resource I could use PUT or POST at /productTypes/123. So far so good.
If I am creating a new resource and I know the id ahead of time, I would use PUT at /productTypes/123 and return 201 otherwise for update PUT with 200.
If I do partial update of the resource I would do PUT or POST at /productTypes/123/displayOrder and return 303 indicating that some other resource has been modified.
But what if I do 1 POST sending in basically a list of Key-Value pairs of product Id and display order, and I modify 5 out of 500 records. How do I indicate now that these 5 records have changed?
What happens with GET at /productTypes now after this partial update. I read somewhere that instead of returning multiple records with this get, I should return the list links to the resources, and then fetch them one by one, as that would allow for insane caching. But still how do I indicate that 5 records have changed? Do I need to do 5 separate posts with 303, or is there different mechanism. Hope this makes sense.

I don't see anything in the spec that specifically bars you from using multiple Content-Location headers. I tested it out and Chrome's totally OK with it. It won't automatically redirect for you, but you don't want that anyway with multiple resources involved.
HTTP/1.1 303 See Other
Content-Location: /productTypes/123/displayOrder
Content-Location: /productTypes/456/displayOrder
Content-Location: /productTypes/789/displayOrder
Content-Location: /productTypes/012/displayOrder
Content-Location: /productTypes/345/displayOrder

I'd do this:
PATCH /productTypes HTTP/1.1
Content-Type: application/json
{"item":[
{"id":"1","displayOrder":"4"},
{"id":"2","displayOrder":"2"},
{"id":"3","displayOrder":"1"},
{"id":"4","displayOrder":"3"},
{"id":"5","displayOrder":"5"}
]}
HTTP/1.1 200 OK
Content-Type: application/hal+json
{
"_links": { "self": {"href": "/productTypes" } }
"_embedded": {
"item": [{
"_links": { "self": { "href": "/productTypes/1" } },
"displayOrder": "4"
},
/* 4 more entries */
]
}
}

Related

How to update share point 2013 list item in rest service

This is my Rest service to update list item
URL: https://site/_api/lists/getbytitle('Test')/items(49)
Header
X-RequestDigest: some unique id from contextinfo post service
Content-Type: application/json;odata=verbose
X-HTTP-Method: MERGE
IF-MATCH: 49
Body
{
"__metadata": {
"type": "SP.Data.TestListItem"
},
"Title": "Title all1",
"Option1": "Updated 2"
}
When I run this URL in post man I get an error
The request ETag value __metadata.etag does not match the object's ETag value
"8".
But If I change IF-MATCH value in header as *, it's updating properly. Why I am getting an error if use ETag?
You are using the ETag incorrectly. The ETag will correspond to the item's Version not the item's ID.
The best way to test this is to do a get request for the item and look at the metadata for the ETag property. You will see something similar to this
{
"__metadata": {
"id":"Web/Lists(guid'xxxxxxxx')/Items(1)",
"uri":"https://site.sharepoint.com/site/subsite/_api/Web/Lists(guid'xxxxxx')/Items(1)",
"etag":"\"12\""
}
}
Notice the formatting of the ETag.
I can't think of any good reason to use the IF-MATCH for a specific ETag in the case of updating the item. The request parameter for the ID should be more than sufficient. Checking versioning with ETags is needed when wanting to PUT, MERGE, or DELETE a specific version of an item.
Check this Working with lists and list items with REST for more information.

HTTP POST response Location header when creating multiple resources

The HTTP/1.1 standard states that if a POST operation results in the creation of a resource, then the response should include a Location header with the address of the new resource.
If a resource has been created on the origin server, the response
SHOULD be 201 (Created) and contain an entity which describes the
status of the request and refers to the new resource, and a Location
header (see section 14.30).
and in section 14.30,
For 201 (Created) responses, the Location is that of the new resource
which was created by the request.
Now suppose that my API allows batch creation of resources by POSTing an array to the collection resource URL. For example:
POST /books
[
{
"name": "The Colour of Magic",
"published": "1983"
},
{
"name": "The Light Fantastic",
"published": "1986"
}
]
Since two \book\{bookId} resources have been created, what should be the value of the Location header in this case?
The question Http post response after multiple new resource creation? is similar, but it asks about the response entity, not the headers (and is unanswered).
RFC 2616 is obsolete. Stop looking at it except for historical purposes.
The current spec, RFC 7231, says:
"If one or more resources has been created on the origin server as a result of successfully processing a POST request, the origin server SHOULD send a 201 (Created) response containing a Location header field that provides an identifier for the primary resource created (Section 7.1.2) and a representation that describes the status of the request while referring to the new resource(s)." -- http://greenbytes.de/tech/webdav/rfc7231.html#POST
And yes, that doesn't help a lot when there isn't a "primary" resource.
I know this answer is late to the party but I believe the best solution is to create a new "Batches" resource with a uuid identifier that would return the list of Book URLs that were added using a URL like this:
http://api.example.com/batches/{uuid}
e.g.
http://api.example.com/batches/2b9b251f71a4b2901d66e04725bc0c9cb5843c74
Then your POST or PUT can return the above URL on it's Location: {url} header and a 201 - Created status code.
If you then GET that URL that resource should respond with a representation that lists the URLs created in that batch, as well as any other info about the batch such as its uuid and the time/date it was created.
{
"uuid": "2b9b251f71a4b2901d66e04725bc0c9cb5843c74",
"datetime": "2005-08-15T15:52:01+00:00",
"books": [
"http://api.example.com/books/the-colour-of-magic",
"http://api.example.com/books/the-light-fantastic"
]
}
Those resources could then have a TTL of an hour or a month, whatever you choose. Or they could live forever if you want; whatever your use-case requires.
I think that you are in a particular use case for the header Location. In the case of bulk creation, the result of the processing is generally provided within the returned content itself. As a matter of fact, the processing can be completely or partially successful. I mean all elements were added or only a subset and the result shows to the end-user what actually happens.
So I think that the header Location isn't usable in such context. I see two options for the status code:
The status code is 201 if at least one element is created)
The status code is 200 to tell that the bulk request globally succeeds but the result of each operation is described in the response content.
You can however notice that a status code 202 exists if your resource handles the bulk creations in an asynchronous way. But in the context, you need then to pull a resource to get the status of the inserts.
Regarding the content of the response, you are free to choose. We could imagine something like that:
{
"took": 4,
"errors": true | false,
"items": [
{ "added": true,
"error": null
"id": "123"
},
{ "added": false,
"error": {
"code": "err12",
"description": "validation error (field type, ...)"
}
"id": null
}
]
}
ElasticSearch provides such bulk api with create but also update and delete support - see this link for more details: http://www.elastic.co/guide/en/elasticsearch/guide/current/bulk.html.
Here are similar questions that could give some hints:
How to Update a REST Resource Collection
REST API - Bulk Create or Update in single request
Hope it helps you,
Thierry

RESTful way to accept a choice

I have a simple api, that works like this:
A user creates a request ( POST /requests)
Another user retrieves all requests ( GET /requests)
Then adds an offer to a request ( POST /requests/123/offers)
Original user can now see all the offers being made for the request (GET /requests/123/offers)
What i want to do, is allow the inital user to accept an request, but I can't figure out the best way to do it RESTfuly.
Should I do it with the PATCH verb? Like PATCH /requests/123 and require that the patch body contain a valid offer id?
Accepting an offer five times should have the same effect as accepting it once. It is idempotent. So it should be a PUT.
You might want to consider choosing a different name for your "requests." When I do GET /requests/123, I request a response that is a request? This could be a little confusing for clients.
Additionally, try to avoid nesting your resource identifiers. That can create problems for you later. An offer doesn't really have to be "underneath" the corresponding request. What happens when you later want to have offers corresponding to multiple requests?
A good rule of thumb is, if you would consider "Gizmo" an entity in an entity-relationship model, it should be a root-level URI, like in GET /gizmos/17, not GET /widgets/54/gizmos/17. A common mistake is to say "Every Gizmo has exactly one related Widget, so I should nest Gizmo URIs as extensions of Widget URIs."
Below I have a suggestion for how the operations would look. You may want to replace some of the ID references with URIs instead, but that's up to you.
POST /requests ---> 201 Created
Location: /requests/123
GET /requests ---> 200 OK
[
{
"requestId": 123,
"offersUri": "/offers?requestId=123",
...
},
...
]
POST /offers ---> 201 Created
{ Location: /offers/456
"requestId": 123,
"amount": 300,
...
}
GET /offers?requestId=123 ---> 200 OK
[
{
"requestId": 123,
"amount": 300,
...
}
]
PUT /offers/456/approval ---> 204 No Content
PUT /offers/456/approval ---> 204 No Content
PUT /offers/456/approval ---> 204 No Content
Depends on the nature of the Acceptance.
If Acceptance is a simple attribute of an offer, I would POST the Offer with the Acceptance set to True.
If the Acceptance is more complex and therefore a resource in its own right, I would PUT an Acceptance into the offer (PUT /requests/123/offers/acceptance).
If there exists such a thing as a rejection, or a request for offer clarification, I might consider the relevant resource to be a Response, not an Acceptance, and PUT that (put /requests/123/offers/response).

RESTful url for printing a resource

I have a Stock resource which is obviously an item of stock and it can be accessed by..
[Get] /stock/{stockId}
And I have a printer resource which is a network printer somewhere and it can be accessed by..
[Get] /printer/{printerId}
And I have a Template which points to a template file on the server
[Get] template/{templateId}
Now what I want to do is be able to send a request to a printer to print a certain stock item using a certain template.
My first thought would be
/printer/{printerId}/printstock?stockId=1&templateId=1
So my questions are -
When it finishes printing it will update the stock item to say it has been printed does this mean it should be a POST (or should it be a PUT?)
Are actions such as the "printstock" in the URL the correct way to go about this?
Should Stock be part of the main Url rather than in the querystring?
Model the print job as a separate resource. You create a new print job by POSTing to a collection resource that represents all print jobs.
Request:
POST /printjobs
Content-Type: application/json
{
"printer": 1234,
"stock": 1,
"template": 1
}
Response:
201 Created
Location: /printjobs/42
Then your client could GET the state of the print job.
GET /printjobs/42
200 OK
Content-Type: application/json
{
"id", 42,
"state": "PRINTING",
"printer": 1234,
"stock": 1,
"template": 1
}
After the job has finished, the sate could change to PRINTED and the state of the stock could change in a similar way.

HATEOAS - How to model link relations that change state

Following HATEOAS principles that each states should be hyperlinked, what is the best way to model links that change resource state?
Let's take classical example with orders:
{
id : 12,
state: 'pending',
...,
links: [
...,
{
rel: 'cancel',
href: '/orders/12/cancel'
},
...
]
}
I am not totall happy with that "/cancel" part - I would feel a lot better if I could send "PUT" request with contents:
{
status:'cancelled'
}
But how do I represent that with "href" attribute in links section? I would like to represent available actions there since, for example, cancelling an order isn't always possible ('completed' state).
One possibility would be to use URL like '/orders/12?action=cancel' what it kinda feels like RPC approach and that I am missing something.
Another possibility that looks probably nicest, would be to have links like that:
{
rel: 'cancel',
href: '/orders/12/',
type: 'PUT',
values: {
state: 'cancelled'
}
}
This solution maybe feels a little bit verbose.
Any ideas how to handle that gracefully? Maybe someone has already solved similar "problem"?
Modelling resources is the most difficult part of REST. Strictly adhering to the standard means if you see yourself ever doing this: /resource/:id/{action}, you're violating the "using HTTP correctly" criteria, as your endpoints should ideally always be "nouns', never "verbs" (the verbs are what the HTTP protocol provides).
So, while "it depends" (ie. the hard part of designing resources), typically:
Object model states can be considered as resources themselves.
Which means, your order status is actually a resource you can query (either as a standalone /orderstatuses resource or as a sub resource eg. /orders/:id/status)
Your Application State can now link to the status resource based on the current status of the order itself. If your 'status' schema looks something like this (pseudo):
key: 'status'
values: ['pending', 'cancelled']
Your app could then PUT /order/:id/status {status:'cancelled'} (a well formed status) back to the API, which would then act to cancel your order. It's a little weird thinking in these terms (RPC is a lot more intuitive), but hopefully this helps.
You have to describe forms somehow. You "verbose" solution is perfectly okay:
{
rel: 'cancel',
href: '/orders/12/',
type: 'PUT',
values: {
state: 'cancelled'
}
}
note: you have to define a custom MIME type or use a generic MIME type which is capable of describing forms (e.g. collection+json), or which an RDF type (which supports REST vocabs like Hydra) - aka. uniform interface / self-descriptive messages
I would like to represent available actions there since, for example,
cancelling an order isn't always possible ('completed' state).
If an operation is not available, then don't send a link pointing to that operation.
I would suggest either of these two models. The first is the classic one, but with rel="edit-form" and using PATCH where available. The second is an alternative which comes about through some lateral thinking about how the HTTP resource model maps onto your application domain model (namely, that the two don't have to have a 1:1 mapping).
Solution 1
Edit the resource in-place.
HTML compatible:
HTTP/1.1 200 OK
Content-Type: text/html
Location: /orders/1/
...<a rel="edit-form" href="./edit">Edit</a>...
 
HTTP/1.1 200 OK
Content-Type: text/html
Location: /orders/1/edit
...
<form action="../" method="POST">
<input type="hidden" name="_METHOD" value="PATCH">
<button type="submit" name="status" value="cancelled">Cancel Order</button>
</form>
...
 
POST /orders/1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
_METHOD=PATCH&status=cancelled
Rich client (e.g. HTML+Javascript) compatible:
PATCH /orders/1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
status=cancelled
and/or
PATCH /orders/1 HTTP/1.1
Content-Type: text/json
{
"status": "cancelled"
}
the _METHOD key is a well-known means of providing REST frameworks with the correct method due to HTML's lack of support for HTTP.
Solution 2
Or, Delete the resource (and, incidentally, create a new one)
DELETE /orders/1 HTTP/1.1
 
HTTP/1.1 201 Created
Location: /cancelled-orders/1
For more info on this way of mapping web resources to domain objects, please see my answer to a similar question.
Another answer you may wish to read is this one.
Further to papercowboy's answer, where status transitions are not always available you can document what is currently possible as a resource, e.g.
/order/:id/availableStates
{
"availableStates": [
{"status": "cancelled"}
]
}