Proxy.Grpc
8.0.4
See the version list below for details.
dotnet add package Proxy.Grpc --version 8.0.4
NuGet\Install-Package Proxy.Grpc -Version 8.0.4
<PackageReference Include="Proxy.Grpc" Version="8.0.4" />
paket add Proxy.Grpc --version 8.0.4
#r "nuget: Proxy.Grpc, 8.0.4"
// Install Proxy.Grpc as a Cake Addin #addin nuget:?package=Proxy.Grpc&version=8.0.4 // Install Proxy.Grpc as a Cake Tool #tool nuget:?package=Proxy.Grpc&version=8.0.4
NetRpc
NetRpc is a light weight rpc engine base on RabbitMQ, Grpc, Http targeting .NET 5.0/6.0/7.0/8.0. It use the simple interface to call each other, provide callback/cancel during invoking, so especially suitable for handle long running call.
NuGet
NetRpc for Grpc channel can be installed in your project with the following command.
PM> Install-Package NetRpc.Grpc
NetRpc also supports Http, RabbitMQ channel following packages are available to install:
PM> Install-Package NetRpc.Http
PM> Install-Package NetRpc.RabbitMQ
Others install:
//Jeager tracing function.
PM> Install-Package NetRpc.Jeager
//Http channel for client only.
PM> Install-Package NetRpc.Http.Client
Name | ||
---|---|---|
NetRpc.Grpc | ||
NetRpc.Http | ||
NetRpc.Http.Client | ||
NetRpc.RabbitMQ | ||
NetRpc.Jaeger |
Hello world
NetRpc for Grpc channel can be installed in your project with the following command.
PM> Install-Package NetRpc.Grpc
//service side
class Program
{
static async Task Main(string[] args)
{
var host = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel((_, options) =>
{
options.ListenAnyIP(50001, listenOptions => listenOptions.Protocols = HttpProtocols.Http2);
})
.ConfigureServices((_, services) =>
{
services.AddNGrpcService();
services.AddNServiceContract<IServiceAsync, ServiceAsync>();
}).Configure(app => { app.UseNGrpc(); });
}).Build();
await host.RunAsync();
}
}
public class ServiceAsync : IServiceAsync
{
public async Task<string> CallAsync(string s)
{
Console.WriteLine($"Receive: {s}");
return "call ret";
}
}
//client side
class Program
{
static async Task Main(string[] args)
{
//register
var services = new ServiceCollection();
services.AddNGrpcClient(options => options.Url = "http://localhost:50001");
services.AddNClientContract<IServiceAsync>();
services.AddLogging();
var buildServiceProvider = services.BuildServiceProvider();
//get service
var service = buildServiceProvider.GetService<IServiceAsync>();
Console.WriteLine("call: hello world.");
var ret = await service.CallAsync("hello world.");
Console.WriteLine($"ret: {ret}");
}
}
//datacontract is referenced by client and service
public interface IServiceAsync
{
Task<string> CallAsync(string s);
}
Code here: samples/HelloWorld.
Quick start: QuickRef
Overall
NetRpc provide RabbitMQ/Grpc/Http Channels to connect, each one has different advantages.
- RabbitMQ provide load balance, queue feature.
- Grpc use http2, provide all http2 advantages.
- Http use webapi, also provide swagger api.
All channels use uniform contract, so easily to switch channel without modify service implementation.
RabbitMQ/Grpc
There is message channel for RabbitMQ and Grpc, Http pls see topic blow.
Initialize by DI
There has two ways to initialize service and client, See DI sample below:
//service side
var host = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel((context, options) =>
{
options.ListenAnyIP(50001, listenOptions => listenOptions.Protocols = HttpProtocols.Http2);
})
.ConfigureServices((context, services) =>
{
services.AddNGrpcService();
services.AddNServiceContract<IServiceAsync, ServiceAsync>();
}).Configure(app =>
{
app.UseNGrpc();
});
}).Build();
//client side
//register
ServiceCollection services = new ServiceCollection();
services.AddNGrpcClient(options => options.Url = "http://localhost:50001");
services.AddNClientContract<IServiceAsync>();
services.AddLogging();
var buildServiceProvider = services.BuildServiceProvider();
//get service
var service = buildServiceProvider.GetService<IServiceAsync>();
var clientProxy = buildServiceProvider.GetService<IClientProxy<IServiceAsync>>();
//call remote
await service.CallAsync("hello world.");
await clientProxy.Proxy.CallAsync("hello world.");
If want to inject multiple ClientProxies, should use IClientProxyFactory.
//service side
services.Configure<RabbitMQClientOptions>("mq1", context.Configuration.GetSection("Mq1"));
services.Configure<RabbitMQClientOptions>("mq2", context.Configuration.GetSection("Mq2"));
services.AddNRabbitMQClient();
services.AddNRpcClientContract<IService>();
//client side
public class MyHost : IHostedService
{
private readonly IClientProxyFactory _factory;
public MyHost(IClientProxyFactory factory) //Get IClientProxyFactory here
{
_factory = factory;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
var clientProxy = _factory.CreateProxy<IService>("grpc1");
...
Serialization
RabbitMQ, Grpc channel base on BinaryFormatter, make sure all contract model mark as [Serializable].
[Serializable]
public class CustomObj
{
//...
}
[Important] When returned Custom object contains a Stream:
RabbitMQ, Grpc channel Stream mask as [field: NonSerialized].
Http channel Stream mask as [JsonIgnore].
Task<ComplexStream> GetComplexStreamAsync();
[Serializable]
public class ComplexStream
{
[field: NonSerialized] //add for RabbitMQ, Grpc channel
[JsonIgnore] //add for http channel
public Stream Stream { get; set; }
public string OtherInfo { get; set; }
}
Supported contract type
//Async
public interface IServiceAsync
{
Task<CustomObj> SetAndGetObj(CustomObj obj);
/// <exception cref="TaskCanceledException"></exception>
Task CallByCancelAsync(CancellationToken token);
Task CallByCallBackAsync(Func<CustomCallbackObj, Task> cb);
/// <exception cref="NotImplementedException"></exception>
Task CallBySystemExceptionAsync();
/// <exception cref="CustomException"></exception>>
Task CallByCustomExceptionAsync();
Task<Stream> GetStreamAsync();
Task SetStreamAsync(Stream data);
Task<Stream> EchoStreamAsync(Stream data);
Task<ComplexStream> GetComplexStreamAsync();
/// <exception cref="TaskCanceledException"></exception>
Task<ComplexStream> ComplexCallAsync(CustomObj obj, Stream data, Func<CustomCallbackObj, Task> cb, CancellationToken token);
}
GenericType
Make sure the genericType in contract is mark as [Serializable].
Task<T2> CallByGenericTypeAsync<T1, T2>(T1 obj);
Header
Header is a type of Dictionary<string, object> object, mark sure your object mark as [Serializable].
Before call action, client set the Header which mark as AsyncLocal that guarantee muti-threads don`t influence each other.
//client side
_proxy.AdditionHeader.Add("k1", "header value");
_proxy.TestHeader();
Service can receive the header object which client sent.
//service side
public void TestHeader()
{
var h = GlobalActionExecutingContext.Context.Header;
//...
}
- AdditionHeader
On the client side, when AdditionHeader is not null and items count is greater than 0, Header will get the value of AdditionHeader when call the remote. This feature is usefull when you want to transfer a sessionId to service.
//client side
//set the AdditionHeader with SessionId
client.Context.AdditionHeader = new Dictionary<string, object> {{"SessionId", 1}};
//will tranfer the header of SessionId to service.
client.Proxy.Call();
Context
On service side, Midderware or Filter can access ActionExecutingContext, it is
Property | Type | Description |
---|---|---|
Header | Dictionary<string object> | Header sent from client. |
Target | object | Service instance of invoked action. |
ChannelType | ChannelType | Enum value: Undefined, Grpc, RabbitMQ, Http. |
InstanceMethodInfo | MethodInfo | Current invoked method. |
ContractMethodInfo | MethodInfo | Current invoked contract method. |
ActionInfo | ActionInfo | Warpped info of current invoked method. |
Args | object[] | Args of invoked action. |
PureArgs | object[] | Args of invoked action without stream and action. |
Callback | Func<object, Task> | Callback of invoked action. |
Token | CancellationToken | CancellationToken of invoked action. |
Stream | Stream | Stream of invoked action. |
ServiceProvider | IServiceProvider | ServiceProvider of invoked action. |
Contract | Contract | Contract. |
MethodObj | MethodObj | MethodObj. |
Result | object | Result of invoked action. |
Properties | Dictionary<object, object> | A central location for sharing state between components during the invoking process. |
On client side, Midderware can access ClientActionExecutingContext, it is
Property | Type | Description |
---|---|---|
ServiceProvider | IServiceProvider | ServiceProvider of invoked action. |
Result | object | Result of invoked action. |
Header | Dictionary<string object> | Header sent from client. |
OptionsName | string | Config options name by DI. |
MethodInfo | MethodInfo | Current invoked method. |
Callback | Func<object, Task> | Callback of invoked action. |
Token | CancellationToken | CancellationToken of invoked action. |
ContractInfo | ContractInfo | ContractInfo. |
MethodObj | MethodObj | MethodObj. |
Stream | Stream | Stream of invoked action. |
PureArgs | object[] | Args of invoked action without stream and action. |
Properties | Dictionary<object, object> | A central location for sharing state between components during the invoking process. |
Filter
Filter is common function like MVC.
//service side
public class TestFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
Console.Write($"TestFilter.Execute(), context:{context}");
}
}
internal class Service : IService
{
[TestFilter]
public void Test()
{
//...
}
}
NetRpc Middleware
The way use NetRpc Middleware and use MVC Middleware is same, the only difference is use RpcContext instead of HttpContext.
Support DI Type and ctor args.
//servcie
services.AddNRpcMiddleware(i => i.UseMiddleware<TestGlobalExceptionMiddleware>("arg1value"));
public class TestGlobalExceptionMiddleware
{
private readonly RequestDelegate _next;
public TestGlobalExceptionMiddleware(RequestDelegate next, string arg1, DIType diType)
{
_next = next;
Console.WriteLine($"{arg1}");
}
public async Task InvokeAsync(RpcContext context, DIType diType)
{
try
{
await _next(context);
}
catch (Exception e)
{
Console.WriteLine($"[log by Middleware] {e.GetType().Name}");
throw;
}
}
}
Client side also support Middleware.
public class ClientOpenTracingMiddleware
{
private readonly ClientRequestDelegate _next;
public ClientOpenTracingMiddleware(ClientRequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(ClientContext context, ITracer tracer)
{
...
Retry
[ClientRetry(1000, 2000, 3000)] // retry in 1000ms, 2000ms, 3000ms
public interface IServiceAsync
{
//[ClientRetry(1000, 2000, 3000)] or set here.
Task CallAsync(string s);
}
...
Load Balance
Only for RabbitMQ.
When run multiple service instances, ther service will auto apply the load balance, this function is base on the RabbitMQ.
MQOptions.PrefetchCount: The service will acquire more messages, up to the PrefetchCount limit, defalut value is 1.
FaultException<T>
FaultException is
Property | Type | Description |
---|---|---|
Detail | Exception | Threw exception, will save the orginal StackTrace when Exception via remote transfer. |
Action | string | Invoked action name and args, if many actions split by '|'. |
//service side
internal class ServiceAsync : IServiceAsync
{
public Task CallBySystemExceptionAsync()
{
throw new NotImplementedException();
}
}
//client side
try
{
await proxy.CallBySystemExceptionAsync();
}
catch (FaultException<NotImplementedException> e)
{
//e.Detail.StackTrace will get the orginal info. (http channel do not support)
}
catch (FaultException e2)
{
}
catch (OperationCanceledException e2)
{
}
catch (TaskCanceledException e2)
{
}
Cancel
/// <exception cref="TaskCanceledException"></exception>
Task CallByCancelAsync(CancellationToken token);
Call back
Task CallByCallBackAsync(Func<CustomCallbackObj, Task> cb);
Built-in CallbackThrottlingMiddleware is useful when callback is progress, normally progress do not need callback every time to client, also for saving network resources.
//service side
services.AddNRpcMiddleware(i => i.UseCallbackThrottling(1000)); //limit to one call per second
//or this:
services.AddNRpcCallbackThrottling(1000);
...
public async Task Call(Func<int, Task> cb)
{
for (int i = 0; i <= 20; i++)
{
await Task.Delay(100);
await cb(i);
Console.WriteLine($"Send callback: {i}");
}
}
//client side
await proxy.Call(i => Console.WriteLine($"receive callback: {i}"));
//service sent 20 callbacks by interval 100ms.
Send callback: 0
Send callback: 1
Send callback: 2
Send callback: 3
Send callback: 4
Send callback: 5
Send callback: 6
Send callback: 7
Send callback: 8
Send callback: 9
Send callback: 10
Send callback: 11
Send callback: 12
Send callback: 13
Send callback: 14
Send callback: 15
Send callback: 16
Send callback: 17
Send callback: 18
Send callback: 19
Send callback: 20 //at the end will force send last callback
//----------------------------------------------------
//client only received 4 callbacks by interval 1000ms
receive callback: 0
receive callback: 8
receive callback: 17
receive callback: 20 //will receive last callback.
Stream
Task<Stream> GetStreamAsync();
Task SetStreamAsync(Stream data);
Task<Stream> EchoStreamAsync(Stream data);
Task<ComplexStream> GetComplexStreamAsync();
[Serializable]
public class ComplexStream
{
[field: NonSerialized]
public Stream Stream { get; set; }
public string OtherInfo { get; set; }
}
MQPostAttribute
Only for RabbitMQ channel, means post way to call, after sent message to rabbitMQ then return immediately, consumer will consum messages in queue asynchronous.
Post method define has some limits, no callback Action, no cancelToken, no return value.
[MQPost]
Task PostAsync(string s1, Stream stream);
IgnoreAttribute
- GrpcIgnoreAttribute
- RabbitMQIgnoreAttribute
- HttpIgnoreAttribute
[GrpcIgnore]
Task CallAsync(call);
Client Event
ClientProxy has events:
- ExceptionInvoked it usefull when you want to log the exception when call.
- Heartbeat see topic below.
- Connected invoked when conntect the service.
- DisConnected invoked when disconntect the service. if Heartbeat faild will invoke too.
clientProxy.Connected += (s, e) => Console.WriteLine("[event] Connected");
clientProxy.DisConnected += (s, e) => Console.WriteLine("[event] DisConnected");
clientProxy.ExceptionInvoked += (s, e) => Console.WriteLine("[event] ExceptionInvoked");
clientProxy.Heartbeat += async s => s.Proxy.Hearbeat();
Hearbeat
ClientProxy has a Heartbeat function after you call StartHeartbeat(), the interval is 10 seconds by default.
Client should register the Heartbeat event and implementation of heartbeat.
According to Heartbeat is successfull or faild, Connected or DisConnected will invoke correspondingly.
//client set the heartbeat interval to 10*1000
clientProxy.Heartbeat += async s => s.Proxy.Hearbeat();
clientProxy.StartHeartbeat(true);
Options support realtime update
Normally four options below support realtime update, if options has changed, will reset underlying service use the new options, do not need restart process to take options effect.
- GrpcServiceOptions
- GrpcClientOptions
- RabbitMQServiceOptions
- RabbitMQClientOptions
Note: only for DI mode:
services.AddNGrpcService(i => i.AddPort("0.0.0.0", 50001));
Gateway
Gateway has many advantages:
- Convert Channel.
- Provide exception handle.
- Provide access authority.
- ...
The code blow show how to Receive message from RabbitMQ channel client and send to Grpc channel service.
//set single target by DI.
services.AddNRabbitMQService(i => i.Value = TestHelper.Helper.GetMQOptions());
services.AddNRpcGateway<IService>(o => o.Channel = new Channel("localhost", 50001, ChannelCredentials.Insecure));
services.AddNRpcGateway<IService2>();
Also privode middleware in the gateway service, can add access authority if needed.
OpenTracing
Set the tags relate to method, it contains:
- Name
- Params
- Result
- Exception
For more details pls go to samples/OpenTracing
Get HttpContext
Use IHttpContextAccessor by services.AddHttpContextAccessor();
[Http] NetRpc.Http
NetRpc.Http provide:
- Webapi for call api.
- Swagger for display and test api.
- SignalR for callback and cancel during method invoking.
Note:
- Swagger is not necessary.
- Mvc is not necessary.
[Http] Create Host
Use DI to create NHttp service, also could create NHttp service base on exist MVC servcie.
//regist services
services.AddSignalR(); // add SignalR service
services.AddNRpcSwagger(); // add Swgger service
services.AddNHttp(i => // add RpcHttp service
{
i.ApiRootPath = "/api";
i.IgnoreWhenNotMatched = false;
});
services.AddNRpcMiddleware(i => i.UseMiddleware<MyNRpcMiddleware>()); // define NetRpc Middleware
services.AddNRpcServiceContract(instanceTypes); // add Contracts
//use components
app.UseSignalR(routes => { routes.MapHub<CallbackHub>(hubPath); }); // define CallbackHub
app.UseNRpcSwagger(); // use NRpcSwagger middleware
app.UseNHttp(); // use NHttp middleware
[Http] Swagger
Use Swashbuckle.AspNetCore.Swagger to implement swagger feature.
Note: swagger need add Http channel, swagger api path is [HttpServiceOptions.ApiRootPath]/swagger, if apiRootPath is "api", should like http://localhost:5000/api/swagger, if apiRootPath is null, should like http://localhost:5000/swagger.
Add codes below to enabled swagger function.
services.AddNRpcSwagger(); // add Swgger service
...
app.UseNRpcSwagger(); // use NRpcSwagger middleware
The demo show how to call a method with callback and cancel:
If define Callback Func<T, Task> and CancelToken supported, need set _connId and _callId when request. OperationCanceledException will receive respones with statuscode 600.
Also support summary on model or method.
HttpServiceOptions
/// <summary>
/// Api root path, like '/api', default value is null.
/// </summary>
public string? ApiRootPath { get; set; }
/// <summary>
/// Set true will pass to next middleware when not match the method, default value is false.
/// </summary>
public bool IgnoreWhenNotMatched { get; set; }
/// <summary>
/// ShortConn redis connection string.
/// </summary>
public string? ShortConnRedisConnStr { get; set; }
/// <summary>
/// ShortConn task stream file temp dir.
/// </summary>
public string? ShortConnTempDir { get; set; }
[Http] Callback and Cancel
Contract define the Func<T, Task> and CancellationToken to enable this feature.
Task CallAsync(Func<int, Task> cb, CancellationToken token);
Client code belows show how to get connectionId, how to receive callback, how to cancel a method.
//client js side
var connection = new signalR.HubConnectionBuilder().withUrl("{hubUrl}").build();
//GetConnId function
connection.start().then(function () {
addText("signalR connected!");
connection.invoke("GetConnId").then((cid) => {
addText("GetConnId, _connId:" + cid);
});
}).catch(function (err) {
return console.error(err.toString());
});
//Callback
connection.on("Callback", function (callId, data) {
addText("callback, callId:" + callId + ", data:" + data);
});
//Cancel
document.getElementById("cancelBtn").addEventListener("click", function (event) {
connection.invoke("Cancel").catch(function (err) {
return console.error(err.toString());
});
event.preventDefault();
});
[Http] Short Connection
Convert long running call to short call with start prog cancel replace interface.
ref: samples/ShortConn
[Http] FaultExceptionAttribute
If contract has Exception defined, should use FaultExceptionAttribute to define statuscode, use response code to define summary(will display in Swagger), otherwise NetRpc will use statuscode 400 to define all Exception by default.
[FaultException(typeof(CustomException), 400, 1, "errorCode1 error description")]
[FaultException(typeof(CustomException2), 400, 2, "errorCode2 error description")]
Task CallByCustomExceptionAsync();
//Also can put here, will effect to all methods in IServiceAsync.
[FaultException(typeof(CustomException2), 400, 2, "errorCode2 error description")]
public interface IServiceAsync
[Http] Method Attribute
Support some Attributes, pls see demo.
[HttpTrimAsync]
[HttpRoute("IRout1")]
[Tag("RoutTag1")]
public interface IService2Async
{
[Tag("CallTag1")]
[HttpPost]
[HttpRoute("Call1/{p1}")]
[HttpGet("/Root/Call/{p1}")]
[HttpTrimAsync]
Task<string> Call1Async(string p1, int p2);
[HttpGet]
[HttpDelete]
[HttpHead]
[HttpPut]
[HttpPatch]
[HttpOptions]
[HttpGet("Call2/{P1}/{P2}/Get")]
[HttpPost("Call2/{P1}/Post")]
Task<string> Call2Async(CallObj obj);
[HttpGet("Call3/{P1}?vp2={P2}")]
Task<string> Call3Async(CallObj obj);
}
[Serializable]
public class CallObj
{
public string P1 { get; set; }
public int P2 { get; set; }
}
[Http] JsonParamName Attribute
public interface IService4Async
{
[HttpPost("Call")]
Task<Obj4> Call([JsonParamName("i-d")] Obj4 id, [JsonParamName("test-red")] string testRed);
}
[Http] QureyRequired Attribute
public interface IService4Async
{
[HttpGet("Call")]
Task<Obj4> Call([QureyRequired] string s);
}
public interface IService4Async
{
[HttpGet("Call")]
Task<Obj4> Call(Obj o);
}
public class Obj
{
[QureyRequired]
public string S1 {get;set;}
}
[Http] Obsolete Attribute
[Obsolete]
[HttpRoute("IRout1", obsolete: true)]
[HttpRoute("IRout2")]
public interface IService4Async
{
//[Obsolete]
[HttpGet(obsolete: true)]
[HttpGet("Call/{id}")]
Task Call(string id);
}
[Http] ValueFilter Attribute
public class V1FilterAttribute : ValueFilterAttribute
{
public override Task InvokeAsync(ValueContext context, IServiceProvider serviceProvider)
{
Console.WriteLine($"value:{context.Value}");
return Task.CompletedTask;
}
}
public class Obj5
{
[Trim] // TrimAttribute will trim string.
[V1]
public string TaskId { get; set; }
public Obj51 Obj51 { get;set; }
}
[Validate] //declare validate
public class Obj51
{
[V1]
public string TaskId { get; set; }
}
public interface IService
{
public Task T1([V1]string s1)
public Task T2(Obj5 obj5)
}
// add ValidateMiddleware
services.AddNMiddleware(i => i.UseMiddleware<ValueFilterMiddleware>());
[Http] Auth
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(opt =>
{
opt.Audience = xxx;
opt.Authority = xxx;
opt.RequireHttpsMetadata = false;
opt.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
};
opt.IncludeErrorDetails = true;
});
[SecurityApiKeyDefine("auth", "Authorization", "auth2.0 token, e.g: Bearer xxxx...")]
public interface IService4Async
{
[SecurityApiKey("auth")]
Task<string> Call(string id);
}
public class Service4Async : IService4Async
{
[AuthToken]
public async Task<string> Call(string id)
{
return "call";
}
}
[Http] Role Attribute
Add a param key 'k' to url:
http://localhost:5000/swagger/index.html?k=k1
will filter the interface in Swagger UI, how to config is blow:
//servcie side
services.AddNSwagger(i =>
{
i.Items.Add(new KeyRole
{
Key = "k1",
Role = "R1"
});
i.Items.Add(new KeyRole
{
Key = "k2",
Role = "R2,R3"
});
i.Items.Add(new KeyRole
{
Key = "kall",
Role = "RAll"
});
});
[Role("RAll")]
public interface IService3Async
{
[SwaggerRole("R1,!RAll")] //!RALL mean exclude RALL
Task CallAsync();
[SwaggerRole("R1")]
[SwaggerRole("R3")]
Task Call2Async();
[SwaggerRole("R2,R3")]
Task Call3Async();
Task Call4Async();
}
Defalut role don't have to set the key:
http://localhost:5000/swagger/index.html would use the default role.
//[SwaggerRole("default")] this line can be omitted.
public interface IService4Async
{
[SwaggerRole("!default")] //hide in swagger, but still avaliable to call
Task Call(string id);
}
[Http] FaultExceptionDefineGroup InheritedFaultExceptionDefine HideFaultExceptionDescription Attribute
Add a header field to swagger.
public sealed class FaultExceptionDefineGroupAttribute : Attribute, IFaultExceptionGroup
{
public List<FaultExceptionDefineAttribute> FaultExceptionDefineAttributes =>
new()
{
new(typeof(CustomException), 400, 1, "errorCode1 error description"),
new(typeof(CustomException2), 400, 2, "errorCode2 error description", true)
};
}
[FaultExceptionDefineGroup] //declear a group
[InheritedFaultExceptionDefine] //pass fault define to methods
[HideFaultExceptionDescription] //is Hide fault decription in swagger?
public interface IService4Async
{
//[HideFaultExceptionDecription]
[HttpGet("Call")]
Task<string> Call(string id);
[HttpPost("Call")]
Task<string> Call2(string id);
}
[Http] HttpHeaderAttribute
Add a header field to swagger.
public interface IServiceAsync
{
[HttpHeader("h2", "h2 descrption.")]
Task CallAsync();
[Http] ResponseTextException
ResponseTextException define pain text response with statucode.
public async Task CallByResponseTextExceptionAsync()
{
throw new ResponseTextException("this is customs text. statucode is 701.", 701);
}
Also should use response code define summary, it will display in swagger.
/// <response code="701">return the pain text.</response>
[ResponseText(701)]
Task CallByResponseTextExceptionAsync();
[Http] StreamName
Normally stream of return value will map to filestream, if you define StreamName property, will set to file name to client.
//stream of return value
Task<ComplexStream> GetComplexStreamAsync();
...
public class ComplexStream
{
public Stream Stream { get; set; }
public string StreamName { get; set; } //the property will map to file name.
}
StreamName also apply to input params.
//Content-Disposition: form-data; name="stream"; filename="t1.docx"
//mapping filename to streamname
Task UploadAsync(Stream stream, string streamName);
[Http] HttpImages
public interface IServiceAsync
{
//default Content-Type "image/jepg"
[HttpImages()]
Task<Stream> CallAsync();
Means return type is images to show in browser, not a download file.
public class ComplexStream
{
public Stream Stream { get; set; }
//affect Content-Type, 1.png -> "image/png", 1.jpg -> "image/jepg"
public string StreamName { get; set; }
}
public interface IServiceAsync
{
[HttpImages()]
Task<ComplexStream> CallAsync();
[Http] Example
Set Example to contract, will effect to swagger.
[Example("s1", "s1value")]
[Example("s2", "s2value")]
Task<CustomObj> Call2Async(CObj obj, string s1, string s2);
[Example(null)]
public int? I1 { get; set; }
Others
- An contract args can only contains one Func<T, Task>, one Stream, same as return value.
ComplexStream Call(Stream data, Func<CustomCallbackObj, Task> cb);
- TimeoutInterval of call is a mechanism of NetRpc owns, it do not use the Grpc or RabbitMQ timeout mechanism.
CreateClientProxy<TService>(Channel channel, int timeoutInterval = 1200000)
Samples
- samples/HelloWorld Quick start.
- samples/Api Contains most of features.
- samples/Http Http webapi and swagger api.
- samples/LoadBalance RabbitMQ load balance and post way to call.
- samples/InitializeByDI Use DI to create a client or servcie and how to DI a http channel to exist MVC service.
- samples/InitializeByDIFactory Use ClientProxyFactory to get multiple ClientProxies.
- samples/CallbackThrottling It useful when callback is progress.
- samples/Gateway Gateway for NetRpc.
- samples/OpenTracing OpenTracing for NetRpc.
- samples/Retry Retry for NetRpc.
- samples/ShortConn ShortConn for NetRpc.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. 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. |
-
net8.0
- Google.Protobuf (>= 3.28.2)
- Grpc.Net.Client (>= 2.66.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Proxy.Grpc:
Package | Downloads |
---|---|
NetRpc.Grpc
Provide grpc channel implementation for NetRpc. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
8.0.9 | 0 | 11/30/2024 |
8.0.8 | 34 | 11/29/2024 |
8.0.7 | 35 | 11/28/2024 |
8.0.6 | 86 | 11/18/2024 |
8.0.5 | 82 | 11/1/2024 |
8.0.4 | 80 | 11/1/2024 |
8.0.3 | 85 | 11/1/2024 |
8.0.2 | 84 | 11/1/2024 |
8.0.1 | 85 | 11/1/2024 |
8.0.0 | 241 | 10/11/2024 |
7.0.31 | 150 | 4/18/2024 |
7.0.30 | 900 | 12/27/2022 |
7.0.29 | 662 | 12/27/2022 |
7.0.28 | 700 | 12/27/2022 |
7.0.27 | 641 | 12/27/2022 |
7.0.26 | 691 | 12/13/2022 |
7.0.25 | 723 | 12/8/2022 |
7.0.24 | 706 | 12/8/2022 |
7.0.23 | 768 | 12/2/2022 |
7.0.22 | 702 | 12/2/2022 |
7.0.21 | 767 | 12/2/2022 |
7.0.20 | 714 | 12/1/2022 |
7.0.19 | 727 | 12/1/2022 |
7.0.18 | 785 | 12/1/2022 |
7.0.17 | 697 | 11/29/2022 |
7.0.16 | 706 | 11/29/2022 |
7.0.15 | 709 | 11/29/2022 |
7.0.14 | 769 | 11/28/2022 |
7.0.13 | 671 | 11/28/2022 |
7.0.12 | 753 | 11/28/2022 |
7.0.11 | 802 | 11/28/2022 |
7.0.10 | 811 | 11/28/2022 |
7.0.9 | 772 | 11/26/2022 |
7.0.8 | 753 | 11/26/2022 |
7.0.7 | 733 | 11/26/2022 |
7.0.6 | 750 | 11/25/2022 |
7.0.5 | 772 | 11/25/2022 |
7.0.4 | 718 | 11/25/2022 |
7.0.3 | 793 | 11/25/2022 |
7.0.2 | 810 | 11/22/2022 |
7.0.1 | 746 | 11/22/2022 |
7.0.0 | 780 | 11/11/2022 |
6.1.17 | 315 | 2/1/2024 |
6.1.15 | 2,778 | 6/21/2022 |
6.1.14 | 1,053 | 6/20/2022 |
6.1.13 | 983 | 6/6/2022 |
6.1.8 | 1,013 | 5/23/2022 |
6.1.7 | 917 | 5/23/2022 |
6.1.6 | 985 | 5/20/2022 |
6.1.5 | 1,016 | 5/18/2022 |
6.1.4 | 1,022 | 5/16/2022 |
6.1.3 | 998 | 5/12/2022 |
6.1.1 | 991 | 5/6/2022 |
6.1.0 | 1,008 | 5/5/2022 |
6.1.0-rc3 | 585 | 4/12/2022 |
6.0.24 | 1,027 | 3/22/2022 |
6.0.20 | 969 | 3/22/2022 |
6.0.19 | 999 | 3/16/2022 |
6.0.14 | 999 | 3/9/2022 |
6.0.12 | 992 | 3/9/2022 |
6.0.10 | 1,016 | 3/7/2022 |
6.0.9 | 1,051 | 3/4/2022 |
6.0.8 | 1,039 | 1/26/2022 |
6.0.7 | 1,043 | 1/24/2022 |
6.0.4 | 807 | 12/14/2021 |
6.0.1 | 825 | 12/9/2021 |
6.0.0 | 855 | 12/1/2021 |
6.0.0-beta2 | 631 | 10/28/2021 |
6.0.0-beta1 | 674 | 10/14/2021 |
5.0.48 | 893 | 11/1/2021 |
5.0.47 | 939 | 10/28/2021 |
5.0.46 | 896 | 9/30/2021 |
5.0.45 | 900 | 9/27/2021 |
5.0.44 | 987 | 7/7/2021 |
5.0.41 | 921 | 6/22/2021 |
5.0.40 | 947 | 6/21/2021 |
5.0.38 | 938 | 5/14/2021 |
5.0.32 | 2,956 | 3/17/2021 |
5.0.31 | 986 | 3/15/2021 |
5.0.29 | 982 | 2/23/2021 |
5.0.28 | 1,031 | 2/3/2021 |
5.0.27 | 929 | 2/2/2021 |
5.0.26 | 940 | 1/29/2021 |
5.0.25 | 960 | 1/26/2021 |
5.0.24 | 965 | 1/26/2021 |
5.0.23 | 1,082 | 1/21/2021 |
5.0.22 | 982 | 1/21/2021 |
5.0.21 | 990 | 1/20/2021 |
5.0.20 | 1,001 | 1/19/2021 |
5.0.19 | 955 | 1/15/2021 |
5.0.15 | 1,102 | 1/6/2021 |
5.0.0-rc3 | 790 | 11/19/2020 |
5.0.0-rc1 | 757 | 11/11/2020 |
3.1.13 | 1,287 | 9/29/2020 |
3.1.12 | 1,207 | 9/23/2020 |
3.1.10 | 1,166 | 9/22/2020 |
3.1.9 | 1,424 | 9/17/2020 |
3.1.7 | 1,197 | 9/15/2020 |
3.1.6 | 1,166 | 9/10/2020 |
3.1.5 | 1,128 | 9/10/2020 |
3.1.4 | 1,147 | 9/9/2020 |
3.1.3 | 1,212 | 9/1/2020 |
3.1.2 | 1,125 | 8/24/2020 |
3.1.1 | 1,182 | 8/23/2020 |
3.1.0 | 1,204 | 8/23/2020 |
3.0.16 | 1,175 | 8/21/2020 |
3.0.15 | 1,191 | 8/20/2020 |
3.0.12 | 1,210 | 8/20/2020 |
3.0.11 | 1,138 | 8/20/2020 |
3.0.10 | 1,159 | 8/19/2020 |
3.0.9 | 1,170 | 8/12/2020 |
3.0.8 | 1,202 | 8/6/2020 |
3.0.7 | 1,184 | 8/5/2020 |
2.6.9 | 1,289 | 6/10/2020 |
2.6.8 | 1,256 | 6/5/2020 |
2.6.6 | 1,207 | 6/5/2020 |
2.6.3 | 1,271 | 6/4/2020 |
2.6.2 | 1,307 | 6/3/2020 |
2.5.21 | 1,256 | 5/11/2020 |
2.5.20 | 1,218 | 4/24/2020 |
2.5.19 | 1,193 | 4/23/2020 |
2.5.18 | 1,171 | 4/23/2020 |
2.5.17 | 1,229 | 4/22/2020 |
2.5.14 | 1,223 | 4/20/2020 |
2.5.12 | 1,233 | 4/14/2020 |
2.5.11 | 1,239 | 4/13/2020 |
2.5.5 | 1,335 | 4/3/2020 |
2.5.4 | 1,290 | 3/30/2020 |
2.5.1 | 1,223 | 3/23/2020 |
2.5.0 | 1,408 | 3/12/2020 |
2.4.7 | 1,283 | 1/2/2020 |
2.4.3 | 1,294 | 12/27/2019 |
2.3.0 | 1,171 | 3/12/2020 |
2.2.37 | 1,279 | 11/11/2019 |
2.2.36 | 1,303 | 11/11/2019 |
2.2.35 | 1,264 | 11/8/2019 |
2.2.34 | 1,209 | 11/7/2019 |
2.2.33 | 1,255 | 11/7/2019 |
2.2.32 | 1,205 | 11/6/2019 |
2.2.31 | 1,263 | 11/6/2019 |
2.2.30 | 1,195 | 11/6/2019 |
2.2.29 | 1,244 | 11/6/2019 |
2.2.28 | 1,278 | 11/5/2019 |
2.2.27 | 1,279 | 11/1/2019 |
2.2.26 | 1,298 | 11/1/2019 |
2.2.25 | 1,250 | 11/1/2019 |
2.2.24 | 1,235 | 10/30/2019 |
2.2.23 | 1,282 | 10/30/2019 |
2.2.22 | 1,304 | 10/29/2019 |
2.2.21 | 1,269 | 10/29/2019 |
2.2.20 | 1,244 | 10/29/2019 |
2.2.19 | 1,258 | 10/28/2019 |
2.2.18 | 1,317 | 10/25/2019 |
2.2.17 | 1,270 | 10/25/2019 |
2.2.16 | 1,251 | 10/25/2019 |
2.2.15 | 1,222 | 10/18/2019 |
2.2.14 | 1,232 | 10/17/2019 |
2.2.13 | 1,312 | 10/17/2019 |
2.2.12 | 1,276 | 10/17/2019 |
2.2.11 | 1,267 | 10/16/2019 |
2.2.10 | 1,231 | 10/16/2019 |
2.2.9 | 1,270 | 10/15/2019 |
2.2.8 | 1,261 | 10/15/2019 |
2.2.7 | 1,284 | 10/14/2019 |
2.2.6 | 1,298 | 10/14/2019 |
2.2.5 | 1,265 | 10/14/2019 |
2.2.4 | 1,287 | 10/14/2019 |
2.2.3 | 1,305 | 10/12/2019 |
2.2.2 | 1,324 | 10/12/2019 |
2.2.1 | 1,264 | 10/11/2019 |
2.1.21 | 1,369 | 9/24/2019 |
2.1.20 | 1,363 | 9/20/2019 |
2.1.19 | 1,341 | 9/18/2019 |
2.1.18 | 1,376 | 9/18/2019 |
2.1.16 | 1,358 | 9/18/2019 |
2.1.15 | 1,302 | 9/17/2019 |
2.1.14 | 1,243 | 9/17/2019 |
2.1.13 | 1,267 | 9/17/2019 |
2.1.12 | 1,303 | 9/12/2019 |
2.1.11 | 1,292 | 9/11/2019 |
2.1.10 | 1,301 | 9/11/2019 |
2.1.9 | 1,283 | 9/11/2019 |
2.1.8 | 1,257 | 9/10/2019 |
2.1.7 | 1,274 | 9/9/2019 |
2.1.6 | 1,267 | 9/9/2019 |
2.1.5 | 1,270 | 9/9/2019 |
2.1.4 | 1,305 | 9/9/2019 |
2.1.3 | 1,282 | 9/9/2019 |
2.1.2 | 1,308 | 9/6/2019 |
2.1.1 | 1,323 | 9/6/2019 |
2.1.0 | 1,312 | 9/6/2019 |
2.0.7 | 1,297 | 8/23/2019 |
2.0.6 | 1,298 | 8/23/2019 |
2.0.5 | 1,294 | 8/22/2019 |
2.0.4 | 1,296 | 8/22/2019 |
2.0.3 | 1,306 | 8/22/2019 |
2.0.2 | 1,290 | 8/22/2019 |
2.0.1 | 1,331 | 8/21/2019 |
2.0.0 | 1,335 | 8/21/2019 |
1.0.39 | 1,404 | 7/9/2019 |
1.0.38 | 1,318 | 7/1/2019 |
1.0.37 | 1,317 | 6/28/2019 |
1.0.36 | 1,287 | 6/18/2019 |
1.0.35 | 1,277 | 6/17/2019 |
1.0.34 | 1,330 | 6/13/2019 |
1.0.33 | 1,331 | 6/13/2019 |
1.0.32 | 1,279 | 6/12/2019 |
1.0.31 | 1,346 | 6/10/2019 |
1.0.30 | 1,412 | 6/10/2019 |
1.0.29 | 1,393 | 5/29/2019 |
1.0.28 | 1,467 | 5/22/2019 |
1.0.27 | 1,416 | 5/22/2019 |
1.0.26 | 1,579 | 5/14/2019 |
1.0.25 | 1,393 | 5/14/2019 |
1.0.23 | 1,431 | 5/14/2019 |
1.0.22 | 1,433 | 5/14/2019 |