+1 to coming up with a new solution.
Here's another alternative to compare to Louis' JSON-RPC proposal: RESTful JSON Batching. In practice I think both proposals are fairly close to each other (but I'd like to get some implementors to verify this). They could be made closer and I think Louis has some ideas in that direction.
Summary of differences from the 0.8 MIME multipart batching:o Doesn't require MIME, only POSTing a JSON list and reading a resulting JSON list
o Doesn't support non-JSON requests or mixed requests (each data format needs a separate proposal; Atom would be a separate proposal with a lot of copy and pasting from this one)
Summary of differences from JSON-RPC proposal:o RESTful vs. RPC orientation (for this particular operation the differences are not large)
o Fewer conceptual differences from doing individual HTTP operations; translation is largely 1:1 through a few generic mechanisms, with a standard way to handle new extensions the "same way" in each
o A few non-essential differences that could easily be unified if it seems worthwhile to the community:
o Keeps URLs to identify resources; these too could be translated into JSON structures if the added spec complexity is warranted, though I'd like to make it a generic 1:1 translation if possible
o Doesn't do signing of request bodies: I'm not convinced this is necessary given the use cases, but if I'm wrong I'll do it the same way as in the JSON-RPC proposal
o Makes IDs for individual requests OPTIONAL, under the theory that they're a bit like forcing people to supply line numbers in BASIC.
Enjoy!
-John
Proposal: RESTful Operation Batching (Rough Draft)
1. Overview
This proposal defines an alternative operation batching mechanism, to replace section 9 of the
0.8 RESTful API specification.
Motivation:
Batching of operations can be critical to reducing latency and
increasing throughput of network protocols. For many common read-only
requests, collections and pagination address this need. However, for
mixed requests dealing with varied operation or resource types, it's
natural to simply collect the desired operations into a batch
operation. This proposal offers a RESTful alternative to "Proposal for
an OpenSocial JSON-RPC API" which attempts to address most of the same
issues with less change from the existing OpenSocial 0.8 RESTful
specification.
Concepts: Clients create
batch operations
when they want to ask a server to orchestrate a set of individual
operations. A batch operation is essentially an ordered list of
individual operations. An individual operation corresponds 1:1 to a
RESTful operation on a resource as described in other sections. The
batch operation is itself a resource which can be represented in JSON.
POSTing the batch operation to a server tells the server to process the
individual operations in order. The server returns the resulting state
of the batch operation resource after the requests are processed,
providing the client with results and/or status on each.
Note:
An XML+Atom variant of this scheme would reflect 1:1 the JSON structure
in this document; this proposal leaves the Atom variant for another
proposal, but claims, without substantiation, that it would be
straightforward once the details of the basic structure are worked out.
2. Examples
2.1 Basic write/read example
This
toy example assumes that a contact record's nickname can be updated in
the
example.org social network, and that after updating the record, the
client wants to retrieve all friends.
Request:POST /batchOps HTTP/1.1
Authorization: OAuth realm="http://example.org/", oauth_consumer_key="dpf43f3p2l4k3l03",
oauth_token="nnch734d00sl2jdk", oauth_signature_method="HMAC-SHA1",
oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D", oauth_timestamp="1191242096",
oauth_nonce="kllo9940pd9333jh", oauth_version="1.0"
Content-Type: application/json
[
{
"method" : "PUT",
"url" : "/people/@me/@all/
example.org:3838923?fields=nickName",
"id" : "setContactNickName",
"body" :
{
"nickName" : "Flounder"
},
"If-Match" : "A35II835334339"
},
{
"method" : "GET",
"url" : "/people/@me/@friends"
},
]
Notes:- Each operation consists of an envelope which mirrors the relevant headers of the equivalent HTTP request, and a body:
- "method" and "url" denote the HTTP method and (relative) URL for the individual request.
- "id" is optional and is used by the client to describe the operation for later matching.
- "body" is required for POST and PUT methods and contains the data that would have been in the HTTP body.
- Other
HTTP headers MAY be provided (e.g., "If-Match") with the same semantics
as the HTTP counterparts. Clients and servers MAY ignore any such
headers, and MUST ignore any headers they do not understand. Unlike
the HTTP protocol, the header names are case sensitive and MUST use the
case specified in RFC2616 (HTTP 1.1). Note that they may include
characters which are not legal JavaScript identifier characters, and
thus may need special syntax to be referenced (op["WWW-Authenticate"] vs. op.WWW-Authenticate, for example).
- The
Authorization: header above uses standard, core OAuth to authorize all
operations as being executed on behalf of a single user (specified via
the oauth_token). Note that the OAuth signature verifies only the
Authorization: header and the /batchOps URL; the payload is not
verified by a signature. If this is a security issue, TLS (https) or
other solutions SHOULD be used for message integrity. (See below for
an example of a multi-user operation.)
Response:HTTP/1.x 200 OK
Content-Type: application/json
[
{
"code" : 200,
"etag" : "B383999",
"id" : "setContactNickName"
},
{
"code" : 200,
"ETag" : "XYZ3737",
"body" : {
"totalResults" : 100,
"startIndex" : 0,
"itemsPerPage" : 10,
"entry" : [
{..},
{..},
]
}
}
]
Notes:
- The response code for the batch MUST be
200 if the body of the response correctly reflects the final state of
the batch operations (every sub-operation may have failed, for example,
but the mechanical execution of the batch itself succeeded). If the
batch execution itself failed or was interrupted, the server SHOULD
respond with a 5xx error code. If the envelope of the batch was
syntactically incorrect, e.g., not a JSON list, the server SHOULD
respond with a 4xx error code.
- Each sub-operation is executed "as if" it were sent separately in the given sequence.
- Servers MUST preserve any "id" fields.
-
The
sub-operation's result is reflected in the new envelope, which mirrors
the relevant headers of the equivalent HTTP response, and possibly a
body:
- "code" is the response code from the server and is REQUIRED.
- "ETag" mirrors the HTTP ETag header and is OPTIONAL.
-
"body" is in general optional and provides the data that would have been in the individual HTTP response body.
2.2 Batch Updates on Behalf of Multiple Users
Below
is an example of a server-to-server interaction in which the requester
is acting on behalf of many users, updating their locations as new data
comes in. In this particular example, it uses stored OAuth tokens to
allow it to update the users' locations.
Request:POST /batchOps HTTP/1.1
Content-Type: application/json
[
{
"method" : "PUT",
"url" : "/people/@me/@self?fields=currentLocation",
"Authorization:OAuth" : {
"realm" : "
http://example.org/",
"oauth_consumer_key" : "dpf43f3p2l4k3l03",
"oauth_token" : "iopq578d01el5iuy",
"oauth_signature_method" : "HMAC-SHA1",
"oauth_signature" : "wX3%2BZo71lMeYAr%2FFid0kMTYa%2FWM%3D",
"oauth_timestamp" : "1191242096",
"oauth_nonce" : "kllo9940pd9333jh",
"oauth_version" : "1.0"
},
"body" :
{
"currentLocation" : {...some new location for user X here...}
},
},
{
"method" : "PUT",
"url" : "/people/@me/@self?fields=currentLocation",
"Authorization:OAuth" : {
"realm" : "
http://example.org/",
"oauth_consumer_key" : "dpf43f3p2l4k3l03",
"oauth_token" : "rznh874d67el5ooi",
"oauth_signature_method" : "HMAC-SHA1",
"oauth_signature" : "iO3%3FBZo98lZeREruEid0kMTYaqZI%2B",
"oauth_timestamp" : "1191242096",
"oauth_nonce" : "kllo9940pd9333jh",
"oauth_version" : "1.0"
},
"body" :
{
"currentLocation" : {...some new location for user Y here...}
},
}
]
Notes:- This authorizes each sub-request separately.
- The
Authorization: header has a special mapping to the sub-operation
format: It becomes a multi-part key "Authorization:OAuth" and an object
with the remaining authorization parameters translated 1:1 to JSON
notation.
- The signature for each sub-operation is the
same as the signature that would have been generated for the equivalent
HTTP operation (combining the Authorization fields, the url path and
parameters, and the consumer secrets as described in the OAuth 1.0
specification).
- A top-level
Authorization: HTTP header, if present, is used to authorize any
sub-operation which lack authorization (note that this omits the
signature on the URL parameters of the individual sub-operation).
- Aside:
This proposal does not include signing of bodies; it assumes that this
is either impossible (no secret keys) or unnecessary (secure connection
already established) in 80% of cases. If this assumption is wrong,
then it would adopt the JSON-RPC proposal's signing.
- I
disclaim all responsibility for any escaping errors in the strings
above. If it works conceptually I'll fix them. Similarly the OAuth
values above for signature, etc. are completely made up.
Response:HTTP/1.x 200 OK
Content-Type: application/json
[
{
"code" : 200,
"ETag" : "C8933duid"
},
{
"code" : 401,
"WWW-Authenticate:OAuth" : "..."
"body" {
"message" : "User token expired."
}
}
]
Notes:
-
In the example above, the first operation succeeded and the second failed because oauth_token supplied has expired.
- The WWW-Authenticate:OAuth element contains additional machine readable information.
- In general, errors can contain bodies with more details (see below.)
Note:
Can extend this to provide examples for Activity, Appdata, etc. but it
will basically be "more of the same" -- would be good to get feedback
on above before proceeding further.
3. Standard Batch-Level Errors
Servers SHOULD use these error codes to indicate the corresponding batch-specific error conditions.
Code
| Meaning
|
400 Parse error/invalid request
| Invalid JSON or invalid batch syntax. An error occurred on the
server while parsing the JSON body. The request SHOULD NOT be repeated without modification per HTTP 1.1.
|
500 Internal server error
|
Internal server error; batch may or may not have been partially executed.
|
503 Server unavailable
| Temporary
server error; batch may or may not have been partially executed; if
retryable, a Retry-After: header SHOULD be supplied.
|
501 Not implemented
| Server does not support a batching extension which the client requests.
|
401 Unauthorized
| The
consumer key, IP address, or (less likely) oauth token do not provide
authorization to even attempt to execute batch operations.
|
404 Not Found
| On
the /batchOps URL, implies that the server does not support batching
(but servers MUST support batching, so this is a "should not happen"
case).
|
303 See Other
| Indicates
that the final result of the batch can be found at another location,
specified in the Location: header, and retrieved via GET. (This
result is possibly cacheable, whereas the POST is not; thus it is
likely to be usable only for a batch of idempotent operations.)
|
4. Standard Operation-Level Errors
These
are supplied in the "code" field of an executed operation, and are the
same as the corresponding HTTP response. In particular, clients SHOULD
be aware of and properly process 200 OK, 201 Created, 202 Accepted, 204
No Content, 304 Not Modified, 400 Bad Request, 401 Unauthorized, 409
Conflict, and the 5xx error range.
For
errors, the error fields as specified in the JSON-RPC proposal are
hereby incorporated by reference (TBD: Copy these in and make this
explicit).