|
| 1 | +# `AustinHarris.JsonRpc` |
| 2 | + |
| 3 | +`AustinHarris.JsonRpc` is a JSON-RPC 2.0 server library for .NET. |
| 4 | +Give it a UTF-8 request document and receive the response document: bytes in, bytes out. |
| 5 | +It handles parsing, method dispatch, parameter binding, batches and error responses. |
| 6 | +You supply the transport. |
| 7 | + |
| 8 | +The core has no JSON library dependency. |
| 9 | +Its built-in serializer works on its own; companion packages add Json.NET, |
| 10 | +`System.Text.Json` or ASP.NET Core hosting. |
| 11 | + |
| 12 | +Targets `netstandard2.0`, `netstandard2.1`, `net8.0` and `net10.0`. |
| 13 | +This is a server library, with no client proxies or server-to-client calls. |
| 14 | + |
| 15 | +## Install |
| 16 | + |
| 17 | +2.0 is a prerelease. Include `--prerelease` when installing: |
| 18 | + |
| 19 | +```sh |
| 20 | +dotnet add package AustinHarris.JsonRpc --prerelease |
| 21 | +``` |
| 22 | + |
| 23 | +Coming from 1.x? Read [What is new in 2.0](https://astn.github.io/JSON-RPC.NET/changelog.html) and [Upgrading from 1.x](https://astn.github.io/JSON-RPC.NET/upgrading.html) first. |
| 24 | + |
| 25 | +## Getting started |
| 26 | + |
| 27 | +### Declare a service |
| 28 | + |
| 29 | +Create `CalculatorService.cs` with the service below. |
| 30 | +Derive from `JsonRpcService` and mark exposed methods with `[JsonRpcMethod]`. |
| 31 | +Constructing the service registers its methods in the default session. |
| 32 | + |
| 33 | +```csharp |
| 34 | +using AustinHarris.JsonRpc; |
| 35 | + |
| 36 | +public class CalculatorService : JsonRpcService |
| 37 | +{ |
| 38 | + [JsonRpcMethod] // exposed as "add" |
| 39 | + private double add(double l, double r) => l + r; |
| 40 | + |
| 41 | + [JsonRpcMethod("multiply")] // exposed under an explicit name |
| 42 | + public int Multiply(int l, int r) => l * r; |
| 43 | + |
| 44 | + [JsonRpcMethod] |
| 45 | + public string StringMe(string x) => x; |
| 46 | +} |
| 47 | +``` |
| 48 | + |
| 49 | +Methods can be `private`. Parameters can be positional or named. |
| 50 | +Keep the service instance alive; it serves concurrent requests, so its state must be thread-safe. |
| 51 | + |
| 52 | +### Process requests |
| 53 | + |
| 54 | +Put this code in `Program.cs` in a console project targeting `net8.0` or `net10.0`. |
| 55 | +It calls the service through both string overloads and the byte entry point. |
| 56 | +The string overloads transcode into the byte pipeline. |
| 57 | + |
| 58 | +```csharp |
| 59 | +using System; |
| 60 | +using System.Buffers; |
| 61 | +using System.Text; |
| 62 | +using AustinHarris.JsonRpc; |
| 63 | + |
| 64 | +var service = new CalculatorService(); // binds itself to the default session; keep a reference |
| 65 | +
|
| 66 | +// Strings, asynchronous invocation. |
| 67 | +string response = await JsonRpcProcessor.ProcessAsync("{\"jsonrpc\":\"2.0\",\"method\":\"add\",\"params\":[1,2],\"id\":1}"); |
| 68 | +// {"jsonrpc":"2.0","result":3.0,"id":1} |
| 69 | +
|
| 70 | +// Strings, synchronous, on the calling thread. Named parameters. |
| 71 | +string sync = JsonRpcProcessor.ProcessSync("{\"method\":\"multiply\",\"params\":{\"l\":6,\"r\":7},\"id\":2}"); |
| 72 | +// {"jsonrpc":"2.0","result":42,"id":2} |
| 73 | +
|
| 74 | +// Bytes: the native path. The string overloads transcode into it. |
| 75 | +byte[] request = Encoding.UTF8.GetBytes("{\"method\":\"add\",\"params\":[2,3],\"id\":3}"); |
| 76 | +var output = new ArrayBufferWriter<byte>(); |
| 77 | +JsonRpcProcessor.Process(Handler.DefaultSessionId(), request.AsSpan(), output); |
| 78 | +Console.WriteLine(Encoding.UTF8.GetString(output.WrittenSpan)); // nothing is written for a notification |
| 79 | +``` |
| 80 | + |
| 81 | +The string responses appear in the comments above. The byte call prints: |
| 82 | + |
| 83 | +```json |
| 84 | +{"jsonrpc":"2.0","result":5.0,"id":3} |
| 85 | +``` |
| 86 | + |
| 87 | +A batch returns an array when it contains calls that need responses. |
| 88 | +A notification has no `id` and produces no response. |
| 89 | +Pass a `byte[]` as `AsSpan()` to select the span overload. |
| 90 | + |
| 91 | +Use `JsonRpcProcessor.ProcessAsync` for methods returning `Task` or `ValueTask`. |
| 92 | +The synchronous entry points do not await those methods. |
| 93 | + |
| 94 | +## Performance |
| 95 | + |
| 96 | +These results compare 1.2.3 and 2.0 with the same five requests on the same machine in one session. |
| 97 | +The 2.0 runs used the built-in serializer on an AMD Ryzen 7 7800X3D with .NET 10, Release and Server GC. |
| 98 | + |
| 99 | + |
| 100 | + |
| 101 | +| Path | RPC/s | Against 1.2.3 | |
| 102 | +| --- | ---: | ---: | |
| 103 | +| 1.2.3, `Task<string> Process(string)`, thread pool, best batch size | 3.08 M | | |
| 104 | +| 2.0, the same string API and the same loop | 13.3 M | 4.3× | |
| 105 | +| 2.0, `Process(bytes)`, 16 dedicated threads | 31.7 M | 10.3× | |
| 106 | +| 2.0, `ProcessAsync(bytes)`, 16 awaited workers | 32.1 M | 10.4× | |
| 107 | + |
| 108 | +These measurements cover the library without a transport. |
| 109 | +The asynchronous byte row uses methods that complete inline. |
| 110 | +For Kestrel TCP with 256 requests in flight per connection and `EnableAsyncMethods = false`, |
| 111 | +the measured range is 14.3 M to 16.5 M RPC/s on loopback, with clients and server on the same machine. |
| 112 | + |
| 113 | +See the [benchmark tables](https://astn.github.io/JSON-RPC.NET/#benchmarks) for conditions |
| 114 | +and the [benchmark explorer](https://astn.github.io/JSON-RPC.NET/benchmarks/charts/explorer.html) for the data. |
| 115 | + |
| 116 | +## Companion packages |
| 117 | + |
| 118 | +Install only the integrations your host needs. Use matching package versions. |
| 119 | + |
| 120 | +- [`AustinHarris.JsonRpc.Newtonsoft`](https://astn.github.io/JSON-RPC.NET/newtonsoft.html): Json.NET converters, contract resolvers, `[JsonProperty]`, `JsonSerializerSettings` and lenient input. |
| 121 | +- [`AustinHarris.JsonRpc.SystemTextJson`](https://astn.github.io/JSON-RPC.NET/systemtextjson.html): `System.Text.Json` conversion with `JsonSerializerOptions`, reading and writing UTF-8 values. |
| 122 | +- [`AustinHarris.JsonRpc.AspNetCore`](https://astn.github.io/JSON-RPC.NET/aspnetcore.html): HTTP endpoints, raw Kestrel connections over TCP, Unix sockets or named pipes, and dependency injection. |
| 123 | + |
| 124 | +## Upgrading from 1.x |
| 125 | + |
| 126 | +Most service methods can stay as they are. Review these changes before switching: |
| 127 | + |
| 128 | +- Json.NET is now a companion package. Use it for `JsonSerializerSettings` and settings-based |
| 129 | + compatibility helpers; `JsonRequest.Params` follows the selected serializer's object model. |
| 130 | +- Review string overloads, especially calls with a positional null context. |
| 131 | + Name the `context` argument explicitly; serializer-taking overloads have changed. |
| 132 | +- Check client-visible behaviour: notifications never return responses, responding batches stay arrays, |
| 133 | + and version values, named parameters and conversion failures are validated. |
| 134 | +- Unhandled exceptions now return internal errors with exception details hidden by default. |
| 135 | + |
| 136 | +[Upgrading from 1.x](https://astn.github.io/JSON-RPC.NET/upgrading.html) covers every API and wire change, and |
| 137 | +[What is new in 2.0](https://astn.github.io/JSON-RPC.NET/changelog.html) is the full changelog. |
| 138 | + |
| 139 | +## Documentation |
| 140 | + |
| 141 | +- [Server guide](https://astn.github.io/JSON-RPC.NET/) |
| 142 | +- [Hosting](https://astn.github.io/JSON-RPC.NET/#hosting) |
| 143 | +- [Configuration](https://astn.github.io/JSON-RPC.NET/#configuration) |
| 144 | +- [Errors and exception handling](https://astn.github.io/JSON-RPC.NET/#errors) |
| 145 | +- [Asynchronous methods and cancellation](https://astn.github.io/JSON-RPC.NET/#asynchronous-methods-and-cancellation) |
| 146 | +- [Security and host responsibilities](https://astn.github.io/JSON-RPC.NET/#security) |
| 147 | +- [What is new](https://astn.github.io/JSON-RPC.NET/changelog.html) |
| 148 | +- [Upgrading from 1.x](https://astn.github.io/JSON-RPC.NET/upgrading.html) |
| 149 | +- [Source repository](https://github.com/Astn/JSON-RPC.NET) |
| 150 | +- [MIT license](https://github.com/Astn/JSON-RPC.NET/blob/master/LICENSE) |
0 commit comments