Analyzing the jsonapi request and response format

Florian Wendelborn edited this page Feb 25, 2015 · 19 revisions

#Analyzing the JSONAPI request and response format This is for people interested in how the JSONAPI API request and response format works. If you are interested in a guide on how to use the API please look at the guides section on the Home page of the wiki.

##Requests All requests for JSONAPI fall into one of four categories. These categories are:

  • A standard API request
  • A multiple API request
  • A stream API request
  • An invalid request

###Standard API request A standard API request is one where a request is made for data, and the data is immediately returned. Examples of standard API methods are getPlayerLimit and getServerVersion.

A standard API request looks like this:

/api/call?method={methodName}&args={jsonEncodedArrayOfArgs}&key={key}&tag={identifier}

Every thing in curly braces in that URL is something you will need to change for each different API request. Let's analyze each one:

  • {methodName} is the the name of the API method you wish to call. There is a list of the default ones built into JSONAPI are available at this page. Some examples values are getPlayerLimit, getPlayer or dynmap.getPort.
  • {jsonEncodedArrayOfArgs} is a JSON encoded array of arguments for a particular method. Every argument listed on the method API docs is required. It is recommended to pass an empty array when there are no arguments. However, not utilizing the args GET variable is also acceptable. Do not forget to escape the JSON encoded result in accordance with RFC 3986. In PHP the rawurlencode function satisfies this. In JavaScript the escape() function seems to satisfy this. Example: ["hello","world"] will convert to %5B%22hello%22%2C%22world%22%5D.
  • {key} is a valid key for this method (see below for more information on keys).
  • {identifier} and its tag argument are optional. The tag is a string and will be included in the response and can be used to identify a response. It comes in very handy if you have multiple handlers registered for only one socket connection.

Here are some examples of valid URLs for the user alecgorge with the password MySecret and salt pepper123:

/api/call?method=reloadServer&args=%5B%5D&key=6821b5b40aa8e91487f95831cc0c0ea1eb1aafe2901447f7d9b9d76a495dcf92
/api/call?method=getBukkitVersion&key=1d4034ddbda9189ae2175ab929012ba8a7bba7c7121497a1a0eb122c572f8de8
/api/call?method=getPlayer&args=%5B%22silvinci%22%5D&key=341f36fce351707911d4baaa0f245f09222aaa5b7d907f3f43781d6dafdb6c94
/api/call?method=broadcastWithName&args=%5B%22Hello%22%2C%22silvinci%22%%5D&key=4f77adf03116c4ca4e19c93a39595b0e8c04bd1df718fd8e366f08c2cd1edeee

You can use standard API requests in both a HTTP request and an open socket connection. See How to use the standard API over HTTP and How to use the standard API over a socket connection for more information.

###Multiple API request Exactly the same as a standard API request, but method is a JSON-encoded array of method names and args is a JSON-encoded array of arrays. Don't forget to properly escape both. Also, make sure all requests go to /api/call-multiple not /api/call.

/api/call-multiple?method={jsonEncodedArrayOfMethodNames}&args={jsonEncodedArrayOfArgs}&key={key}

Here is an example:

+-------- Methods --------+---According arguments ---+----------------- Function ---------------+
| getPlayer               | ["silvinci"]             | Get information about player silvinci    |
| givePlayerItemWithData  | ["silvinci", 35, 64, 14] | Give player silvinci a stack of red wool |
| opPlayer                | ["silvinci"]             | Make player silvinci OP                  |
| getPlayerCount          | []                       | Get the number of online players         |
+-------------------------+--------------------------+------------------------------------------+

JSON encoded arrrays:

["getPlayer","givePlayerItemWithData","opPlayer","getPlayerCount"]
[ ["silvinci"],["silvinci",35,64,14],["silvinci"],[]] // due to a technical limitation of GitHub, there is a space needed between [ and the following [. It doesnt belong there.

Escaped:

