RPC.NET.Client 5.0.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package RPC.NET.Client --version 5.0.1                
NuGet\Install-Package RPC.NET.Client -Version 5.0.1                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RPC.NET.Client" Version="5.0.1" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add RPC.NET.Client --version 5.0.1                
#r "nuget: RPC.NET.Client, 5.0.1"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install RPC.NET.Client as a Cake Addin
#addin nuget:?package=RPC.NET.Client&version=5.0.1

// Install RPC.NET.Client as a Cake Tool
#tool nuget:?package=RPC.NET.Client&version=5.0.1                

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(string[] args) : base(args) { }
    
      public override void OnConfigureWin32Service(Win32ServiceDescriptor descriptor)
      {
        base.OnConfigureWin32Service(descriptor);
    
        descriptor.Name = "Calculator";
      }
    
      public override void OnConfigure(RpcServiceBuilder serviceBuilder)
      {
        base.OnConfigure(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 int Main(string[] args) => new AppHost(args).Run();
    }
    
  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 service install to install your app as a local service (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

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
6.0.0-preview2 201 2/13/2022
6.0.0-preview1 188 1/30/2022
5.0.2 425 12/22/2021
5.0.1 301 12/18/2021
5.0.0 266 12/12/2021
5.0.0-preview3 207 12/4/2021
5.0.0-preview2 233 10/29/2021
5.0.0-preview1 230 10/29/2021
4.0.1 382 7/2/2021
4.0.0 354 7/1/2021
3.0.4 382 6/29/2021
3.0.3 359 5/25/2021
3.0.2 377 4/8/2021
3.0.1 384 4/2/2021
3.0.0 412 3/20/2021
3.0.0-preview3 249 3/9/2021
3.0.0-preview2 252 3/8/2021
3.0.0-preview1 251 2/22/2021
2.2.0 440 10/16/2020
2.1.1 437 10/12/2020
2.1.0 453 9/25/2020
2.0.0 451 9/8/2020
2.0.0-preview4 311 8/25/2020
2.0.0-preview3 312 8/17/2020
2.0.0-preview2 320 8/8/2020
2.0.0-preview1 338 8/3/2020
1.0.0 501 7/19/2020
1.0.0-preview3 410 7/18/2020
1.0.0-preview2 338 7/14/2020
1.0.0-preview1 381 7/12/2020