Skip to main content

# implementation

In this section we will implement our own interceptors.

# Server interceptor

Interceptor for unary types

       public class ServerLoggingInterceptor : Interceptor
        {
            private readonly ILogger _logger;

            public ServerLoggingInterceptor(ILogger<ServerLoggingInterceptor> logger)
            {
                _logger = logger;
            }

            public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
                TRequest request,
                ServerCallContext context,
                UnaryServerMethod<TRequest, TResponse> continuation)
            {
                _logger.LogTrace($"Starting receiving call. Type: {MethodType.Unary}. " + $"Method: {context.Method}.");
                Console.WriteLine("interceptor here:");

                try
                {
                    return await continuation(request, context);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"Error thrown by {context.Method}.");
                    throw;
                }
            }
        }

## Registering the interceptor​

Registering

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

# Client interceptors

Title

      public class ClientLoggerInterceptor : Interceptor
        {
            private readonly ILogger<ClientLoggerInterceptor> _logger;

            public ClientLoggerInterceptor(ILoggerFactory loggerFactory)
            {
                _logger = loggerFactory.CreateLogger<ClientLoggerInterceptor>();
            }

            public override TResponse BlockingUnaryCall<TRequest, TResponse>(
                TRequest request,
                ClientInterceptorContext<TRequest, TResponse> context,
                BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
            {
                LogCall(context.Method);
                AddCallerMetadata(ref context);

                try
                {
                    return continuation(request, context);
                }
                catch (Exception ex)
                {
                    LogError(ex);
                    throw;
                }
            }

            public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
                TRequest request,
                ClientInterceptorContext<TRequest, TResponse> context,
                AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
            {
                LogCall(context.Method);
                AddCallerMetadata(ref context);

                try
                {
                    var call = continuation(request, context);

                    return new AsyncUnaryCall<TResponse>(HandleResponse(call.ResponseAsync), call.ResponseHeadersAsync, call.GetStatus, call.GetTrailers, call.Dispose);
                }
                catch (Exception ex)
                {
                    LogError(ex);
                    throw;
                }
            }

            private async Task<TResponse> HandleResponse<TResponse>(Task<TResponse> t)
            {
                try
                {
                    var response = await t;
                    _logger.LogInformation($"Response received: {response}");
                    return response;
                }
                catch (Exception ex)
                {
                    LogError(ex);
                    throw;
                }
            }

            public override AsyncClientStreamingCall<TRequest, TResponse> AsyncClientStreamingCall<TRequest, TResponse>(
                ClientInterceptorContext<TRequest, TResponse> context,
                AsyncClientStreamingCallContinuation<TRequest, TResponse> continuation)
            {
                LogCall(context.Method);
                AddCallerMetadata(ref context);

                try
                {
                    return continuation(context);
                }
                catch (Exception ex)
                {
                    LogError(ex);
                    throw;
                }
            }

            public override AsyncServerStreamingCall<TResponse> AsyncServerStreamingCall<TRequest, TResponse>(
                TRequest request,
                ClientInterceptorContext<TRequest, TResponse> context,
                AsyncServerStreamingCallContinuation<TRequest, TResponse> continuation)
            {
                LogCall(context.Method);
                AddCallerMetadata(ref context);

                try
                {
                    return continuation(request, context);
                }
                catch (Exception ex)
                {
                    LogError(ex);
                    throw;
                }
            }

            public override AsyncDuplexStreamingCall<TRequest, TResponse> AsyncDuplexStreamingCall<TRequest, TResponse>(
                ClientInterceptorContext<TRequest, TResponse> context,
                AsyncDuplexStreamingCallContinuation<TRequest, TResponse> continuation)
            {
                LogCall(context.Method);
                AddCallerMetadata(ref context);

                try
                {
                    return continuation(context);
                }
                catch (Exception ex)
                {
                    LogError(ex);
                    throw;
                }
            }

            private void LogCall<TRequest, TResponse>(Method<TRequest, TResponse> method)
                where TRequest : class
                where TResponse : class
            {
                _logger.LogInformation($"Starting call. Name: {method.Name}. Type: {method.Type}. Request: {typeof(TRequest)}. Response: {typeof(TResponse)}");
            }

            private void AddCallerMetadata<TRequest, TResponse>(ref ClientInterceptorContext<TRequest, TResponse> context)
                where TRequest : class
                where TResponse : class
            {
                var headers = context.Options.Headers;

                // Call doesn't have a headers collection to add to.
                // Need to create a new context with headers for the call.
                if (headers == null)
                {
                    headers = new Metadata();
                    var options = context.Options.WithHeaders(headers);
                    context = new ClientInterceptorContext<TRequest, TResponse>(context.Method, context.Host, options);
                }

                // Add caller metadata to call headers
                headers.Add("caller-user", Environment.UserName);
                headers.Add("caller-machine", Environment.MachineName);
                headers.Add("caller-os", Environment.OSVersion.ToString());
                headers.Add("calling-time", DateTime.UtcNow.ToShortDateString());
            }

            private void LogError(Exception ex)
            {
                _logger.LogError(ex, $"Call error: {ex.Message}");
            }

            //var invoker = channel.Intercept(new ClientLoggerInterceptor(loggerFactory));
        }

## Registering the interceptor​

Title

    builder.Services
        .AddGrpcClient<SpeakerServiceDefinition.SpeakerServiceDefinitionClient>
        (o => { o.Address = speakers; })
         .ConfigureChannel(o =>
         {

             // o.Credentials
         })
          .AddInterceptor<ClientLoggerInterceptor>();

### Testing out​

- add a breakpoint in ClientLoggerInterceptor
- open the MVC app
- add a breakpoint in ServeLoggerinterceptor
- get the `context.GetHttpContext()`
- inspect the `Request.Headers` object to identify the added metadata

ClientLoggerInterceptor- > ServerLoggerInterceptor