You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Getting started: single-file lambda server first, raw string literals in step 2
Step 1 now opens with a complete Kestrel server in one file: a .NET 10
file-based app that registers two lambdas with ServiceBinder.BindMethod
and maps them at /rpc, with the verified curl exchange. The
CalculatorService class follows as the second example, and the session
paragraph says what happens when both register the same name.
Step 2 uses raw string literals and a u8 literal for the byte call, so
the JSON is readable and the byte[]/AsSpan() workaround is no longer
needed in the example. The note records what was measured: a bare
byte[] is ambiguous on C# 12 and 13 and selects the span overload on
C# 14 and later.
The package README mirrors both changes at shorter length.
Run `dotnet run server.cs`; Kestrel prints its listening URL.
52
+
Use `dotnet run server.cs -- --urls http://127.0.0.1:5077` to pin it for this request from another terminal:
53
+
54
+
```bash
55
+
curl -s -X POST http://127.0.0.1:5077/rpc -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"add","params":[1,2],"id":1}'
56
+
```
57
+
58
+
```json
59
+
{"jsonrpc":"2.0","result":3.0,"id":1}
60
+
```
61
+
62
+
On .NET 8, use the same code in `Program.cs` in an ordinary ASP.NET Core project, install with `dotnet add package AustinHarris.JsonRpc.AspNetCore --prerelease`, and drop the two `#:` lines.
63
+
64
+
For a service class, create `CalculatorService.cs`.
30
65
Derive from `JsonRpcService` and mark exposed methods with `[JsonRpcMethod]`.
31
66
Constructing the service registers its methods in the default session.
32
67
@@ -47,8 +82,13 @@ public class CalculatorService : JsonRpcService
47
82
```
48
83
49
84
Methods can be `private`. Parameters can be positional or named.
85
+
Optional parameter defaults are honoured; `[JsonRpcParam("name")]` overrides a parameter's JSON name.
50
86
Keep the service instance alive; it serves concurrent requests, so its state must be thread-safe.
51
87
88
+
Both examples use the default session (`Handler.DefaultSessionId()`).
89
+
Lambdas and classes can be mixed in one session when their method names differ; both examples register `add`, so keep one of them.
90
+
The next step drives `CalculatorService` in process, without a transport.
91
+
52
92
### Process requests
53
93
54
94
Put this code in `Program.cs` in a console project targeting `net8.0` or `net10.0`.
@@ -64,17 +104,16 @@ using AustinHarris.JsonRpc;
64
104
varservice=newCalculatorService(); // binds itself to the default session; keep a reference
Copy file name to clipboardExpand all lines: README.md
+41-8Lines changed: 41 additions & 8 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -91,7 +91,39 @@ Add `AustinHarris.JsonRpc.Newtonsoft` or `AustinHarris.JsonRpc.SystemTextJson` i
91
91
92
92
### 1. Declare a service
93
93
94
-
Derive from `JsonRpcService` and mark the methods you want to expose with `[JsonRpcMethod]`. Constructing the service registers it, so you only need to keep the instance alive.
94
+
Save this as `server.cs`. It is a .NET 10 file-based app: one C# file with no project file. The `#:sdk` and `#:package` directives select the web SDK and package. `ServiceBinder.BindMethod` registers the lambdas; Kestrel serves them at `/rpc`.
Run `dotnet run server.cs`. Kestrel prints the URL it listens on. To use the address below, run `dotnet run server.cs -- --urls http://127.0.0.1:5077`, then send this request from another terminal:
115
+
116
+
```bash
117
+
curl -s -X POST http://127.0.0.1:5077/rpc -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"add","params":[1,2],"id":1}'
118
+
```
119
+
120
+
```json
121
+
{"jsonrpc":"2.0","result":3.0,"id":1}
122
+
```
123
+
124
+
On .NET 8, the same code works in `Program.cs` in an ordinary ASP.NET Core project: install the package with `dotnet add package AustinHarris.JsonRpc.AspNetCore --prerelease` and drop the two `#:` lines.
125
+
126
+
For methods grouped in a class, create `CalculatorService.cs`. Derive from `JsonRpcService` and mark the methods you want to expose with `[JsonRpcMethod]`. Constructing the service registers it, so you only need to keep the instance alive.
95
127
96
128
```csharp
97
129
usingAustinHarris.JsonRpc;
@@ -111,7 +143,9 @@ public class CalculatorService : JsonRpcService
111
143
112
144
Methods can be `private`; parameters may be positional (`"params":[1,2]`) or named (`"params":{"l":1,"r":2}`). Optional parameters with default values are honoured, and a parameter's JSON name can be overridden with `[JsonRpcParam("name")]`.
113
145
114
-
Every method lives in a *session*, a named set of methods. Everything above goes into the default session (`Handler.DefaultSessionId()`), which is all most applications need. Overloads that take a `sessionId` let one process serve separate method sets; see [Sessions and context](#sessions-and-context).
146
+
Every method lives in a *session*, a named set of methods. Both examples register methods in the default session (`Handler.DefaultSessionId()`), which is all most applications need. Lambdas and classes can be mixed in one session, but method names must be unique: `BindMethod` throws for a name that is already registered, and a class bound afterwards replaces an earlier registration of the same name. Both examples register `add`, so keep one of them. Overloads that take a `sessionId` let one process serve separate method sets; see [Sessions and context](#sessions-and-context).
147
+
148
+
The next step drives `CalculatorService` in process, without a transport.
115
149
116
150
### 2. Process requests
117
151
@@ -124,23 +158,22 @@ using AustinHarris.JsonRpc;
124
158
varservice=newCalculatorService(); // binds itself to the default session; keep a reference
Console.WriteLine(Encoding.UTF8.GetString(output.WrittenSpan)); // nothing is written for a notification
139
172
```
140
173
141
-
Batches (`[{...},{...}]`) and notifications (requests without an `id`) are handled per the spec: a batch answers with an array, a notification produces nothing. The byte overloads take `ReadOnlySpan<byte>`, `ReadOnlyMemory<byte>` or `ReadOnlySequence<byte>`; pass a `byte[]` as `AsSpan()`, because on C# 12 a bare array is ambiguous between the memory and span overloads.
174
+
Batches (`[{...},{...}]`) and notifications (requests without an `id`) are handled per the spec: a batch answers with an array, a notification produces nothing. The byte overloads take `ReadOnlySpan<byte>`, `ReadOnlyMemory<byte>` or `ReadOnlySequence<byte>`. A `"""..."""u8` literal is a `ReadOnlySpan<byte>` (C# 11 and later). A bare `byte[]` selects the span overload on C# 14 and later; on C# 12 and 13 it is ambiguous between the memory and span overloads, so pass it as `AsSpan()` there.
142
175
143
-
That is the whole server. The rest of this page is about exposing methods, putting a transport in front, and what happens when things go wrong.
176
+
That is the whole in-process server. The rest of this page is about exposing methods, putting a transport in front, and what happens when things go wrong.
0 commit comments