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
Related
I have the following resources in my system 1. Services 2. Features where a feature has the following JSON structure,
{
id: "featureName",
state: "active",
allowList: [serviceID1, serviceID2],
denyList: [serviceID3, serviceID4]
}
I am trying to update the allowList or denyList which consists of serviceIDs and thinking of using PATCH method to do it like below,
/features/{featureId}/allowlist
/features/{featureId}/denylist
/features/{featureName}/state/{state}
My first question is should I even include allowlist, state, denylist in the url as my resources are services and features, not the allowlist or denylist.
How should the rest endpoint look like?
After reading thread mentioned below I was thinking about restructuring urls as below,
/features/{featureId}
[
{ "op": "add", "path": "/allowList", "value": [ "serviceA", "serviceB"]},
{ "op": "update", "path": "/state", "value": false}
]
Lastly, the use of PATCH even justified here? or there is any better way to design the api.
Note: I have got some help from the thread REST design for update/add/delete item from a list of subresources but have not used patch often.
How should the rest endpoint look like?
The URI that you use to edit (PUT, PATCH) a resource should look the same as the URI that you use to read (GET) the resource. The motivation for this design is cache-invalidation; your successful writes automatically invalidate previously cached reads of the same resource (same URI).
Lastly, the use of PATCH even justified here? or there is any better way to design the api.
In this example, the representation of the document is small compared to the HTTP headers, and the size of your patch document is close to the size of the resource representation. If that's the typical case, I'd be inclined to use PUT rather than PATCH. PUT has idempotent semantics, which general purpose components can take advantage of (for example, automatically resending requests when the response to an earlier request has been lost on the network).
GET /features/1 by user1
PUT /features/1 //done by user 2
PUT /features/1 //done by user1
the PUT by user2 will not be visible for user1 and user1 will make an update on the old object's state (with id=1) what can be done in this situation?
Conditional Requests.
You arrange things such that (a) the GET request from the server includes validators that identify the representation (b) the server responds 428 Precondition Required when the request lacks conditional headers (c) the clients know to read the validators from the resource metadata, and use the correct condition headers when submitting the PUT request (d) the server knows to compare the validator to the current representation before accepting the new representation.
We have a web application (AngularJS and Web API) which has quite a simple functionality - displays a list of jobs and allows users to select and cancel selected jobs.
We are trying to follow RESTful approach with our API, but that's where it gets confusing.
Getting jobs is easy - simple GET: /jobs
How shall we cancel the selected jobs? Bearing in mind that this is the only operation on jobs we need to implement. The easiest and most logical approach (to me) is to send the list of selected jobs IDs to the API (server) and do necessary procedures. But that's not RESTful way.
If we are to do it following RESTful approach it seams that we need to send PATCH request to jobs, with json similar to this:
PATCH: /jobs
[
{
"op": "replace",
"path": "/jobs/123",
"status": "cancelled"
},
{
"op": "replace",
"path": "/jobs/321",
"status": "cancelled"
},
]
That will require generating this json on client, then mapping it to some the model on server, parsing "path" property to get the job ID and then do actual cancellation. This seems very convoluted and artificial to me.
What is the general advice on this kind of operation? I'm curious what people do in real life when a lot of operations can't be simply mapped to RESTful resource paradigm.
Thanks!
If by cancelling a job you mean deleting it then you could use the DELETE verb:
DELETE /jobs?ids=123,321,...
If by cancelling a job you mean setting some status field to cancelled then you could use the PATCH verb:
PATCH /jobs
Content-Type: application/json
[ { "id": 123, "status": "cancelled" }, { "id": 321, "status": "cancelled" } ]
POST for Business Process
POST is often an overlooked solution in this situation. Treating resources as nouns is a useful and common practice in REST, and as such, POST is often mapped to the "CREATE" operation from CRUD semantics - however the HTTP Spec for POST mandates no such thing:
The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics. For example, POST is used for the following functions (among others):
Providing a block of data, such as the fields entered into an HTML form, to a data-handling process;
Posting a message to a bulletin board, newsgroup, mailing list, blog, or similar group of articles;
Creating a new resource that has yet to be identified by the origin server; and
Appending data to a resource's existing representation(s).
In your case, you could use:
POST /jobs/123/cancel
and consider it an example of the first option - providing a block of data to a data handling process - and is analogous to html forms using POST to submit the form.
With this technique, you could return the job representation in the body and/or return a 303 See Other status code with the Location set to /jobs/123
Some people complain that this looks 'too RPC' - but there is nothing that is not RESTful about it if you read the spec - and personally I find it much clearer than trying to find an arbitrary mapping from CRUD operations to real business processes.
Ideally, if you are concerned with following the REST spec, the URI for the cancel operation should be provided to the client via a hypermedia link in your job representation. e.g. if you were using HAL, you'd have:
GET /jobs/123
{
"id": 123,
"name": "some job name",
"_links" : {
"cancel" : {
"href" : "/jobs/123/cancel"
},
"self" : {
"href" : "/jobs/123"
}
}
}
The client could then obtain the href of the "cancel" rel link, and POST to it to effect the cancellation.
Treat Processes as Resources
Another option is, depending on if it makes sense in your domain, to make a 'cancellation' a noun and associate data with it, such as who cancelled it, when it was cancelled etc. - this is especially useful if a job may be cancelled, reopened and cancelled again, as the history of changes could be useful business data, or if the act of cancelling is an asynchronous process that requires tracking the state of the cancellation request over time. With this approach, you could use:
POST /jobs/123/cancellations
which would "create" a job cancellation - you could then have operations like:
GET /jobs/123/cancellations/1
to return the data associated with the cancellation, e.g.
{
"cancelledBy": "Joe Smith",
"requestedAt": "2016-09-01T12:43:22Z",
"status": "in process"
"completedAt": null
}
and:
GET /jobs/123/cancellations
to return a collection of cancellations that have been applied to the job and their current status.
Example 1: Let’s compare it with a real-world example: You go to a restaurant you sit at your table and you choose that you need ABC. You will have your waiter coming up and taking a note of what you want. You tell him that you want ABC. So, you are requesting ABC, the waiter responds back with ABC he gets in the kitchen and serves you the food. In this case, who is your interface in between you and the kitchen is your waiter. It’s his responsibility to carry the request from you to the kitchen, make sure it’s getting done, and you know once it is ready he gets back to you as a response.
Example 2: Another important example that we can relate is travel booking systems. For instance, take Kayak the biggest online site for booking tickets. You enter your destination, once you select dates and click on search, what you get back are the results from different airlines. How is Kayak communicating with all these airlines? There must be some ways that these airlines are actually exposing some level of information to Kayak. That’s all the talking, it’s through API’s
Example 3: Now open UBER and see. Once the site is loaded, it gives you an ability to log in or continue with Facebook and Google. In this case, Google and Facebook are also exposing some level of users’ information. There is an agreement between UBER and Google/Facebook that has already happened. That’s the reason it is letting you sign up with Google/ Facebook.
PUT /jobs{/ids}/status "cancelled"
so for example
PUT /jobs/123,321/status "cancelled"
if you want to cancel multiple jobs. Be aware, that the job id must not contain the comma character.
https://www.rfc-editor.org/rfc/rfc6570#page-25
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.
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).
I have a webservice that we intend to use to approve transactions.
This is a batch process but we feel PUT is the most appropriate method but believe its should normally be reserved for an individual transaction.
SHOULD we persist with PUT for this process and pass payload similar to:
{
"Transactions": [
{
"TxId": "gtx-32",
"VendorUserId" "76",
"Status": "A"
},
{
"TxId": "gtx-76",
"VendorUserId" "76",
"Status": "D"
}
]
}
the above would set TxId: gtx-32 to status A and TxId: gtx-76 to status D
we would then reply with json object(s) of those transactions with the updated status.
if any one transaction was requesting an update to an invalid status the response would simply have the 'old' status against the transaction (should we include an error message there?).
If PUT is not the right method to adopt here we would welcome suggestions to alternatives.
We are aware that this is really only a partial update of the record so not a true PUT however this is most definitely an update...
The PUT verb is intended to mean "put this document here". Importantly, the URL of a PUT request is intended to be the location at which the document should be "stored", i.e. a GET on the URL would return the document last put there.
In your case, unless you're "uploading" your batch of information to a specific location and can think of your batch as a document of itself, PUT is probably not what you mean.
Instead, you should probably use the more generic POST, intended to be used to ask an existing resource to process the document contained in the request. The response then expresses the result of the processing action.
POST is probably the most generic verb in the HTTP protocol. "Process" can be anything you need it to be, so it's generally the best to choose when you don't find any of the other verbs reflect the activity you want to perform.