商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > C#怎么创建gRPC服务_C# ASP.NET Core gRPC通信教程【高级】

C#怎么创建gRPC服务_C# ASP.NET Core gRPC通信教程【高级】

  发布于2026-07-18 阅读(0)

扫一扫,手机访问

在 gRPC 服务开发中,有几个细节如果没处理好,服务端看起来“编译通过”,客户端却直接报错,或者请求超时、收不到错误详情。今天咱们就挑最常踩坑的几个点,一个一个拆开说清楚。

C#怎么创建gRPC服务_C# ASP.NET Core gRPC通信教程【高级】

先从最基础的继承关系说起。如果你写了一个 GreeterService,它必须继承自 GreeterBase,并且重写的方法返回类型必须是 Task。这不是可选项——一旦漏掉,客户端那边收到的就是 StatusCode.Unimplemented 错误,而不是你辛辛苦苦写的业务逻辑。换句话说,连服务入口都没对上。

怎么让 .proto 文件生成服务基类

关键不是手写这些基类,而是让 Grpc.Tools 在构建时自动产出 GreeterBaseHelloRequestHelloReply 这些类型。你只需要在项目文件里放对一行配置:

  
  • GrpcServices="Server" 表示只生成服务端基类,不生成客户端存根;如果你需要同时调用其他服务,改成 Both 就行。
  • Include 路径是相对于项目根目录的,.proto 文件必须真实存在于那个位置(比如 Protos/greet.proto)。
  • 千万别手动把生成的 .cs 文件加到项目里——它们会在 obj/ 目录下临时存在,每次构建都会被覆盖,手动加进去反而会冲突。

为什么 MapGrpcService 不生效

常见原因就两个:注册顺序不对,或者中间件冲突。ASP.NET Core 要求 gRPC 端点必须在 UseRouting() 之后、UseEndpoints()UseAuthorization() 之前注册。典型正确顺序长这样:

app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapGrpcService(); // 必须放在这行
app.MapControllers();
  • 如果用了 UseEndpoints,记得检查里面有没有漏掉 endpoints.MapGrpcService() 调用。
  • MapGrpcService 只对 HTTP/2 请求有效。开发时如果你用浏览器直接访问 https://localhost:5001/greet.Greeter/SayHello,肯定会失败——浏览器不支持 gRPC-Web 以外的原生 gRPC。
  • 还要确认 Program.cs 是否启用了 HTTP/2:Kestrel 默认只在 HTTPS 下启用 HTTP/2,HTTP 端口(比如 5000)默认是不支持的。

一元方法里怎么获取请求上下文和取消信号

ServerCallContext 可不是摆设——它提供了真实的运行时信息:截止时间、客户端元数据、取消 CancellationToken,这些在复杂业务场景中特别有用。比如你想在超时前主动退出耗时操作:

public override async Task SayHello(HelloRequest request, ServerCallContext context)
{
    using var cts = CancellationTokenSource.CreateLinkedTokenSource(context.CancellationToken);
    cts.CancelAfter(TimeSpan.FromSeconds(3));
    try
    {
        var result = await Hea vyWorkAsync(cts.Token); // 传入组合后的 token
        return new HelloReply { Message = $"Hello {request.Name} -> {result}" };
    }
    catch (OperationCanceledException)
    {
        throw new RpcException(new Status(StatusCode.DeadlineExceeded, "Processing timed out"));
    }
}
  • context.CancellationToken 来自客户端发起的取消信号(比如移动端断网、前端主动 abort)。
  • 不要直接 await context.CancellationToken.WaitHandle,应该把它传给支持 CancellationToken 的异步 API。
  • 如果你忽略了 context.CancellationToken,服务端就无法响应客户端的中断,时间长了会导致连接堆积和资源泄漏,这是非常隐蔽的坑。

为什么客户端收不到错误详情

默认情况下,RpcExceptionStatus.Detail 字段不会被序列化到网络上——除非你在服务器端显式开启详细错误模式。注意,这个开关只应该在开发环境打开:

builder.Services.AddGrpc(options =>
{
    options.EnableDetailedErrors = true; // ⚠️ 生产环境必须关掉
});
  • 关闭后,客户端收到的 RpcException.Status 仍然包含 StatusCode 和简短消息,但 Detail 会为空。
  • 生产环境如果需要结构化错误信息,应该通过自定义状态码加上响应体字段(比如 error_codedebug_id)来传递,而不是依赖 Status.Detail
  • 这个开关不影响 StatusCode.Unimplemented 这类底层错误的传播——它们始终可见。

最后再总结两个容易被忽略的细节:ServerCallContext 决定了你能拿到什么运行时信息,GrpcServices 属性决定了构建时生成哪些类型。漏掉任意一个,服务就只是“能编译”,而不是“能通信”。

本文转载于:https://www.php.cn/faq/2344573.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注