vue + .net core + grpc-web
环境准备
- 下载插件
protoc-gen-grpc-web
,下载完成后重名文件为protoc-gen-grpc-web.exe
。 - 下载
grpc
,下载完成后解压文件。 - 将
protoc-gen-grpc-web.exe
所在目录和grpc
的bin
目录添加到系统环境变量中。
这里需要特别说明的是
grpc
的版本号不能高于3.20.1
,否则在根据proto生成js文件的时候控制台会报protoc-gen-js
不是内部命令的错误。
.Net Core(3.1)
创建grpc服务端项目
- 启动 Visual Studio 2022 并选择“创建新项目”。
- 在“创建新项目”对话框中,搜索 gRPC。 选择“ASP.NET Core gRPC 服务”,并选择“下一步” 。
- 在“配置新项目”对话框中,为“项目名称”输入 GrpcGreeter。
- 选择“下一页”。
- 在“其他信息”对话框中,选择“.NET 3.1 (长期支持)”,然后选择“创建”。
配置 ASP.NET Core 中的 gRPC-Web
- 右键
GrpcGreeter
项目的依赖项,选择管理NuGet程序包,搜做Grpc.AspNetCore.Web
并安装。 - 配置
StartUp.cs
文件
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
// 跨域配置
services.AddCors(o => o.AddPolicy("AllowAll", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.WithExposedHeaders("Grpc-Status", "Grpc-Message", "Grpc-Encoding", "Grpc-Accept-Encoding");
}));
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseGrpcWeb(); // 在路由之后、终结点之前添加 gRPC-Web 中间件 UseGrpcWeb。
app.UseCors(); // 启用跨域中间件
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<GreeterService>()
.EnableGrpcWeb() // 指定 endpoints.MapGrpcService<GreeterService>() 方法支持带有 EnableGrpcWeb 的 gRPC-Web。
.RequireCors("AllowAll"); // 指定 endpoints.MapGrpcService<GreeterService>() 方法支持带有 RequiresCors的 CORS。
});
}
- 修改
greet.proto
syntax = "proto3";
option csharp_namespace = "GrpcGreeter";
package greet;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply);
rpc SayHellos (HelloRequest) returns (stream HelloReply);
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}
- 修改
GreeterService.cs
using Grpc.Core;
using System;
using System.Threading.Tasks;
namespace GrpcGreeter
{
public class GreeterService : Greeter.GreeterBase
{
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
return Task.FromResult(new HelloReply
{
Message = "Hello " + request.Name
});
}
public override async Task SayHellos(HelloRequest request, IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
{
var i = 0;
while (!context.CancellationToken.IsCancellationRequested)
{
await responseStream.WriteAsync(new HelloReply { Message = $"Hello {request.Name} {i}" });
await Task.Delay(TimeSpan.FromSeconds(1));
i++;
}
}
}
}
在 Vue 中使用grpc-web
- 新建vue2项目
- 安装依赖包
npm i google-protobuf grpc-web
- 在后台程序的greet.proto目录下执行编译命令
protoc greet.proto --js_out=import_style=commonjs:.\ --grpc-web_out=import_style=commonjs,mode=grpcwebtext:.\
,复制生成的文件greet_grpc_web_pb.js
和greet_pb.js
到前端项目中。 - 修改App.vue代码:
<template>
<div id="app">
<h2>gRPC-Web ASP.NET Core example</h2>
<div>
<span>Hello:</span>
<input id="name" type="text" v-model="name" />
<button @click="sendUnary" :disabled="unaryDisabled">Send unary</button>
<button @click="startServerStream">{{ btnText }}</button>
</div>
<p id="result" v-html="result"></p>
</div>
</template>
<script>
import { HelloRequest } from "./pb/greet_pb";
import { GreeterClient } from "./pb/greet_grpc_web_pb";
let streamingCall = null;
export default {
data() {
return {
client: null,
result: "",
name: "",
unaryDisabled: false,
btnText: "Start server stream",
};
},
created() {
this.client = new GreeterClient("https://localhost:5001");
},
methods: {
sendUnary() {
let request = new HelloRequest();
request.setName(this.name);
this.client.sayHello(request, {}, (err, response) => {
this.result = response.getMessage();
});
},
startServerStream() {
if (!streamingCall) {
this.unaryDisabled = true;
this.btnText = "Stop server stream";
this.result = "";
var request = new HelloRequest();
request.setName(this.name);
streamingCall = this.client.sayHellos(request, {});
streamingCall.on("data", (response) => {
this.result += response.getMessage() + "<br />";
});
streamingCall.on("status", (status) => {
if (status.code == 0) {
this.result += "Done";
} else {
this.result += "Error: " + status.details;
}
});
} else {
streamingCall.cancel();
streamingCall = null;
this.unaryDisabled = false;
this.btnText = "Start server stream";
}
},
},
};
</script>
<style lang="scss">
#app {
* + * {
margin-left: 20px;
}
}
</style>