- A+
.NET 6 使用 MagicOnion
MagicOnion开源地址:https://github.com/Cysharp/MagicOnion
什么是MagicOnion?
MagicOnion 是用于 .NET 平台的现代 RPC 框架,它提供双向实时通信(如SignalR和Socket.io)和 RPC 机制(如 WCF 和基于 Web 的 API)。
此框架基于gRPC,它是用于 HTTP/2 的快速紧凑的二进制网络传输。但是,与普通 gRPC 不同,它将 C# 接口视为协议架构,从而在没有 C# 项目(协议缓冲区 IDL)的情况下实现无缝代码共享。.proto
MagicOnion快速入门 (创建服务器端项目)
首先,您需要从 Visual Studio 或 .NET CLI 工具中创建gRPC Service项目。MagicOnion Server 建立在 ASP.NET Core 和 gRPC 之上,因此服务器项目必须是 ASP.NET Core 项目。
创建gRPC服务器微软文档:在 ASP.NET Core 中创建 .NET Core gRPC 客户端和服务器 | Microsoft Learn
创建项目时,它包含 protos 文件夹,这个在 MagicOnion 中不需要所以可以删除。
将 NuGet 包添加到项目。MagicOnion.Server
当然也可以用开发者 PowerShell 命令安装命令如下:
dotnet add package MagicOnion.Server
因为我们用的是 .NET 6 框架没有 Startup.cs 文件,所以我们在 Program.cs 中添加
builder.Services.AddMagicOnion();
app.UseEndpoints
调用方法重写如下。
app.UseEndpoints(endpoints => { endpoints.MapMagicOnionService(); endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909"); }); });
完整的 Program.cs 文件如下:
using MyFirstMagicOnionServer.Services; var builder = WebApplication.CreateBuilder(args); // Additional configuration is required to successfully run gRPC on macOS. // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682 // Add services to the container. builder.Services.AddGrpc(); builder.Services.AddMagicOnion(); var app = builder.Build(); // Configure the HTTP request pipeline. app.MapGrpcService<GreeterService>(); app.UseRouting() // 添加 UseRouting app.UseEndpoints(endpoints => { endpoints.MapMagicOnionService(); app.MapGet("/", async context => { await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909"); }); }); app.Run();
ps:这边有个坑,运行之后会报一个错误:EndpointRoutingMiddleware matches endpoints setup by EndpointMiddleware and so must be added to the request execution pipeline before EndpointMiddleware. Please add EndpointRoutingMiddleware by calling 'IApplicationBuilder.UseRouting' inside the call to 'Configure(...)' in the application startup code.
意思就是说要在app.UseEndpoints()
方法前面加上app.UseRouting()
方法
在RPC服务端项目上使用MagicOnion
MagicOnion提供类似Web API的RPC服务和用于实时通信的StreamingHub。本节实现类似于 Web API 的 RPC 服务。添加要在服务器和客户端之间共享的接口(命名空间应与项目匹配)。
创建共享库(共享库,客户端服务端都要有)
创建一个 Shared 文件夹 里面再创建一个接口文件 IMyFirstService
using MagicOnion; namespace MyFirstMagicOnionServer.Shared { // 将 .NET 接口定义为 Server/Client IDL. // 这接口在服务器和客户端之间共享 public interface IMyFirstService : IService<IMyFirstService> { // 该返回的类型必须是 UnaryResult<T> UnaryResult<int> SumAsync (int x,int y); } }
添加实现接口的类(服务端实现)
创建一个 Service 文件夹里面再创建一个实现类 MyFirstMagicOnionServer
using MagicOnion; using MagicOnion.Server; using MyFirstMagicOnionServer.Shared; namespace MyFirstMagicOnionServer.Services { public class MyFirstService : ServiceBase<IMyFirstService>, IMyFirstService { public async UnaryResult<int> SumAsync(int x, int y) { Console.WriteLine($"Received:{x}, {y}"); await Task.Run(() => true); return x + y; } } }
客户端:在MagicOnion上调用服务
创建控制台应用程序项目并将 NuGet 包添加到该项目。MagicOnion.Client
与客户端共享接口。以某种方式共享接口定义,例如文件链接、共享库或复制和粘贴。IMyFirstService
我这边采用复制粘贴的模式把 MagicOnion 服务端上的shared
文件夹复制到控制台程序(这边记得修改IMyFirstService
的命名空间)
在客户端代码中,共享接口上的 Createclient 代理并透明地调用服务。MagicOnionClient
using Grpc.Net.Client; using MagicOnion.Client; using MyFirstMagicOnionClient.Shared; // 引用 gRPC 的 channel 连接到服务器。 var channel = GrpcChannel.ForAddress("http://localhost:33559"); // 服务端运行的接口地址 // 创建一个代理来透明的调用服务器 IMyFirstService client = MagicOnionClient.Create<IMyFirstService>(channel); // 使用代理调用服务端的方法 var result = await client.SumAsync(123, 456); Console.WriteLine($"Result: {result}");
介绍 MagicOnion 的四个 NuGet 包
MagicOnion.Server
包来实现服务器。您需要安装此软件包才能在服务器上实现服务。MagicOnion.Client
包来实现客户端。若要实现 WPF 和 Xamarin 等客户端,需要安装此包。MagicOnion.Abstractions
该包提供服务器和客户端常用的接口和属性。若要创建在服务器和客户端之间共享的类库项目,需要安装此包。MagicOnion
包是元包,用于实现服务器和客户端的角色。 若要实现服务器到服务器的通信(如微服务),可以是服务器也可以是客户端,我们建议安装此包。
WebApi 使用 MagicOnion
- 创建一个 WebApi 项目(名字自取)
- 和上面控制台服务器一样添加 Nuget 包
MagicOnion.Server
- 在 Program.cs 里面添加:
builder.Services.AddGrpc(); builder.Services.AddMagicOnion(); app.UseRouting(); // 调用UseRouting() app.UseEndpoints(endpoints => { endpoints.MapMagicOnionService(); endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909"); }); });
附带完整的 Program.cs
using MagicOnion; using MagicOnion.Server; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); #region 添加的 MagicOnion builder.Services.AddGrpc(); builder.Services.AddMagicOnion(); #endregion var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseRouting(); // 调用UseRouting() app.UseAuthorization(); // 添加的 app.UseEndpoints(endpoints => { endpoints.MapMagicOnionService(); endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909"); }); }); app.MapControllers(); app.Run();
- 创建接口和实现类
创建接口:和上面gRPC一样 创建一个 Shared 文件夹,里面创建 IMyFirstService 接口文件
using MagicOnion; namespace MagicOnionWebApi.Shared { // 将 .NET 接口定义为 Server/Client IDL. // 这接口在服务器和客户端之间共享 public interface IMyFirstService : IService<IMyFirstService> { // 该返回的类型必须是 UnaryResult<T> UnaryResult<int> SumAsync (int x,int y); } }
实现接口:也和上面一样创建一个 Service 文件夹里面再创建一个实现类 MyFirstMagicOnionServer
public class MyFirstService : ServiceBase<IMyFirstService>, IMyFirstService { public async UnaryResult<int> SumAsync(int x, int y) { Console.WriteLine("服务端接受到请求"); Console.WriteLine($"Received:{x}, {y}"); await Task.Run(() => true); return x + y; } }
- 客户端调用:
用上面创建的 MagicOnion 客户端项目改一下请求地址就好了:
// 修改服务端的接口地址 var channel = GrpcChannel.ForAddress("https://localhost:7109"); // 服务端运行的接口地址