HTTP POST response Location header when creating multiple resources
Asked Answered
M

3

16

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).

Montherlant answered 17/3, 2015 at 6:38 Comment(0)
A
7

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.

Actinia answered 20/6, 2016 at 20:24 Comment(2)
I think software should be signed to meet demand, not to meet standards. To me, this indicates a gap in the standards. I think returning the URL, without ID specification of the record(s) created makes sense. And then add the IDs as an array in de response.Agram
@MikedeKlerk — Can you elaborate on what you mean by "I think should should be "signed"...?"Actinia
N
8

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.

Newmann answered 17/3, 2015 at 6:50 Comment(3)
As you can see in my example, there is no "primary" resource - they are all peers. Not sure what to do in that case.Montherlant
And thank you for the link to the new RFC ! Will have to bookmark it for reference.Montherlant
I'll leave the question open for a bit to see if anyone has other suggestions.Montherlant
A
7

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.

Actinia answered 20/6, 2016 at 20:24 Comment(2)
I think software should be signed to meet demand, not to meet standards. To me, this indicates a gap in the standards. I think returning the URL, without ID specification of the record(s) created makes sense. And then add the IDs as an array in de response.Agram
@MikedeKlerk — Can you elaborate on what you mean by "I think should should be "signed"...?"Actinia
A
3

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:

Hope it helps you, Thierry

Aarika answered 17/3, 2015 at 10:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.