gRPC
|
This section documents ASP.NET Core on .NET 10 (LTS), the current release — the minimal hosting model, the middleware pipeline, dependency injection, Minimal APIs, MVC & Razor Pages, Blazor with the current render modes, SignalR and gRPC, EF Core, ASP.NET Core Identity and policy-based authorization, output caching, rate limiting, and Native-AOT-aware building — as described by the official documentation at Microsoft Learn, which is the reference these pages are written and verified against. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. .NET ships a major release every November and its APIs continue to evolve: the examples here target .NET 10 / C# 14. This section’s bibliography lists the reference material consulted while preparing these pages. |
gRPC is a contract-first, high-performance RPC framework for service-to-service calls. It defines services in a
.proto file and generates typed client and server code, over HTTP/2 and Protocol Buffers.
Contract-first .proto
// greet.proto
syntax = "proto3";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest { string name = 1; }
message HelloReply { string message = 1; }
The .NET tooling (Grpc.Tools, referenced automatically by the gRPC service templates) generates a
GreeterBase server base class and a GreeterClient from this file at build time:
public sealed class GreeterService : Greeter.GreeterBase
{
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
=> Task.FromResult(new HelloReply { Message = $"Hello {request.Name}" });
}
// Program.cs
builder.Services.AddGrpc();
app.MapGrpcService<GreeterService>();
The four call types
service OrderTracker {
rpc GetOrder (OrderRequest) returns (Order); // unary
rpc WatchOrder (OrderRequest) returns (stream OrderUpdate); // server streaming
rpc UploadEvents (stream Event) returns (UploadSummary); // client streaming
rpc Chat (stream ChatMessage) returns (stream ChatMessage); // bidirectional streaming
}
public override async Task WatchOrder(
OrderRequest request, IServerStreamWriter<OrderUpdate> responseStream, ServerCallContext context)
{
await foreach (var update in orders.WatchAsync(request.OrderId, context.CancellationToken))
{
await responseStream.WriteAsync(update);
}
}
-
Unary — one request, one response; the default and most common.
-
Server streaming — one request, a stream of responses (progress updates, live data).
-
Client streaming — a stream of requests, one response (batch upload, aggregation).
-
Bidirectional streaming — both sides stream independently (chat, real-time collaboration).
Interceptors
Server and client interceptors wrap every call for cross-cutting logic — logging, auth, metrics — the gRPC analog of MVC filters or middleware:
public sealed class LoggingInterceptor(ILogger<LoggingInterceptor> logger) : Interceptor
{
public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
TRequest request, ServerCallContext context, UnaryServerMethod<TRequest, TResponse> continuation)
{
logger.LogInformation("-> {Method}", context.Method);
return await continuation(request, context);
}
}
builder.Services.AddGrpc(o => o.Interceptors.Add<LoggingInterceptor>());
gRPC-Web and JSON transcoding
Browsers cannot speak raw HTTP/2 trailers-based gRPC, and REST-only consumers don’t speak Protocol Buffers at all — two different bridges cover each case:
// dotnet add package Grpc.AspNetCore.Web
app.UseGrpcWeb();
app.MapGrpcService<GreeterService>().EnableGrpcWeb();
// JSON transcoding: annotate the .proto with an HTTP mapping
import "google/api/annotations.proto";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = { get: "/v1/greet/{name}" };
}
}
gRPC-Web (app.UseGrpcWeb()) lets browsers call gRPC services over a compatible wire format. gRPC JSON
transcoding (Grpc.AspNetCore.JsonTranscoding) exposes the same service as a conventional REST/JSON endpoint
from one implementation, useful for consumers that expect plain HTTP. See
gRPC-Web in ASP.NET Core gRPC apps and
gRPC JSON transcoding.
Deadlines and cancellation
A gRPC call carries a deadline the server can observe via context.CancellationToken, so long-running work
stops as soon as the caller gives up:
var deadline = DateTime.UtcNow.AddSeconds(5);
var reply = await client.SayHelloAsync(request, deadline: deadline);
Passing no deadline means the call can run indefinitely — always set one for calls that must not hang.
The gRPC client factory
Grpc.Net.ClientFactory integrates with IHttpClientFactory (see
HTTP Client and Resilience) for pooled connections and
Polly-based resilience:
builder.Services.AddGrpcClient<Greeter.GreeterClient>(o =>
o.Address = new Uri("https://greeter.internal"));
public sealed class GreetingService(Greeter.GreeterClient client)
{
public Task<HelloReply> GreetAsync(string name, CancellationToken ct)
=> client.SayHelloAsync(new HelloRequest { Name = name }, cancellationToken: ct).ResponseAsync;
}
Code-first gRPC
protobuf-net.Grpc generates the wire contract from plain C# interfaces and [ServiceContract] attributes
instead of a .proto file, letting client and server share the same C# contract assembly — convenient inside
a single .NET solution, at the cost of the .proto file other, non-.NET consumers would otherwise rely on.
gRPC vs. REST
| Choose | When |
|---|---|
gRPC |
Internal, chatty, strongly-typed service-to-service traffic; streaming; polyglot microservices sharing
a |
REST/JSON |
Public or browser-facing APIs, cache-friendly reads, or consumers that expect plain HTTP/JSON — see Web API Controllers and Minimal APIs. |
JSON transcoding narrows this trade-off by exposing one gRPC implementation both ways. See gRPC compared to HTTP APIs with JSON.