这里简单记录下如何为.NetCore3.1的WebAPI项目新增gRPC支持,.Net 5 6 7大体上也差不多是这个思路,应该比3.1要简单一些,就不多赘述了,有需要可以参照本文问问gpt就足以了。

首先nuget引用包 Grpc.AspNetCore ,注意如果是3.1版本需要2.56版本的,2.57开始仅支持.net 6 7 8版本,而.Net Core 2.1是不能使用gRPC的,因为2.1仅支持http1。

引用完nuget包后,修改Startup.cs,在 ConfigureServices 方法下新增 services.AddGrpc(),样例如下:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddCors(options => options.AddPolicy("CorsPolicy",
                    builder =>
                    {
                        builder.AllowAnyMethod()
                                .AllowAnyHeader()
                                .SetIsOriginAllowed(_ => true) // =AllowAnyOrigin()
                                .AllowCredentials();
                    }
                )
            );

            services.AddGrpc();

            services.AddControllers(m =>
            {
                m.Filters.Add(typeof(GlobalFilter));
            })
            .AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
                options.SerializerSettings.ContractResolver = new DefaultContractResolver();
            });

            services.AddHttpClient();
        }

然后在 Configure 方法下新增 endpoints.MapGrpcService<YourService>(); 注意这里的YourService是稍后所编写的适配器,样例如下:

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseCors("CorsPolicy");

            app.UseSwagger();
            app.UseSwaggerUI();

            app.ConsulRegister(Configuration);

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapGrpcService<AdService>();
            });
        }

之后修改 Program.cs,在ConfigureWebHostDefaults中,新增如下:

        public static IHostBuilder CreateHostBuilder(string[] args) =>
             Host.CreateDefaultBuilder(args)
                 .ConfigureAppConfiguration((hostBuilderContext, configurationBuilder) =>
                 {
                     var env = EnvironmentForBusiness.GetEnvironmentValForBusiness();
                     
                     configurationBuilder
                        .SetBasePath(hostBuilderContext.HostingEnvironment.ContentRootPath)
                        .AddJsonFile($"appsettings.{env}.json", optional: false, reloadOnChange: true)
                        .AddEnvironmentVariables();
                        .AddDefault();
   })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                    //webBuilder.UseUrls("http://*:8088");
                    webBuilder.ConfigureKestrel(options => 
                    {
                        options.Listen(System.Net.IPAddress.Any, 8088, listenOptions =>
                        {
                            listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
                        });
                        options.Listen(System.Net.IPAddress.Any, 8089, listenOptions =>
                        {
                            listenOptions.Protocols = HttpProtocols.Http2;
                        });
                    });
                });

这里Listen了两个端口,一个是8088,另一个是8089,因为gRPC要求必须是http2,所以单独一个8089端口作为gRPC的端口,并且设置为http2,8088作为原有的http webAPI的端口,使用了Http1AndHttp2,可能你要问了都是Http1AndHttp2了为什么8088不能作为http和gRPC共同的端口呢,我这里试验过是不行的,客户端调用gRPC时会报错http2解析错误,所以必须是独立出来两个端口才行。

然后就是编写proto文件了,这里不用多说,就是标准的gRPC的protoBuf,我这里简单截图一个我项目中的测试样例:

编写好后,双击项目的Connected Services:

然后新增服务引用,选择gRPC,文件选择刚刚写好的proto文件,并且设置为服务器类型:

然后保存,生成项目,这个时候就会根据你的proto文件生成代码了。

最后就是编写适配器,适配器里的代码基本和你原有的API的Controller一样,这个类型应该继承你proto里定义的服务,类型名称AdService实际上就是你之前在Startup里写的YourService,而Ad这个命名空间则是你在proto文件里定义的csharp_namespace,而Advertisement则是proto文件里定义的service,最后的AdvertisementBase则是自动生成的类型:

然后重写proto文件里定义的rpc方法,在前面的截图里,这个方法叫做 Get :

基于以上实现后,这个WebAPI项目就同时支持http和gRPC调用了。

附赠.Net调用gRPC的方式:

首先先起一个支持gRPC的.Net项目,把上面的proto文件copy到这个项目中,然后按照上面提到的方式,双击Connected Services添加服务引用,注意这里不再是服务器模式,要选择客户端模式。

最后编写如下代码:

    public class ClientService
    {
        public void Test()
        {
            using (var channel = GrpcChannel.ForAddress("http://localhost:8089"))
            {
                var client = new Ad.Advertisement.AdvertisementClient(channel);

                var r = client.Get(new Ad.GetRequest { BasicData = "999,0,2,0,0,822ca7b3261a0b2ec10c8cdabfb5a0ea,0", Country = "344", Lang = "zh-CN", Type = Ad.AdType.WikiRecommend, Ptn = Ad.VersionPattern.Default });
            }
        }
    }

按照以上方式就可以成功获取到接口的返回值了。

葫芦

葫芦,诞生于1992年8月11日,游戏宅,胶佬,爱好摸鱼,一个干过超市收银,工地里搬过砖,当过广告印刷狗,做过电焊铁艺的现役.Net程序员。

发表回复