JSON-RPCって何？

この記事は、[JSON-RPC 2.0](http://www.jsonrpc.org) を調べたことを走り書きしたレベルものです。実際の利用経験に基づいた情報でもなければ、しっかり時間をかけて裏付けをとった情報でもないため、誤った理解が含まれている可能性がある点にご注意ください。ツッコミや編集リクエストは歓迎します。

## 概要

[JSON-RPC](http://www.jsonrpc.org) は、 [RPC (Remote Procedure Call)](https://ja.wikipedia.org/wiki/RPC) を実現するプロトコルの1つです。その名の通りJSON形式でリクエスト＆レスポンスを表現するシンプルな仕様となっています。

例えば、次のようなリクエストをJSONで送ると、別のマシンで `subtract(42, 23)` が実行され、結果の `19` がレスポンスとして返ってきます。

```json:request
{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}
```
```json:response
{"jsonrpc": "2.0", "result": 19, "id": 1}
```

プロトコルの仕様でしかないため、どう実現されるかは個別の実装に委ねられる点に注意してください。

JSONを使ったシンプルなやりとりになるため、Webブラウザやモバイルアプリなどのクライアントからサーバーへ通信するときだけでなく、マイクロサービスにおけるサーバー間の通信にも利用できます。

## サンプル

### 基本形

典型的なパターンは次の通りです。

```json:request
{"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}
```
```json:response
{"jsonrpc": "2.0", "result": 19, "id": 1}
```

* `jsonrpc` はバージョン1.0と区別するためのフィールドで `2.0` 固定
* `method` は実行する関数名
* `params` は関数の引数リスト
* `id` はリクエストを識別する番号（レスポンスに同じ番号が入るので追跡できる）
* `result` は実行結果が入る任意のオブジェクト

### 名前付き引数

関数のパラメーターは、名前付き引数で指定することもできます。

```json:request
 {"jsonrpc": "2.0", "method": "subtract", "params": {"subtrahend": 23, "minuend": 42}, "id": 3}
```
```json:response
{"jsonrpc": "2.0", "result": 19, "id": 3}
```

### 通知 (Notification) 

`id` フィールドを省略すると通知とみなされます。この場合は、サーバーからレスポンスは返ってきません。

```json:request
{"jsonrpc": "2.0", "method": "update", "params": [1,2,3,4,5]}
```

### エラー

エラーが発生した場合は、 `result` の代わりに `error` オブジェクトが返却されます。エラーコード `code` は -32768 〜 -32000 があらかじめ予約されていてかなり違和感がありますが、 [XML-RPC](http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php) にならっているとのことです。

```json:request
{"jsonrpc": "2.0", "method": "foobar", "id": "1"}
```
```json:response
{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "1"}
```

### バッチ (Batch) 

複数のリクエストをまとめて送ることもできます。レスポンスのリストは順番が保証されていないため、 `id` フィールドを使ってリクエストと突き合わせる必要があります。

```json:request
[
    {"jsonrpc": "2.0", "method": "sum", "params": [1,2,4], "id": "1"},
    {"jsonrpc": "2.0", "method": "notify_hello", "params": [7]},
    {"jsonrpc": "2.0", "method": "subtract", "params": [42,23], "id": "2"},
    {"foo": "boo"},
    {"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"},
    {"jsonrpc": "2.0", "method": "get_data", "id": "9"} 
]
```
```json:response
[
    {"jsonrpc": "2.0", "result": 7, "id": "1"},
    {"jsonrpc": "2.0", "result": 19, "id": "2"},
    {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": null},
    {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"},
    {"jsonrpc": "2.0", "result": ["hello", 5], "id": "9"}
]
```

## 実装

仕様がシンプルで薄いラッパーになりがちだからか、ライブラリは色々ある印象。どれがデファクトか分からない…

Go

* [net/rpc/jsonrpc](https://golang.org/pkg/net/rpc/jsonrpc/)
    * 標準モジュールだが JSON-RPC 2.0 には対応していない
* [gorilla/rpc/v2/json2](http://www.gorillatoolkit.org/pkg/rpc/v2/json2)
    * 標準モジュールっぽい仕様で JSON-RPC 2.0 にも対応している
* https://godoc.org/?q=jsonrpc+2.0

Java

* [jsonrpc4j](https://github.com/briandilley/jsonrpc4j)

## メリット？

* REST APIよりはURL設計が楽そう
    * エンドポイントとなるURLは1つでもよい（リクエストの中身で `method` を指定するので）
    * 同じリソースに複数操作がある場合などに表現しやすいはず
* ただのJSONなのでクライアント／サーバー間の通信でも使える
* Batchリクエストの機能は便利そう
* 通信レイヤーはHTTPに限定されないので、WebSocketなどと組み合わせても使える

## デメリット？

* REST APIほどは周辺のエコシステム（デバッグツールやAPIドキュメント生成など）が揃っていなさそう
* リクエストの中身を見ないとどんな操作を要求するリクエストか分からずデバッグしづらそう
* すべてPOSTで `id` も毎回変わるとなるとキャッシュを効かせづらそう

## 調査できていない

* APIドキュメント系のツール
* プログラム自動生成系のツール（アプリケーションのひな形やテストコード）

## 参考情報

* [JSON-RPC](http://www.jsonrpc.org)
* [Go で始める JSON-RPC 入門](https://medium.com/@osamingo/how-to-use-json-rpc-with-go-63f218676e83)
* [REST vs JSON-RPC?](https://stackoverflow.com/questions/15056878/rest-vs-json-rpc)