%5B%22getPlayer%22%2C%22givePlayerItemWithData%22%2C%22opPlayer%22%2C%22getPlayerCount%22%5D
%5B%5B%22silvinci%22%5D%2C%5B%22silvinci%22%2C35%2C64%2C14%5D%2C%5B%22silvinci%22%5D%2C%5B%5D%5D

Putting it all together for the user alecgorge with the password MySecret and salt pepper123:

/api/call-multiple?method=%5B%22getPlayer%22%2C%22givePlayerItemWithData%22%2C%22opPlayer%22%2C%22getPlayerCount%22%5D&args=%5B%5B%22silvinci%22%5D%2C%5B%22silvinci%22%2C35%2C64%2C14%5D%2C%5B%22silvinci%22%5D%2C%5B%5D%5D&key=760457f2d3bbbe2ae6e8c3766119464e99e454057d799b8097948f9217c1eab6

###Stream API request A stream API request is one where a request is made to "subscribe" to a particular data source, and as new data is found on the server, another response line is sent back to the client.

A stream API request looks like this:

/api/subscribe?source={sourceName}&key={key}&show_previous={showPrevious}

Every thing in curly braces in that URL is something you will need to change for each different API request. Let's analyze each one:

  • {sourceName} is the the name the data source you want to subscribe to. The only options right now are chat, console and connections.
  • {key} is a valid key for this method (see below for more information on keys).
  • {showPrevious} (optional, default is true) is either true or false. The last 50 messages will be sent immediately if this is true.

You can use stream API requests in both a HTTP request and an open socket connection. See How to use the stream API over HTTP and How to use the stream API over a socket connection for more information.

###An invalid request An invalid request does not fulfill any of the other request types.

##Key format All requests need to be authenticated with a key for security reasons. The key is a sha256 sum of a string in this format: username + methodNameOrSourceName + password + salt

  • username needs to be a valid username that is in the server's config.yml in the plugins/JSONAPI/ folder.
  • methodNameOrSourceName is the name of the method that you are trying to call. If you are using the multiple-call API, then this should just be the JSON-encoded array of methods. It can also be the name of the data source you are trying to subscribe to in the stream API. This should be exactly the same as the unescaped method or source GET variable.
  • password is the corresponding password to username.
  • salt is the salt set in the server's config.yml in the plugins/JSONAPI folder.

This format means that there is a different key for each different API method. When you have multiple username and password combinations, that means that there can be more than one valid key.

Here are some examples of valid keys for the user alecgorge with the password MySecret and salt pepper123:

getBukkitVersion -> alecgorgegetBukkitVersionMySecretpepper123 -> b97162cc3ec6d230d196b7f5950dc2dcb2877947ddbc2db7268f8e0ebf797a1e
saveMap          -> alecgorgesaveMapMySecretpepper123          -> 37db859d3d23d3affcabdc72ec66d34cda8dea58ee0e0dc7bce8fa9af4ac2db2
reloadServer     -> alecgorgereloadServerMySecretpepper123     -> 858eef1c1591a2a5c3f156b92cb60948d65c1ed5867a4cc0100d57897f8fb572

["getPlayer","givePlayerItemWithData","opPlayer","getPlayerCount"] -> alecgorge["getPlayer","givePlayerItemWithData","opPlayer","getPlayerCount"]MySecretpepper123 -> 760457f2d3bbbe2ae6e8c3766119464e99e454057d799b8097948f9217c1eab6

connections     -> alecgorgeconnectionsMySecretpepper123       -> 93d730c15edd4a66ff12a9b9ebbfb1842d301a06db25000cfa28ff5fb7b80009

Here are some examples of key generation in different languages:

PHP

$key = hash('sha256', $username . $methodNameOrSourceName . $password . $salt);

Java

// this assumes you have a method called `sha256` that creates the SHA256 hash from a `String`
String key = sha256(username + methodNameOrSourceName + password + salt);

node.js

var crypto = require("crypto");
function getKey(username, methodNameOrSourceName, password, salt) {
	var shasum = crypto.createHash("sha256");
	shasum.update(username + methodNameOrSourceName + password + salt);
	return shasum.digest("hex");
}
var key = getKey(username, methodNameOrSourceName, password, salt);