RPC.NET.Server
5.0.0
See the version list below for details.
dotnet add package RPC.NET.Server --version 5.0.0
NuGet\Install-Package RPC.NET.Server -Version 5.0.0
<PackageReference Include="RPC.NET.Server" Version="5.0.0" />
paket add RPC.NET.Server --version 5.0.0
#r "nuget: RPC.NET.Server, 5.0.0"
// Install RPC.NET.Server as a Cake Addin #addin nuget:?package=RPC.NET.Server&version=5.0.0 // Install RPC.NET.Server as a Cake Tool #tool nuget:?package=RPC.NET.Server&version=5.0.0
RPC.NET
Simple, lightweight RPC implementation for .NET
This documentation refers the version 5.X of the library
Name | Package |
---|---|
RPC.NET.Interfaces | |
RPC.NET.Client | |
RPC.NET.Server | |
RPC.NET-Connector |
How it works
- 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]
.
- 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 isapplication/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:
or{ "Result": 12, "Exception": null }
{ "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 isapplication/octet-stream
and the response body contains the raw data.
- If the remote method has a non
Server example
Install the RPC.NET.Server package. Since modules are stored in a
IServiceCollection
you may need to install the Injector.NET package as well.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.
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>()); } }
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(); }
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")
)
- If you don't have your own, create a self-signed certificate
Create-SelfSignedCertificate -OutDir ".\Cert" -Password "cica"
- Register the certificate
Bind-Certificate -P12Cert ".Cert\certificate.p12" -Password "cica" -IpPort "127.0.0.1:1986"
Client example
- Install the RPC.NET.Client package.
- Reference the assembly that contains the module interface you want to use.
- 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
Server boilerplate (comprehensive)
Sample server (used in tests)
Tests (remote module invocation related)
Product | Versions 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. |
-
.NETStandard 2.0
- Injector.NET (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 5.0.0)
- RPC.NET.Interfaces (>= 5.0.0)
- Solti.Utils.Primitives (>= 6.1.2)
- System.ServiceProcess.ServiceController (>= 5.0.0)
- System.Text.Json (>= 5.0.2)
-
.NETStandard 2.1
- Injector.NET (>= 6.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 5.0.0)
- RPC.NET.Interfaces (>= 5.0.0)
- Solti.Utils.Primitives (>= 6.1.2)
- System.ServiceProcess.ServiceController (>= 5.0.0)
- System.Text.Json (>= 5.0.2)
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 | 221 | 2/13/2022 |
6.0.0-preview1 | 200 | 1/30/2022 |
5.0.2 | 401 | 12/22/2021 |
5.0.1 | 469 | 12/18/2021 |
5.0.0 | 342 | 12/12/2021 |
5.0.0-preview3 | 244 | 12/4/2021 |
5.0.0-preview2 | 245 | 10/29/2021 |
5.0.0-preview1 | 266 | 10/29/2021 |
4.0.1 | 407 | 7/2/2021 |
4.0.0 | 367 | 7/1/2021 |
3.0.4 | 411 | 6/29/2021 |
3.0.3 | 375 | 5/25/2021 |
3.0.2 | 404 | 4/8/2021 |
3.0.1 | 413 | 4/2/2021 |
3.0.0 | 454 | 3/20/2021 |
3.0.0-preview3 | 244 | 3/9/2021 |
3.0.0-preview2 | 285 | 3/8/2021 |
3.0.0-preview1 | 268 | 2/22/2021 |
2.2.0 | 436 | 10/16/2020 |
2.1.1 | 455 | 10/12/2020 |
2.1.0 | 465 | 9/25/2020 |
2.0.0 | 451 | 9/8/2020 |
2.0.0-preview4 | 330 | 8/25/2020 |
2.0.0-preview3 | 358 | 8/17/2020 |
2.0.0-preview2 | 346 | 8/8/2020 |
2.0.0-preview1 | 384 | 8/3/2020 |
1.0.0 | 556 | 7/19/2020 |
1.0.0-preview3 | 424 | 7/18/2020 |
1.0.0-preview2 | 348 | 7/14/2020 |
1.0.0-preview1 | 414 | 7/12/2020 |