Introduction

When building gRPC services in .NET, you often need to run common logic across multiple requests, such as logging, authentication checks, or error handling. Writing this logic inside every method can make your code messy and hard to maintain.

This is where gRPC interceptors come in.

In this article, we will understand what interceptors are, how they work, and how to use them effectively in ASP.NET Core in simple and clear language.

What are gRPC Interceptors?

Interceptors in gRPC are similar to middleware in ASP.NET Core.

They allow you to:

This helps you keep your service methods clean and focused only on business logic.

How Interceptors Work Internally

When a request is sent to a gRPC service, it goes through a pipeline.

This means interceptors can run logic both before and after the service method.

Types of Interceptors in gRPC

In .NET, you can use interceptors for different types of calls:

Each type has its own handler method in the interceptor class.

Creating a Basic Interceptor

Let’s create a simple logging interceptor.

public class LoggingInterceptor : Interceptor
{
    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        Console.WriteLine("Request received");

        var response = await continuation(request, context);

        Console.WriteLine("Response sent");

        return response;
    }
}

Here’s what is happening:

Registering an Interceptor

To use the interceptor, register it in your application.

builder.Services.AddGrpc(options =>
{
    options.Interceptors.Add<LoggingInterceptor>();
});

Now, this interceptor will run for all gRPC calls.

Using Interceptors for Authentication

Interceptors are commonly used for authentication.

public class AuthInterceptor : Interceptor
{
    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        var token = context.RequestHeaders.FirstOrDefault(h => h.Key == "authorization")?.Value;

        if (string.IsNullOrEmpty(token))
        {
            throw new RpcException(new Status(StatusCode.Unauthenticated, "Token missing"));
        }

        return await continuation(request, context);
    }
}

This ensures that every request is validated before reaching the service.

Using Interceptors for Error Handling

You can also use interceptors for centralized error handling.

public class ExceptionInterceptor : Interceptor
{
    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        try
        {
            return await continuation(request, context);
        }
        catch (Exception)
        {
            throw new RpcException(new Status(StatusCode.Internal, "Something went wrong"));
        }
    }
}

This avoids repeating try-catch blocks in every service method.

Chaining Multiple Interceptors

You can register multiple interceptors.

builder.Services.AddGrpc(options =>
{
    options.Interceptors.Add<LoggingInterceptor>();
    options.Interceptors.Add<AuthInterceptor>();
});

They will execute in the order they are registered.

Best Practices for Using Interceptors

Following these practices keeps your code clean and maintainable.

Common Mistakes to Avoid

Avoiding these mistakes will help you use interceptors effectively.

When Should You Use Interceptors?

You should use interceptors when you need:

They are very useful in large applications with multiple services.

Summary

gRPC interceptors in .NET are a powerful way to handle cross-cutting concerns like logging, authentication, and error handling in a centralized way. They work like middleware and allow you to intercept requests and responses. By using interceptors properly, you can keep your service methods clean, improve maintainability, and build scalable applications.