Skip to content

RPC Server: Advanced Usages

Chen edited this page May 30, 2017 · 7 revisions

If you have read RPC Server: Getting Started, here you can learn some more advanced usages on server-side RPC handling.

Middlewares

Like "middlewares" in ASP.NET Core, in JsonRpc.Standard, it's possible to intercept the incoming JSON-RPC Request messages and customize the way they are processed. In JsonRpc.Standard, the middlewares are added by calling JsonRpcServiceHostBuilder.Intercept. For example, if you want to log the timing of each requests and responses, you may insert a middleware before the actual method dispatching and invocation step. Here is how it's done in UnitTestProject1.Utility

public static IJsonRpcServiceHost CreateJsonRpcServiceHost(UnitTestBase owner)
{
    var builder = new JsonRpcServiceHostBuilder();
    builder.Register(typeof(Utility).GetTypeInfo().Assembly);
    builder.ContractResolver = DefaultContractResolver;
    var globalSw = Stopwatch.StartNew();
    var session = new SessionFeature();
    if (owner.Output != null)
    {
        builder.Intercept(async (context, next) =>
        {
            var sw = Stopwatch.StartNew();
            owner.Output.WriteLine("{0}> {1}", globalSw.Elapsed, context.Request);
            try
            {
                context.Features.Set(session);
                await next();
                owner.Output.WriteLine("{0}< {1}", globalSw.Elapsed, context.Response);
            }
            finally
            {
                owner.Output.WriteLine("Server: Ellapsed time: {0}", sw.Elapsed);
            }
        });
    }
    builder.LoggerFactory = owner.LoggerFactory;
    return builder.Build();
}

in this xUnit-based unit test project, I used ITestOutputHelper provided by xUnit to print the timings, which will give me the following output

00:00:00.3802118> {"id":"32106157#1","method":"one","jsonrpc":"2.0"}
00:00:00.8513458< {"id":"32106157#1","result":1,"jsonrpc":"2.0"}
Server: Ellapsed time: 00:00:00.4970798
00:00:00.9267219> {"id":"32106157#2","method":"one","params":{"negative":false},"jsonrpc":"2.0"}
00:00:00.9487014< {"id":"32106157#2","result":1,"jsonrpc":"2.0"}
Server: Ellapsed time: 00:00:00.0223802
00:00:00.9496177> {"id":"32106157#3","method":"one","params":{"negative":true},"jsonrpc":"2.0"}
00:00:00.9500884< {"id":"32106157#3","result":-1,"jsonrpc":"2.0"}
Server: Ellapsed time: 00:00:00.0020213
00:00:00.9521625> {"id":"32106157#4","method":"two","jsonrpc":"2.0"}
00:00:00.9572604< {"id":"32106157#4","result":2,"jsonrpc":"2.0"}
Server: Ellapsed time: 00:00:00.0055367
00:00:00.9582994> {"id":"32106157#5","method":"two","params":{"negative":false},"jsonrpc":"2.0"}
00:00:00.9586298< {"id":"32106157#5","result":2,"jsonrpc":"2.0"}
Server: Ellapsed time: 00:00:00.0005468
00:00:00.9590794> {"id":"32106157#6","method":"two","params":{"negative":true},"jsonrpc":"2.0"}
00:00:00.9593365< {"id":"32106157#6","result":-2,"jsonrpc":"2.0"}
Server: Ellapsed time: 00:00:00.0004628

For another middleware example of converting OperationCanceledException emitted in JSON-RPC method handler into LSP-compliant RequestCancelled error response, you may take a look at the implementation of UseCancellationHandling of LanguageServer.VsCode/LanguageServerExtensions.cs in LanguageServer.NET.

Features

Again, uhmm, I stole the notion of "feature" from ASP.NET Core. Since JsonRpc.Standard v0.3, you can add or consume the "features" via RequestContext.Features. For example, considering that all the JsonRpcService instances are transcient and used in a "throwaway" manner, if you want to implement some session-like behavior, to pass the state between multiple method-calls, you can use custom feature interfaces to achieve this.

First of all, you can add features to the feature collection of the RequestContext in your middleware, so that you may use it in the method handler of JsonRpcService or in the inner middleware. Again, in CreateJsonRpcServiceHost utility function in the unit test project, I attached a SessionFeature instance to each of the incoming requests by calling context.Features.Set(session). Noting the session I passed into the feature collection is virtually the same object instance, each of the JSON-RPC call shares the same state, which allows the JSON-RPC server to remember whether the last Delay call is successful, for unit-testing purpose.

Besides, most of the JsonRpcServerHandler can attach a collection of default features by setting JsonRpcServerHandler.DefaultFeatures collection. You can also attach your session instance from here.

What's more, some JsonRpcServerHandler may attach some other features. For example, StreamRpcServerHandler will automatically attach IRequestCancellationFeature, if server-side cancellation is enabled, to each of the incoming requests. This will make it possible for the client to request the server to cancel another proceeding method handler. See InitializaionService.CancelRequest of DemoLanguageServer/Services/InitializaionService.cs in LanguageServer.NET for how this cancellation is initiated in the service method handler.

Clone this wiki locally