我们接着上一篇 (一) gRPC初探之协定优先方法进行 API 开发 来讲,基于proto协定优先的方式开发服务是在不限语言的基础上,当整个系统使用 .NET开发时,我们可以使用代码优先:
- 可以在 .NET 服务器和客户端之间共享 .NET 服务和数据协定类型
- 无需在 .proto 文件和代码生成过程中定义协定
protobuf-net.Grpc
基于 .NET版本的gRPC社区开源项目.通过属性注解的 .NET 类型来定义应用的 gRPC 服务和消息
项目结构
创建服务和数据协定类型
- 创建
GrpcService1.Shared
项目并添加protobuf-net.Grpc
包引用 - 创建出参/入参并定义接口
- 添加属性批注
namespace GrpcService1.Shared
{
using System.Runtime.Serialization;
[DataContract]
public class CreateOrderInput
{
[DataMember(Order = 1)]
public int BuyerId { get; set; }
[DataMember(Order = 2)]
public string BuyerName { get; set; }
[DataMember(Order = 3)]
public int OrderId { get; set; }
[DataMember(Order = 4)]
public string OrderName { get; set; }
}
}
namespace GrpcService1.Shared
{
using System.Runtime.Serialization;
[DataContract]
public class CreateOrderOutput
{
[DataMember(Order = 1)]
public int BuyerId { get; set; }
[DataMember(Order = 2)]
public string BuyerName { get; set; }
[DataMember(Order = 3)]
public int OrderId { get; set; }
[DataMember(Order = 4)]
public string OrderName { get; set; }
}
}
namespace GrpcService1.Shared
{
using ProtoBuf.Grpc;
using System.ServiceModel;
using System.Threading.Tasks;
[ServiceContract]
public interface IOrderService
{
[OperationContract]
Task<CreateOrderOutput> CreateOrderAsync(CreateOrderInput request, CallContext context = default);
}
}
创建代码优先gRPC服务
- 创建
GrpcService1
API - 添加
protobuf-net.Grpc.AspNetCore
包引用 - 添加
GrpcService1.Shared
项目引用 - 在项目路径添加
/Service/OrderService.cs
代码如下
namespace GrpcService1.Shared
{
using ProtoBuf.Grpc;
using System.Threading.Tasks;
public class OrderService : IOrderService
{
public async Task<CreateOrderOutput> CreateOrderAsync(CreateOrderInput request, CallContext context = default)
{
return await Task.FromResult(new CreateOrderOutput
{
BuyerId = request.BuyerId,
BuyerName = request.BuyerName,
OrderId = request.OrderId,
OrderName = request.OrderName,
});
}
}
}
- 配置
Startup.cs
namespace GrpcService1
{
using GrpcService1.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ProtoBuf.Grpc.Server;
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
// 关键代码
services.AddCodeFirstGrpc();//注册启用了代码优先的服务。
// 关键代码
services.AddLogging(options =>
{
options.AddConsole();
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
var log = app.ApplicationServices.GetService<ILoggerFactory>()?.CreateLogger(env.ApplicationName);
log.LogInformation("Hello world");
app.UseEndpoints(endpoints =>
{
// 关键代码
endpoints.MapGrpcService<OrderService>();//添加代码优先的服务终结点。
// 关键代码
//endpoints.MapGet("/", async context =>
// {
// await context.Response.WriteAsync("Hello world");
// });
});
}
}
}
创建代码优先gRPC客户端
- 创建 dotnetcore 控制台应用程序
- 添加
Grpc.Net.Client
包引用 - 添加
protobuf-net.Grpc
引用
namespace GrpcClient1
{
using Grpc.Net.Client;
using GrpcService1.Shared;
using ProtoBuf.Grpc.Client;
using System;
using System.Net.Http;
using System.Text.Json;
using System.Text.Unicode;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
// 使用不受信任/无效证书调用 gRPC 服务
var httpHandler = new HttpClientHandler();
httpHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
using var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = httpHandler });
var client = channel.CreateGrpcService<IOrderService>();
var result = await client.CreateOrderAsync(new CreateOrderInput
{
BuyerId = 1,
BuyerName = "张小三",
OrderId = 1,
OrderName = "方便面",
});
var options = new JsonSerializerOptions();
options.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(UnicodeRanges.All);
Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(result, options));
Console.ReadKey();
}
}
}
控制台输出:
示例已上传到本人github