RPC.NET.Interfaces 5.0.0-preview3

This is a prerelease version of RPC.NET.Interfaces.
There is a newer version of this package available.
See the version list below for details.
Install-Package RPC.NET.Interfaces -Version 5.0.0-preview3
dotnet add package RPC.NET.Interfaces --version 5.0.0-preview3
<PackageReference Include="RPC.NET.Interfaces" Version="5.0.0-preview3" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add RPC.NET.Interfaces --version 5.0.0-preview3
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: RPC.NET.Interfaces, 5.0.0-preview3"
#r directive can be used in F# Interactive, C# scripting and .NET Interactive. Copy this into the interactive tool or source code of the script to reference the package.
// Install RPC.NET.Interfaces as a Cake Addin
#addin nuget:?package=RPC.NET.Interfaces&version=5.0.0-preview3&prerelease

// Install RPC.NET.Interfaces as a Cake Tool
#tool nuget:?package=RPC.NET.Interfaces&version=5.0.0-preview3&prerelease
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

RPC.NET Build status AppVeyor tests Coverage Status GitHub last commit (branch)

Simple, lightweight RPC implementation for .NET

This documentation refers the version 5.X of the library

Name Package
RPC.NET.Interfaces Nuget (with prereleases)
RPC.NET.Client Nuget (with prereleases)
RPC.NET.Server Nuget (with prereleases)
RPC.NET-Connector npm version

How it works

  1. The client sends a HTTP POST to the server where
    • The request URI

      • Must use HTTP or HTTPS scheme
      • Identifies the remote module and method (in the query component)
      • May contain the sessionid and/or custom data (in the query component)

      For example: http://www.example.org:1986/api?module=IMyModule&method=ModuleMethod&sessionid=xXx.

    • The content-type is application/json

    • The request body is an (UTF-8) JSON stringified array that contains the method arguments. For example: ["cica", 10].

  2. The type of response depends on the kind of the result:
    • If the remote method has a non Stream return value then the content-type is application/json and the response body contains the (UTF-8) JSON stringified result. The result is a wrapped object that contains the actual outcome of the method or the error description:
      {
        "Result": 12,
        "Exception": null
      }
      
      or
      {
        "Result": null,
        "Exception": {
          "TypeName": "System.Exception, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e",
          "Message": "Exception of type 'System.Exception' was thrown.",
          "Data": {}
        }
      }
      
    • If the remote method has a Stream return value (and the invocation was successful) then the content-type is application/octet-stream and the response body contains the raw data.

