Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 A library that implements the [JSON-RPC 2.0 spec][spec]. | 1 A library that implements the [JSON-RPC 2.0 spec][spec]. |
| 2 | 2 |
| 3 [spec]: http://www.jsonrpc.org/specification | 3 [spec]: http://www.jsonrpc.org/specification |
| 4 | 4 |
| 5 ## Server | 5 ## Server |
| 6 | 6 |
| 7 A JSON-RPC 2.0 server exposes a set of methods that can be called by clients. | 7 A JSON-RPC 2.0 server exposes a set of methods that can be called by clients. |
| 8 These methods can be registered using `Server.registerMethod`: | 8 These methods can be registered using `Server.registerMethod`: |
| 9 | 9 |
| 10 ```dart | 10 ```dart |
| 11 import "package:json_rpc_2/json_rpc_2.dart" as json_rpc; | 11 import "package:json_rpc_2/json_rpc_2.dart" as json_rpc; |
| 12 | 12 |
| 13 var server = new json_rpc.Server(); | 13 WebSocket.connect('ws://localhost:4321').then((socket) { |
|
Bob Nystrom
2015/02/05 00:32:24
Would it be premature to use await here? :)
Also,
nweiz
2015/02/05 01:02:48
Yes :(
| |
| 14 // You can start the server with a Stream for requests and a StreamSink for | |
| 15 // responses, or with an object that's both, like a WebSocket. | |
| 16 var server = new json_rpc.Server(socket); | |
| 14 | 17 |
| 15 // Any string may be used as a method name. JSON-RPC 2.0 methods are | 18 // Any string may be used as a method name. JSON-RPC 2.0 methods are |
| 16 // case-sensitive. | 19 // case-sensitive. |
| 17 var i = 0; | 20 var i = 0; |
| 18 server.registerMethod("count", () { | 21 server.registerMethod("count", () { |
| 19 // Just return the value to be sent as a response to the client. This can be | 22 // Just return the value to be sent as a response to the client. This can be |
| 20 // anything JSON-serializable, or a Future that completes to something | 23 // anything JSON-serializable, or a Future that completes to something |
| 21 // JSON-serializable. | 24 // JSON-serializable. |
| 22 return i++; | 25 return i++; |
| 26 }); | |
| 27 | |
| 28 // Methods can take parameters. They're presented as a [Parameters] object whi ch | |
|
Bob Nystrom
2015/02/05 00:32:24
Some long lines.
nweiz
2015/02/05 01:02:48
Done.
| |
| 29 // makes it easy to validate that the expected parameters exist. | |
| 30 server.registerMethod("echo", (params) { | |
| 31 // If the request doesn't have a "message" parameter, this will automaticall y | |
| 32 // send a response notifying the client that the request was invalid. | |
| 33 return params.getNamed("message"); | |
| 34 }); | |
| 35 | |
| 36 // [Parameters] has methods for verifying argument types. | |
| 37 server.registerMethod("subtract", (params) { | |
| 38 // If "minuend" or "subtrahend" aren't numbers, this will reject the request . | |
| 39 return params.getNum("minuend") - params.getNum("subtrahend"); | |
| 40 }); | |
| 41 | |
| 42 // [Parameters] also supports optional arguments. | |
| 43 server.registerMethod("sort", (params) { | |
| 44 var list = params.getList("list"); | |
| 45 list.sort(); | |
| 46 if (params.getBool("descending", orElse: () => false)) { | |
| 47 return params.list.reversed; | |
| 48 } else { | |
| 49 return params.list; | |
| 50 } | |
| 51 }); | |
| 52 | |
| 53 // A method can send an error response by throwing a `json_rpc.RpcException`. | |
| 54 // Any positive number may be used as an application-defined error code. | |
| 55 const DIVIDE_BY_ZERO = 1; | |
| 56 server.registerMethod("divide", (params) { | |
| 57 var divisor = params.getNum("divisor"); | |
| 58 if (divisor == 0) { | |
| 59 throw new json_rpc.RpcException(DIVIDE_BY_ZERO, "Cannot divide by zero."); | |
| 60 } | |
| 61 | |
| 62 return params.getNum("dividend") / divisor; | |
| 63 }); | |
| 64 | |
| 65 // To give you time to register all your methods, the server won't actually | |
| 66 // start listening for requests until you call `listen`. | |
| 67 return server.listen(); | |
| 23 }); | 68 }); |
| 24 | |
| 25 // Methods can take parameters. They're presented as a [Parameters] object which | |
| 26 // makes it easy to validate that the expected parameters exist. | |
| 27 server.registerMethod("echo", (params) { | |
| 28 // If the request doesn't have a "message" parameter, this will automatically | |
| 29 // send a response notifying the client that the request was invalid. | |
| 30 return params.getNamed("message"); | |
| 31 }); | |
| 32 | |
| 33 // [Parameters] has methods for verifying argument types. | |
| 34 server.registerMethod("subtract", (params) { | |
| 35 // If "minuend" or "subtrahend" aren't numbers, this will reject the request. | |
| 36 return params.getNum("minuend") - params.getNum("subtrahend"); | |
| 37 }); | |
| 38 | |
| 39 // [Parameters] also supports optional arguments. | |
| 40 server.registerMethod("sort", (params) { | |
| 41 var list = params.getList("list"); | |
| 42 list.sort(); | |
| 43 if (params.getBool("descending", orElse: () => false)) { | |
| 44 return params.list.reversed; | |
| 45 } else { | |
| 46 return params.list; | |
| 47 } | |
| 48 }); | |
| 49 | |
| 50 // A method can send an error response by throwing a `json_rpc.RpcException`. | |
| 51 // Any positive number may be used as an application-defined error code. | |
| 52 const DIVIDE_BY_ZERO = 1; | |
| 53 server.registerMethod("divide", (params) { | |
| 54 var divisor = params.getNum("divisor"); | |
| 55 if (divisor == 0) { | |
| 56 throw new json_rpc.RpcException(DIVIDE_BY_ZERO, "Cannot divide by zero."); | |
| 57 } | |
| 58 | |
| 59 return params.getNum("dividend") / divisor; | |
| 60 }); | |
| 61 ``` | |
| 62 | |
| 63 Once you've registered your methods, you can handle requests with | |
| 64 `Server.parseRequest`: | |
| 65 | |
| 66 ```dart | |
| 67 import 'dart:io'; | |
| 68 | |
| 69 WebSocket.connect('ws://localhost:4321').then((socket) { | |
| 70 socket.listen((message) { | |
| 71 server.parseRequest(message).then((response) { | |
| 72 if (response != null) socket.add(response); | |
| 73 }); | |
| 74 }); | |
| 75 }); | |
| 76 ``` | |
| 77 | |
| 78 If you're communicating with objects that haven't been serialized to a string, | |
| 79 you can also call `Server.handleRequest` directly: | |
| 80 | |
| 81 ```dart | |
| 82 import 'dart:isolate'; | |
| 83 | |
| 84 var receive = new ReceivePort(); | |
| 85 Isolate.spawnUri('path/to/client.dart', [], receive.sendPort).then((_) { | |
| 86 receive.listen((message) { | |
| 87 server.handleRequest(message['request']).then((response) { | |
| 88 if (response != null) message['respond'].send(response); | |
| 89 }); | |
| 90 }); | |
| 91 }) | |
| 92 ``` | 69 ``` |
| 93 | 70 |
| 94 ## Client | 71 ## Client |
| 95 | 72 |
| 96 Currently this package does not contain an implementation of a JSON-RPC 2.0 | 73 A JSON-RPC 2.0 client calls methods on a server and handles the server's |
| 97 client. | 74 responses to those method calls. These methods can be called using |
| 75 `Client.sendRequest`: | |
| 98 | 76 |
| 77 ```dart | |
| 78 import "package:json_rpc_2/json_rpc_2.dart" as json_rpc; | |
| 79 | |
| 80 WebSocket.connect('ws://localhost:4321').then((socket) { | |
| 81 // Just like the server, a client takes a Stream and a StreamSink or a | |
| 82 // single object that's both. | |
| 83 var client = new json_rpc.Client(socket); | |
| 84 | |
| 85 // This calls the "count" method on the server. A Future is returned that will | |
| 86 // complete to the value contained in the server's response. | |
| 87 client.sendRequest("count").then((result) => print("Count is $result.")); | |
| 88 | |
| 89 // Parameters are passed as a simple Map or, for positional parameters, an | |
| 90 // Iterable. Make sure they're JSON-serializable! | |
| 91 client.sendRequest("echo", {"message": "hello"}) | |
| 92 .then((echo) => print('Echo says "$echo"!')); | |
| 93 | |
| 94 // A notification is a way to call a method that tells the server that no | |
| 95 // result is expected. Its return type is `void`; even if it causes an error, | |
| 96 // you won't hear back. | |
| 97 client.sendNotification("count"); | |
| 98 | |
| 99 // If the server sends an error response, the returned Future will complete | |
| 100 // with an RpcException. You can catch this error and inspect its error code, | |
| 101 // message, and any data that the server sent along with it. | |
| 102 client.sendRequest("divide", {"dividend": 2, "divisor": 0}).catchError((error) { | |
|
Bob Nystrom
2015/02/05 00:32:25
Long.
nweiz
2015/02/05 01:02:48
Done.
| |
| 103 print("RPC error ${error.code}: ${error.message}"); | |
| 104 }); | |
| 105 | |
| 106 // The client won't subscribe to the input stream until you call `listen`. | |
| 107 client.listen(); | |
| 108 }); | |
| 109 ``` | |
| 110 | |
| 111 ## Peer | |
| 112 | |
| 113 Although JSON-RPC 2.0 only explicitly describes clients and servers, it also | |
| 114 mentions that two-way communication can be supported by making each endpoint | |
| 115 both a client and a server. This package supports this directly using the `Peer` | |
| 116 class, which implements both `Client` and `Server`. It supports same methods as | |
|
Bob Nystrom
2015/02/05 00:32:25
"same" -> "the same".
nweiz
2015/02/05 01:02:48
Done.
| |
| 117 those classes, and automatically makes sure that every message from the other | |
| 118 endpoint is routed and handled correctly. | |
| OLD | NEW |