前端 JS/TS 调用 ASP.NET Core gRPC-Web
前言
在上两篇文章中,介绍了ASP.NET Core 中的 gRPC-Web 实现 和 在 Blazor WebAssembly 中使用 gRPC-Web,实现了 Blazor WebAssembly 调用 ASP.NET Core gRPC-Web。虽然 ASP.NET Core 中的 gRPC-Web 实现目前还是试验性项目,但是鉴于它在生态上的重大意义,说不定我们很快就能在正式版本中使用。
虽然 Blazor WebAssembly 现在已经是 .NET 进军前端的大热门,但有同学说,只介绍了 Blazor WebAssembly 的调用方法还不够呀,现在比较常用的还是 JS/TS 前端,那么本篇,我就介绍一下在前端 JS/TS 中调用 ASP.NET Core gRPC-Web。
其实 gRPC-Web 项目本身,就是为 JS/TS 提供 gRPC 能力的,让不支持 HTTP/2 的客户端和服务端也能使用 gRPC 的大部分特性。gRPC-Web 项目提供了一个 protoc CLI 插件,可用于把 proto 协议文件转换为 JS/TS 语言可导入的对应 gRPC 服务的客户端,还生成了 .d.ts 文件来支持 Typescript。
示例
接下来,我就来展示一下,用 Visual Studio 自带的 ASP.NET Core + Angular 模板创建的项目,把原来的 WebApi 调用改造成 gRPC-Web 调用。
本示例基于 .NET Core 3.1,请安装好最新的 .NET Core SDK 和 Visual Studio 2019。
创建项目
打开 Visual Studio 2019,创建新项目 -> 选择"ASP.NET Core Web 应用程序" -> 填写项目名 -> 选择 "Angular" 项目模板。如图:

我们就是用这个项目,把 fetch-data 页面获取数据的方式修改为 gRPC-Web 。
添加 gRPC proto 文件
在项目中新建一个目录 Protos,创建文件 weather.proto :
syntax = "proto3";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
option csharp_namespace = "AspNetCoreGrpcWeb";
package WeatherForecast;
service WeatherForecasts {
  rpc GetWeather (google.protobuf.Empty) returns (WeatherReply);
}
message WeatherReply {
  repeated WeatherForecast forecasts = 1;
}
message WeatherForecast {
  google.protobuf.Timestamp dateTimeStamp = 1;
  int32 temperatureC = 2;
  int32 TemperatureF = 3;
  string summary = 4;
}
可以看到 proto 中导入了官方库的其他 proto 文件,它们是编译用的辅助文件。对于.NET Core 项目,可以通过引用 Google.Protobuf 这个包引入。
修改 ASP.NET Core 服务端
我们先修改服务端,让 ASP.NET Core 提供 gRPC-Web 服务。
由于 gRPC-Web 包还没有发布到 NuGet.org,现在你需要添加一个临时的包管理源来获得 nightly 预览。在你的解决方案的根目录下添加NuGet.config文件:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <!--To inherit the global NuGet package sources remove the <clear/> line below -->
    <clear />
    <add key="nuget" value="https://api.nuget.org/v3/index.json" />
    <add key="gRPC-nightly" value="https://grpc.jfrog.io/grpc/api/nuget/v3/grpc-nuget-dev" />
  </packageSources>
</configuration>
再添加必要的 Nuget 包引用:
<PackageReference Include="Grpc.AspNetCore" Version="2.27.0-dev202001100801" />
<PackageReference Include="Grpc.AspNetCore.Web" Version="2.27.0-dev202001100801" />
接着,修改原来的 WeatherForecastController 改为 WeatherForecastService:
    public class WeatherForecastsService : WeatherForecasts.WeatherForecastsBase
    {
        private static readonly string[] Summaries = {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };
        public override Task<WeatherReply> GetWeather(Empty request, ServerCallContext context)
        {
            var reply = new WeatherReply();
            var rng = new Random();
            reply.Forecasts.Add(Enumerable.Range(1, 5).Select(index =>
            {
                var temperatureC = rng.Next(-20, 55);
                return new WeatherForecast
                {
                    DateTimeStamp = Timestamp.FromDateTime(DateTime.UtcNow.AddDays(index)),
                    TemperatureC = temperatureC,
                    TemperatureF = 32 + (int)(temperatureC / 0.5556),
                    Summary = Summaries[rng.Next(Summaries.Length)]
                };
            }));
            return Task.FromResult(reply);
        }
    }
现在,在你的服务器的 Startup.cs 文件中,修改 ConfigureServices 以添加以下行:
  services.AddGrpc();
注意:如果你只打算公开 gRPC 服务,你可能不再需要 MVC 控制器,在这种情况下,你可以从下面删除services.AddMvc() 和 endpoints.MapDefaultControllerRoute()。
只是在 app.AddRouting(); 的下面添加以下内容,它会处理将传入的 gRPC-web 请求映射到服务端,使其看起来像 gRPC 请求:
  app.UseGrpcWeb();