Server example

  1. Install the RPC.NET.Server package. Since modules are stored in a IServiceCollection you may need to install the Injector.NET package as well.

  2. Define an interface and implementation for your module:

    public interface ICalculator // Since clients may want to use it as well, it may be worth to put this interface into a common assembly
    {
      int Add(int a, int b);
      Task<int> AddAsync(int a, int b); // async methods also supported
      double PI { get; }
    }
    ...
    public class Calculator : ICalculator 
    {
      private readonly IRequestContext FContext;
      // You can access the request context as a dependency
      public Calculator(IRequestContext context) => FContext = context ?? throw new ArgumentNullException(nameof(context));
      public int Add(int a, int b) => a + b;
      public Task<int> AddAsync(int a, int b)
      {
        FContext.Cancellation.ThrowIfCancellationRequested();
        return Task.FromResult(a + b);
      }
      public double PI => Math.PI;
    }
    

    There are some control attributes that can be applied on (module) interface methods:

    • AliasAttribute: Specifies the alias of the method. Useful if your module has overloaded methods.

    • IgnoreAttribute: Marks the method "remotely invisible".

    • Aspects are also supported. The built-in aspects are the followings:

      • ParameterValidatorAspectAttribute:
      [ParameterValidatorAspect]
      public interface IModule
      {
        void DoSomething([NotNull, Match("cica", ParameterValidationErrorMessage = "ooops")] string arg1, [NotNull] object arg2);
        void DoSomethingElse();
        void ConditionallyValidated([NotNull(Condition = typeof(IfLoggedIn))] string arg);
      }
      
      public enum MyRoles
      {
        Anonymous = 0,
        LoggedInUser = 1
      }
      
      public class IfLoggedIn : IConditionalValidatior
      {
        public bool ShouldRun(MethodInfo containingMethod, IInjector currentScope) =>
          currentScope.Get<IRoleManager>().GetAssignedRoles(null).Equals(MyRoles.LoggedInUser);
      }
      

      The complete list of available parameter/property validators are here

      • TransactionAspectAttribute:
      [TransactionAspect]
      public interface IModule
      {
        void NonTransactional();
        [Transactional]
        void DoSomething(object arg);
        [Transactional]
        void DoSomethingFaulty();
        [Transactional(IsolationLevel = IsolationLevel.Serializable)]
        Task<int> DoSomethingAsync();
      }
      
      • RoleValidatorAspectAttribute:
      [Flags]
      public enum MyRoles
      {
        Anonymous = 0,
        User = 1,
        MayPrint = 2,
        Admin = 4
      }
      
      [RoleValidatorAspect] // to usse this aspect you have to implement and register the IRoleManager service
      public interface IModule
      {
        [RequiredRoles(MyRoles.User | MyRoles.MayPrint, MyRoles.Admin)]
        void Print();
        [RequiredRoles(MyRoles.User | MyRoles.MayPrint, MyRoles.Admin)]
        Task<string> PrintAsync();
        [RequiredRoles(MyRoles.Anonymous)]
        void Login();
        void MissingRequiredRoleAttribute(); // will throw since there is not RequiredRoles attribute
      }	 
      
      • LoggerAspectAttribute:
      [ModuleLoggerAspect]
      public interface IModule
      {
        void DoSomething(string arg1, object arg2);
        [Loggers(typeof(ExceptionLogger), typeof(StopWatchLogger))] // overrides the default loggers
        void DoSomethingElse();
      }
      // the above is a shorthand for:
      [LoggerAspect(typeof(ModuleMethodScopeLogger), typeof(ExceptionLogger), typeof(ParameterLogger), typeof(StopWatchLogger))]  // this sets the default loggers
      public interface IModule
      {
        void DoSomething(string arg1, object arg2);
        [Loggers(typeof(ExceptionLogger), typeof(StopWatchLogger))] // overrides the default loggers
        void DoSomethingElse();
      }
      

      Note that these aspects are naked

    These attributes are provided by the RPC.NET.Interfaces package.

  3. Define and host your service:

    using System;
    using System.Linq;   
    using Microsoft.Extensions.Logging;
    using Solti.Utils.DI.Interfaces;
    using Solti.Utils.Rpc.Hosting;
    using Solti.Utils.Rpc.Interfaces;
    
    public class AppHost : AppHostBase
    {
      public AppHost() => Name = "Calculator";
    
      public override void OnBuildService(RpcServiceBuilder serviceBuilder) => serviceBuilder
        .ConfigureWebService(new WebServiceDescriptor
        {
          Url = "http://localhost:1986/api/",
          AllowedOrigins = new[] 
          {
            "http://localhost:1987"
          }
        })
        .ConfigureServices(services => services.Factory<ILogger>(i => ConsoleLogger.Create<AppHost>(), Lifetime.Singleton))
        .ConfigureModules(modules => modules.Register<ICalculator, Calculator>());
    }
    
  4. Create the service exe:

    using System;
    using Solti.Utils.DI;
    using Solti.Utils.Rpc;
    
    class Program
    {
      static void Main(string[] args) => HostRunner.Run<AppHost>();
    }
    
  5. The compiled executable can be used in several ways:

    • You can simply run it to debug your app (Ctrl-C terminates the server)
    • You can invoke it with -install to install your app as a local service (-uninstall does the opposite)
    • It can run as a local service (started by SCM) - if it was installed previously

How to listen on HTTPS (Windows only)

Requires this script to be loaded (.(".\cert.ps1"))

  1. If you don't have your own, create a self-signed certificate
    Create-SelfSignedCertificate -OutDir ".\Cert" -Password "cica"
    
  2. Register the certificate
    Bind-Certificate -P12Cert ".Cert\certificate.p12" -Password "cica" -IpPort "127.0.0.1:1986"
    

Client example

  1. Install the RPC.NET.Client package.
  2. Reference the assembly that contains the module interface you want to use.
  3. Create the client:
    using Solti.Utils.Rpc;
    ...
    using var factory = new RpcClientFactory("http://127.0.0.1:1986/api/");
    ICalculator calculator = await factory.CreateClient<ICalculator>();
    try
    {
      int result = await calculator.AddAsync(1, 2);
    } catch(RpcException ex) {
      // ex.InnerException will contain the original exception
    }
    

JS client example

See here

Resources

API docs

Version history

Server boilerplate (comprehensive)

Sample server (used in tests)

Tests (remote module invocation related)

Benchmark results

NuGet packages (2)

Showing the top 2 NuGet packages that depend on RPC.NET.Interfaces:

Package Downloads
RPC.NET.Server

SDK designed for building lightweight RPC servers.

RPC.NET.Client

Lightweight client to invoke RPC services built with RPC.NET

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
5.0.2 118 12/22/2021
5.0.1 237 12/18/2021
5.0.0 151 12/12/2021
5.0.0-preview3 53 12/4/2021
5.0.0-preview2 151 10/29/2021
5.0.0-preview1 142 10/29/2021
4.0.1 224 7/2/2021
4.0.0 217 7/1/2021
3.0.4 228 6/29/2021
3.0.3 214 5/25/2021
3.0.2 247 4/8/2021
3.0.1 252 4/2/2021
3.0.0 280 3/20/2021
3.0.0-preview3 130 3/9/2021
3.0.0-preview2 149 3/8/2021
3.0.0-preview1 146 2/22/2021
2.2.0 286 10/16/2020
2.1.1 288 10/12/2020
2.1.0 301 9/25/2020
2.0.0 283 9/8/2020
2.0.0-preview4 221 8/25/2020
2.0.0-preview3 230 8/17/2020
2.0.0-preview2 228 8/8/2020
2.0.0-preview1 169 8/8/2020