JSON-RPC 1.1 Alt

suggestion for a simple JSON-RPC 1.1

suggestion for a simple JSON-RPC 1.1

Date:2007-05-06
Web site:http://89.106.94.126/jsonrpc/
Author:Roland Koebler (r.koebler at yahoo dot de)

Table of Contents

1   Introduction

"JSON-RPC is a lightweight remote procedure call protocol. It's designed to be simple!" [http://json-rpc.org/wiki/specification]

That's good.

But unfortunately, some useful things are missing in JSON-RPC 1.0, especially named parameters and maybe some definitions about error-messages. The JSON-RPC 1.1 Working Draft on the other side somehow overshoots the mark and makes things much more complicated. This already led to several discussions on the JSON-RPC Yahoo! Group.

The goal of this document is to propose a JSON-RPC-specification which enhances JSON-RPC 1.0, adds reasonable features from the 1.1WD, but still stays simple. This currently is only a working draft.

1.1   Thoughts about RPC

In my option, RPC consists of several independent parts:

  1. data structure (how requests/responses/errors look like)
  2. serializer (i.e. JSON, XML, URI, ...)
  3. transport (i.e. unix domain socket, tcp/ip, http)
  4. proxy/dispatcher

Unfortunately, these parts are often not treated as independent, which results in unnecessary complex results [1]. A RPC-specification should only define point 1 ("data structure"), and tell the user which serialization to use [2].

[1](Have you ever tried to run i.e. XML-RPC over unix domain sockets? this does not work, because XML-RPC always uses http, although this would not be necessary.)
[2]Although requiring a specific serialization would not be absolutely necessary: It would also be possible to serialize XML-RPC-data-structures in JSON, or JSON-RPC-data-structures in XML. But I don't think that things like this are really used.

1.2   Structure of this Document

The following sections mostly use the same headlines (and often same numbers) as the 1.1WD, if possible.

1.3   TODO

  • should a note on an optimal "Member Sequence" and the possible optimization be added?
  • should something like class-hinting (like in JSON-RPC 1.0), or references (for cyclic objects etc.) be defined?
  • error-messages: timeout!
  • should Object extensions (like in 1.1WD Object Extensions) be defined?
  • ...

see also the may TODO tags all over this document.

2   Differences

2.1   Differences from Version 1

The most important differences are:

  • named parameters added (like in 1.1WD)
  • "version" added (like in 1.1WD)
  • "id" now optional (like in 1.1WD)
  • "kwparams" added
  • notifications removed (like in 1.1WD)
  • added error object
  • added system services

2.2   Differences from 1.1WD

The most important differences are:

  • Transport:

    1.1WD requires HTTP.

    This specification does not rely on any specific transport, so you can use i.e. unix domain sockets, tcp/ip, http, or even letters or avian carriers. This removes much of the complexity of the 1.1WD.

  • HTTP:

    Nevertheless, if you want to use http, an appendix recommends how to use JSON-RPC over HTTP: simply tunnel JSON-RPC through HTTP.

    HTTP then doesn't need to know anything about JSON-RPC. And I don't think that it's reasonable to i.e. convert JSON-RPC-errors to HTTP errors.

    HTTP GET is discouraged. And if you really want to use HTTP GET, you should use a small wrapper, which converts HTTP GET calls to json-rpc.

    The "User-Agent" header is not explicitly required anymore, because it does not provide any information for JSON-RPC.

  • Named and Positional Parameters, Null parameters:

    1.1WD allows mixed named and positional parameters, which requires much more complicated servers and clients and adds some ambiguity. It also says that "Null ... as the value of any parameter ... MUST be taken to mean that the parameter is not being supplied for the call".

    This specification allows either named or positional parameters, and it also optionally allows but discourages the use of both in one call. It also uses a separate objects for named and positional parameters.

    "Null" is no special value, and a parameter-value of "Null" simply means that the parameter should be "Null".

  • Member Sequence:

    1.1WD: the members of the JSON-RPC-object SHOULD be in a specific order, and allows the server to refuse requests which do not stick to this order.

    This is removed in this specification.

  • Procedure Call Parity, Call Approximation:

    Removed in this specification. A Service must be called exactly as specified.

    (Things like "Call Approximation" and other magic are considered harmful, and may result in unexpected results. It's much better to require the client to say exactly what he wants than having a server which guesses.)

  • Error Object:

    The "name" member was removed, because it does not provide any information. The error-code is no longer limited to 100-999, so that i.e. the xmlrpc-error-codes can be used.

  • Services, Service Description, Parameter and Return Type Strings:

    service.description is now optional. Additional optional system-services were added (mainly those from xmlrpc).

    The "Parameter and Return Type Strings" now SHOULD be used (instead of "MAY"), and it is not allowed to supply the Null value for all parameters.

  • Service, Procedure and Parameter Names:

    Added underscore ("_") to, and removed hypen ("-") from the recommended characters. Additionally, added period (".") for methods, and everything is defined as case-sensitive.

  • Object Extensions:

    This was removed in this specification.

  • Multicall:

    This was added in this specification.

3   Requirements

see 1.1WD, Requirements.

4   Terminology

see 1.1WD, Terminology.

5   Type system

see 1.1WD, Type System.
(or http://www.json.org)

6   Procedure Call (Request)

A remote procedure call is made by sending a request to a remote service. The request is expressed as a JSON Object, with the following members:

version
REQUIRED. A String specifying the version of the JSON-RPC protocol to which the client conforms. An implementation conforming to this specification MUST use the exact String value of "1.1" for this member.
method

REQUIRED. A String containing the name of the procedure to be invoked.

Procedure names that begin with the word "system" followed by a period character (U+002E or ASCII 46) are reserved. In other words, a procedure named "system.foobar" is considered to have reserved semantics.

params
OPTIONAL. An Array that holds the positional parameter values for the invocation of the procedure.
kwparams
OPTIONAL. An Object that holds the named parameter values for the invocation of the procedure.
id
OPTIOINAL. A request identifier that can be of any JSON type. This member can be used to correlate a response with its request. If this member is present then the server MUST repeat it verbatim on the response.

Unless a call is for notification purposes only, bearing one-way semantics, it MUST be replied to with a response.

6.1   Named and Positional Parameters

Parameters for a procedure call can be identified by their name or position. The name and position of a parameter is defined by the formal argument list of the target procedure.

A client can specify parameters:

  • by-position: "params" contains the parameters in the right order, "kwparams" is omitted. This is like JSON-RPC 1.0.

    Every JSON-RPC implementation MUST support this.

  • by-name: "kwparams" contains the parameter-names and its values, "params" is omitted. The names MUST match exactly (including in case) the names defined by the formal arguments.

    Every JSON-RPC implementation SHOULD support this.

  • mixed: The usage of both "params" and "kwparams" in one call is discouraged. Nevertheless, a JSON-RPC implementation MAY support this. Then, if a parameter is both given as named and positional parameter, an error MUST be returned.

    If the implementation does not support both in one call, it MUST return an error if called with both.

TODO:specify which errors should be returned

6.1.1   Choosing between Named and Positional Parameters

Calls that identify the parameters by-name are RECOMMENDED over positional parameters in most cases, for the following reasons:

  1. The call appears more readable and self-contained on the wire, which can aid in logging and diagnosis without having to constantly consult the procudure definitions.
  2. The client gains independence from any change in the order of the formal argument list.
  3. If the formal argument list is large and mostly optional, the client can send only those parameters that it needs to.

In small or simple applications however, positional parameters may sometimes be more appropriate.

6.2   Call examples


--> data sent to service
<-- data coming from service

Suppose a remote procedure called sum that defines three formal arguments called a, b and c. Suppose further that a comes first in the argument list, b second and c third, as in "sum(a,b,c)". The following examples show how this procedure can be called.

6.2.1   Procedure Call with Named Parameters


--> { "version" : "1.1", "method" : "sum", "kwparams" : { "a" : 12, "b" : 34, "c" : 56 } }

Since parameters are identified by their name, the order is insignificant. The same MAY be expressed with parameters appearing in a different order than which is defined by the formal argument list:


--> {
"version" : "1.1",
"method" : "sum",
"kwparams" : { "b" : 34, "c" : 56, "a" : 12 }
}

6.2.2   Procedure Call with Positional Parameters

If a client chooses to send parameters by their position, then it must use the params member of the procedure call object:


--> {"version" : "1.1", "method" : "sum", "params" : [ 12, 34, 56 ] }

6.2.3   Procedure Call With Mixed Parameters

Although this is discouraged, here is an example:


--> { "version": "1.1", "method": "sum", "params": [12,34], "kwparams":{"c": 56} }

7   Procedure Return (Response)

When a remote procedure call is made, the service MUST reply with a response whether the invocation was successful or not.

TODO:how about notifications ?

The response MUST be a JSON Object that carries the following properties or members:

version

REQUIRED. A String specifying the version of the JSON-RPC protocol to which the client conforms. An implementation conforming to this specification MUST use the exact String value of "1.1" for this member.

The absence of this member can effectively be taken to mean that the remote server implements version 1.0 of the JSON-RPC protocol.

result
REQUIRED on success. The value that was returned by the procedure upon a successful invocation. Its contents is entirely defined by the procedure. This member MUST be entirely omitted in if there was an error invoking the procedure.
error
REQUIRED on error. An Object containing error information about the fault that occured before, during or after the call. This member MUST be entirely omitted if there was no error.
id
OPTIONAL. If the id member was present on the request, then the server MUST repeat it verbatim on the response. This member can be used to correlate a response with its request.

Exactly one of "result" or "error" MUST be specified. It's not allowed to specify both or none.

7.1   Error Object

When a remote procedure call fails, the Procedure Return object MUST contain the error member whose value is a JSON Object with the following members:

code
REQUIRED. A Number value that indicates the actual error that occurred. This MUST be an integer.
message
REQUIRED. A String value that provides a short description of the error. The message SHOULD be limited to a concise single sentence.
error
OPTIONAL. A JSON Null, Number, String or Object value that carries custom and application-specific error information. Error objects MAY be nested using this property.

The following table lists the error codes defined by this specification:

codemessageMeaning
Server errorGeneral error on the server, prior to procedure invocation.
Parse errorInvalid JSON / An error occured on the server while parsing the JSON text.
Bad callThe procedure call is not valid.
Service errorThe call is valid, but a general error occurred during the procedure invocation.
Procedure not foundThe call is valid but the procedure identified by the call could not be located on the service.
TODO:specify error-codes; maybe use the same error-codes as XML-RPC?

7.2   Response Examples

Suppose the example from Call Examples. The response to all these calls on success would be:


<-- { "version" : "1.1", "result" : 102 }

And here's a possible response on error:


<-- {
"version" : "1.1",
"error" : {
"code" : 123,
"message" : "An error occurred parsing the request object.",
"error" : {
"message" : "Bad array",
"at" : 42,
"text" : "{\"id\":1,\"method\":\"sum\",\"params\":[1,2,3,4,5}"

}
}
}
TODO:use the defined errors/error-codes as soon as they are defined.

8   System Services

Procedure names that begin with "system." are reserved (see Procedure Call (Request)).

A server MAY implement the following system-services. It SHOULD at least implement "system.listMethods" and "system.methodHelp".

system.listMethods()
This method returns a list of strings, one for each (non-system) method supported by the RPC server.
system.methodSignature(name)

This method takes one parameter, the name of a method implemented by the XML-RPC server. It returns an array of possible signatures for this method. A signature is an array of types. The first of these types is the return type of the method, the rest are parameters.

Because multiple signatures (i.e. overloading) is permitted, this method returns a list of signatures rather than a singleton.

Signatures themselves are restricted to the top level parameters expected by a method. For instance if a method expects one Array of Objects as a parameter, and it returns a String, its signature is simply "str, arr". If it expects three Numbers and returns a String, its signature is "str, num, num, num".

If no signature is defined for the method, Null is returned.

system.methodHelp(name)

This method takes one parameter, the name of a method implemented by the RPC server. It returns a documentation string describing the use of that method. If no such string is available, an empty string is returned.

The documentation string may contain markup.

system.echo(data)
This method takes one parameter of any type, and returns it as "result". It serves as a simple test-function.
system.describe()
This method is the same as in 1.1WD, Services. Although it is a large and complex method, it is listed here for compatibility with 1.1WD.
system.multicall()
see Multicall
TODO:Maybe it would be reasonable to define a simplified version of "describe".
TODO:describe/define these services in detail.

The 3 services "listMethods", "methodSignature" and "methodHelp" are nearly the same as in XML-RPC (see http://xml-rpc.org or http://docs.python.org/lib/serverproxy-objects.html).

8.1   Parameter and Return Type Strings

The type member of the Procedure Parameter Description SHOULD be one of the following String values:

TODO:SHOULD or MUST?
"bit"
Boolean
"num"
Number
"str"
String
"arr"
Array
"obj"
Object
"any"
Boolean, Number, String, Array or Object
"nil"
None (valid only for procedure return type)

If another String value is found then it MUST be treated the same as "any".

The "nil" string MUST NOT be used to describe the type of a procedure's formal argument. Rather, it is strictly reserved to denote the return type of a procedure that is not expected to produce a result. In other words, the result member of the Procedure Return object resulting from a call to such procedure is not interesting because it will always be the Null value.

9   Service, Procedure and Parameter Names

In JSON-RPC, service, procedure, parameters identifiable by name. The names of all these artifacts SHOULD only include:

All other characters in a name, although not technically excluded here, could severely limit the reach of the service and its procedures given certain environments, scenarios and especially programming languages.

All names etc. are case-sensitive. Conforming implementations therefore MUST treat all names as being case-sensitive such the names "bar" and "BAR" would be seen as two distinct entities.

10   Extensions

All extensions are OPTIONAL.

10.1   Multicall

There is a special method called "system.multicall", like in XMLRPC. This method takes the single calls as positional parameters (in "params"). The result is a list of the single-call results.

Call Example:


--> { "version":"1.1", "method":"system.multicall", "params":
[{ "version":"1.1", "method":"sum", "params":{"a":1,"b":1} },
{ "version":"1.1", "method":"sum", "params":{"a":2,"b":2} },
{ "version":"1.1", "method":"sum", "params":{"a":3,"b":3} }]
}

Return Example on success:


<-- { "version":"1.1", "result":
[{ "version":"1.1", "result":2 },
{ "version":"1.1", "result":4 },
{ "version":"1.1", "result":6 }]
}

Return Example with some errors:


<-- {"version":"1.1", "result":
[{"version":"1.1", "error":{"code":..., "message":"..."}},
{"version":"1.1", "result":4},
{"version":"1.1", "error":{"code":..., "message":"..."}}]
}

Return Example if the multicall itself fails:


<-- {"version":"1.1", "error":{"code":..., "message":"Parse error"}}

11   APPENDIX A: Transports

JSON-RPC does not depend on any specific transport (see also Differences From 1.1WD). Any transport should be possible.

Nevertheless, here are some recommendations how to use JSON-RPC over some commonly-used transports.

11.1   Unix Domain Sockets

This is straight-forward: open a socket and send/receive the data.

11.2   TCP/IP Sockets

This is straight-forward: open a socket and send/receive the data.

11.3   HTTP

If you want to use HTTP, simply tunnel JSON-RPC through HTTP with Content-Type "application/json".

TODO:should "application/json" be changed to "application/jsonrpc"?

11.3.1   HTTP POST Procedure Call

The HTTP request message MUST specify the following headers:

  • Content-Type MUST be specified and SHOULD read "application/json".
  • Content-Length MUST be specified and correct according to the HTTP specification.
  • Accept MUST be specified and SHOULD read "application/json".

The Request itself is carried in the body of the HTTP message.

Example:


POST /myservice HTTP/1.1
User-Agent: Wget/1.6
Host: www.example.com
Content-Type: application/json
Content-Length: ...
Accept: application/json

{
"version" : "1.1",
"method" : "sum",
"kwparams" : { "b" : 34, "c" : 56, "a" : 12 }
}

11.3.2   HTTP GET Procedure Call

The use of JSON-RPC directly over HTTP GET is NOT RECOMMENDED, since this would mean to encode the JSON-string of the Request in the URL, which would be ugly, cause several problems, and is not what most people would expect when thinking of HTTP GET.

But if you need to call JSON-RPC-services via HTTP GET, use a wrapper which accepts a HTTP GET request, decides if a JSON-RPC-service should be called and then converts it to JSON-RPC-request-syntax. It could additionally mangle the JSON-RPC-response before returning the result (via HTTP).

But note that (according to HTTP Section 9.1, Safe and Idempotent Methods) only procedures that are considered safe and idempotent MAY be invoked using HTTP GET.

11.3.3   HTTP Procedure Return

The HTTP return message MUST specify the following headers:

  • Content-Type MUST be specified and SHOULD read "application/json".
  • Content-Length MUST be specified and correct according to the HTTP specification.

The status code MUST be 200, except for HTTP errors.

The Response (both on success and error) is carried in the HTTP body.

Example on Success:


HTTP/1.1 200 OK
Connection: close
Content-Length: ...
Content-Type: application/json
Date: Sat, 08 Jul 2006 12:04:08 GMT

{
"version" : "1.1",
"result" : 102
}

Example on Error:


HTTP/1.1 200 OK
Connection: close
Content-Length: ...
Content-Type: application/json
Date: Sat, 08 Jul 2006 12:04:08 GMT

{
"version" : "1.1",
"error" : {
"code" : 123,
"message" : "An error occurred parsing the request object.",
"error" : {
"message" : "Bad array",
"at" : 42,
"text" : "{\"id\":1,\"method\":\"sum\",\"params\":[1,2,3,4,5}"}
}
}
}

TODO:add correct Content-Length