最后,在app.UseEndpoints语句块中注册你的 gRPC-Web 服务类,并在该语句块的顶部使用以下代码行:
  endpoints.MapGrpcService<WeatherForecastsService>().EnableGrpcWeb();
就这样,你的 gRPC-Web 服务端已经准备好了!
修改 Angular 项目
项目的 ClientApp 目录中,就是 Angular 的项目文件,使用 ASP.NET Core 托管,是这个项目模板约定的。
我们需要先通过 proto 生成需要的 js 文件。
安装 npm 包
运行命令:
 npm i protoc google-protobuf ts-protoc-gen @improbable-eng/grpc-web -s
生成 js 文件
在 ClientApp 目录下创建目录 proto ,再运行命令:
./node_modules/protoc/protoc/bin/protoc --plugin="protoc-gen-ts=.\node_modules\.bin\protoc-gen-ts.cmd" --js_out="import_style=commonjs,binary:./../Protos" --ts_out="service=grpc-web:src/app/proto" -I ./../Protos ../Protos/*.proto
可以在proto目录中看到生成了 4 个文件(每个 proto 会生成 4 个)
- weather_pb_service.js: 包含了 rpc 调用客户端- WeatherForecastsClient。
- weather_pb.js: 包含了传输对象- WeatherForecast。这个文件在原 proto 的目录下,需要手动移过来。
- 两个 *.d.ts文件是对应以上两个文件的类型描述,用于 TS。

修改 fetch-data 页面组件
接下来,需要引用生成的文件,创建一个 WeatherForecastsClient 来调用 gRPC-Web 服务端。
- fetch-data.component.ts - import { Component } from '@angular/core';
 import { WeatherForecast } from '../proto/weather_pb';
 import { WeatherForecastsClient } from '../proto/weather_pb_service';
 import { Empty } from 'google-protobuf/google/protobuf/empty_pb'; @Component({
 selector: 'app-fetch-data',
 templateUrl: './fetch-data.component.html',
 })
 export class FetchDataComponent {
 public forecasts: WeatherForecast[]; private client: WeatherForecastsClient;
 constructor() {
 this.client = new WeatherForecastsClient('https://localhost:5001');
 this.client.getWeather(new Empty(), (error, reply) => {
 if (error) {
 console.error(error);
 } this.forecasts = reply.getForecastsList();
 });
 }
 }
 
- fetch-data.component.html - <h1 id="tableLabel">Weather forecast</h1> <p>This component demonstrates fetching data from the server.</p> <p *ngIf="!forecasts"><em>Loading...</em></p> <table class="table table-striped" aria-labelledby="tableLabel" *ngIf="forecasts">
 <thead>
 <tr>
 <th>Date</th>
 <th>Temp. (C)</th>
 <th>Temp. (F)</th>
 <th>Summary</th>
 </tr>
 </thead>
 <tbody>
 <tr *ngFor="let forecast of forecasts">
 <td>{{ forecast.getDatetimestamp().toDate() }}</td>
 <td>{{ forecast.getTemperaturec() }}</td>
 <td>{{ forecast.getTemperaturef() }}</td>
 <td>{{ forecast.getSummary() }}</td>
 </tr>
 </tbody>
 </table>
 
可以看到:
- 创建 WeatherForecastsClient对象需要传入服务端的 HostName,要注意不要用/后缀。
- 生成出来的 WeatherForecast类型包含getter/setter, 而WeatherForecast.AsObject类才是值对象,可以直接访问属性值,需要调用.toObject()方法进行转换。
- datetimestamp属性的类型- proto.google.protobuf.Timestamp是 protobuf 里的关键字,调用- toDate()方法可转换为 TS 的- Date类型。
运行项目
完事具备,我们可以运行项目了。访问 https://localhost:5001/fetch-data,就可以看到前端是通过 gRPC-Web 获取数据了。

总结
可以看出,要在前端 JS/TS 使用 gRPC-Web 虽然在开发工具上没有 Blazor WebAssembly 方便,但是从 proto 生成客户端之后,前端的 TS 代码就能直接获得强类型的调用方法和路由,很简单地得到 gRPC 带来的好处。另外,gRPC-Web 项目本身已经 GA,所以我们可以先在后端使用它的 gRPC 代理,而前端可以放心大胆地在我们的生产项目中使用它。等到 ASP.NET Core 正式支持 gRPC-Web 后,就可以不需要代理了,比其他平台和语言都更有优势。
本示例的源码已发布到 Github:https://github.com/ElderJames/AspNetCoreGrpcWeb
前端 JS/TS 调用 ASP.NET Core gRPC-Web的更多相关文章
- ASP.NET Core gRPC 入门全家桶
		一. 说明 本全家桶现在只包含了入门级别的资料,实战资料更新中. 二.官方文档 gRPC in Asp.Net Core :官方文档 gRPC 官网:点我跳转 三.入门全家桶 正片: ASP.NET ... 
- 旧 WCF 项目迁移到 asp.net core + gRPC 的尝试
		一个月前,公司的运行WCF的windows服务器down掉了,由于 AWS 没有通知,没有能第一时间发现问题. 所以,客户提出将WCF服务由C#改为JAVA,在Linux上面运行:一方面,AWS对Li ... 
- ASP.NET Core GRPC 和 Dubbo 互通
		一.前言 Dubbo 是比较流行的服务治理框架,国内不少大厂都在使用.以前的 Dubbo 使用的是私有协议,采集用的 hessian 序列化,对于多语言生态来说是极度的不友好.现在 Dubbo 发布了 ... 
- 将前端js异步调用的多个服务合并为一个前端服务
		将前端js异步调用的多个服务合并为一个前端服务 1. 减少前端js异步请求的次数改善浏览体验 2. 方便地针对单个接口做异常降级处理 
- ASP.NET Core gRPC 健康检查的实现方式
		一. 前言 gRPC 服务实现健康检查有两种方式,前面在此文 ASP.NET Core gRPC 使用 Consul 服务注册发现 中有提到过,这里归纳整理一下.gRPC 的健康检查,官方是定义了标准 ... 
- angular4和asp.net core 2 web api
		angular4和asp.net core 2 web api 这是一篇学习笔记. angular 5 正式版都快出了, 不过主要是性能升级. 我认为angular 4还是很适合企业的, 就像.net ... 
- 温故知新,使用ASP.NET Core创建Web API,永远第一次
		ASP.NET Core简介 ASP.NET Core是一个跨平台的高性能开源框架,用于生成启用云且连接Internet的新式应用. 使用ASP.NET Core,您可以: 生成Web应用和服务.物联 ... 
- Dapr 运用之集成 Asp.Net Core Grpc 调用篇
		前置条件: <Dapr 运用> 改造 ProductService 以提供 gRPC 服务 从 NuGet 或程序包管理控制台安装 gRPC 服务必须的包 Grpc.AspNetCore ... 
- ASP.NET Core gRPC 使用 Consul 服务注册发现
		一. 前言 gRPC 在当前最常见的应用就是在微服务场景中,所以不可避免的会有服务注册与发现问题,我们使用gRPC实现的服务可以使用 Consul 或者 etcd 作为服务注册与发现中心,本文主要介绍 ... 
随机推荐
- ELK学习实验009:安装kibana的仪表盘
			一 metricbeat仪表盘 1.1 安装metricbeat仪表盘 可以将metricbeat数据在kibana中展示 [root@node4 ~]# cd /usr/local/metricbe ... 
- Analysis of Two-Channel Generalized Sidelobe Canceller (GSC) With Post-Filtering
			作者:凌逆战 地址:https://www.cnblogs.com/LXP-Never/p/12071748.html 题目:带后置滤波的双通道广义旁瓣相消器(GSC)的分析 作者:Israel Co ... 
- 从零开始のcocos2dx生活(五)ActionEase
			文章目录 sineEaseIn sineEaseOut sineEaseInOut expoEaseIn expoEaseOut expoEaseInOut easeIn easeOut easeIn ... 
- “Deep models under the GAN: information leakage from collaborative deep learning”阅读笔记
			一.摘要 指出深度学习在机器学习场景下的优势,以及深度学习快速崛起的原因.随后点出研究者对于深度学习隐私问题的考虑.作者提出了一种强力的攻击方法,在其攻击下任何分布式.联邦式.或者中心化的深度学习方法 ... 
- ScheduledThreadPoolExecutor中定时周期任务的实现源码分析
			ScheduledThreadPoolExecutor是一个定时任务线程池,相比于ThreadPoolExecutor最大的不同在于其阻塞队列的实现 首先看一下其构造方法: public Schedu ... 
- 带有外部Tomcat的Spring Boot
			在本文中,我将如何在外部Tomcat上运行Spring Boot应用程序.对我来说,这是一个现实的场景,我必须解决这个问题,因此也请教了一下优锐课老师,得到了很多帮助.也希望当你遇到类似问题时,能为你 ... 
- 利用cuteftp上传并修改网站上内容
			1.下载cuteftp 2.在host中输入网址(如:219.142.121.2) 3.username中输入(如:BNULS) 4.passpord中输入:(如410teamgood) 5.端口输入 ... 
- Jmeter+Jenkins 搭配进行接口测试
			单纯通过Jmeter的界面进行Web的接口测试,效率低下.为此将Jmeter的接口测试与Jenkins联合,实现持续集成.配置完成后,只需修改运行的Jmeter脚本即可,运行结束后测试结果发送到指定邮 ... 
- 从头学pytorch(十八):GoogLeNet
			GoogLeNet GoogLeNet和vgg分别是2014的ImageNet挑战赛的冠亚军.GoogLeNet则做了更加大胆的网络结构尝试,虽然深度只有22层,但大小却比AlexNet和VGG小很多 ... 
- docker制作cenos+php56+nginx镜像
			首先你环境要安装好docker 1 获取centos镜像. docker search centos 选取第一个官方镜像. docker pull docker.io/centos 新建镜像挂载目录 ... 
