Перехватчик клиента C# gRPC устанавливает заголовок авторизации

#c# #grpc #interceptor

Вопрос:

Я пытаюсь создать перехватчик для клиента gRCP, для которого всегда задан настроенный маркер API.

Проблема в том, что я просто не могу найти способ установить context.Options.Headers . Если я читаю документы, мне нужно вызвать WithHeaders метод, и для этого необходимо установить новые метаданные, чтобы я мог добавить больше заголовков. Единственная проблема заключается в том, что по-прежнему не создается объект заголовков.

Кто-нибудь знает, что я делаю не так?

Документы:

https://grpc.github.io/grpc/csharp/api/Grpc.Core.CallOptions.html?q=вызовы

Код:

 using System;
using Floof.Common.Exceptions.IoC;
using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace Floof.Common.GrpcClient.Interceptors
{
    public class AuthorizationHeaderInterceptor : Interceptor
    {
        private readonly ILogger<AuthorizationHeaderInterceptor> _logger;
        public const string Section = "gRPC";
        public const string Key = "ApiKey";

        private const string AuthorizationHeader = "Authorization";
        private readonly string _apiToken;

        public AuthorizationHeaderInterceptor(
            ILogger<AuthorizationHeaderInterceptor> logger,
            IConfiguration configuration
        )
        {
            if (configuration == null)
                throw new ArgumentNullException(nameof(configuration));
            
            _logger = logger;

            var apiToken = configuration.GetSection(Section)?[Key];
            if (string.IsNullOrWhiteSpace(apiToken))
                throw new IoCConfigurationException("API key", Section, Key);
            
            _apiToken = apiToken;
        }

        public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
            TRequest request,
            ClientInterceptorContext<TRequest, TResponse> context,
            AsyncUnaryCallContinuation<TRequest, TResponse> continuation
        )
        {
            // Check if the headers are not null, if so, initialize new headers
            if (context.Options.Headers == null)
            {
                _logger.LogDebug("Adding gRPC option headers");
                context.Options.WithHeaders(new Metadata());
            }
            
            // gRPC calls and responses can also include metadata that's similar to HTTP headers. This metadata is mostly
            // invisible to gRPC itself and is passed through to be processed by your application code or middleware.
            // Metadata is represented as key/value pairs, where the key is a string and the value is either a string or
            // binary data. You don’t need to specify metadata in the .proto file.
            // https://docs.microsoft.com/en-us/dotnet/architecture/grpc-for-wcf-developers/metadata
            var authorization = new Metadata.Entry(AuthorizationHeader, _apiToken);

            // Check if the header already has an Authorization header within. For now we agreed on that no one is allowed 
            // to set this header. If we want to use this client for external API services as well, we need to look up if 
            // this is a good client to use and what changes it takes for this client to pair with another API then the Floof cloud
            var setAuthorizationHeaderEntry = context.Options.Headers.Get(AuthorizationHeader);
            if (setAuthorizationHeaderEntry != null)
            {
                _logger.LogWarning("Replacing the Authorization header by the configured Floof API key value.");
                // Remove the header out of the options because we set the configured key
                context.Options.Headers.Remove(setAuthorizationHeaderEntry);
            }

            // Add the header to the context. 
            context.Options.Headers.Add(authorization);
            
            // replace the context with the authorization context
            return base.AsyncUnaryCall(request, context, continuation);
        }
    }
}

 

Ответ №1:

Попытка передать новый контекст new ClientInterceptorContext со всеми необходимыми заголовками, установленными при вызове return base.AsyncUnaryCall , должна помочь:

 public class AuthorizationHeaderInterceptor : Interceptor
{

 ...

    public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
        TRequest request,
        ClientInterceptorContext<TRequest, TResponse> context,
        AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
    {
        ...
 
        var headers = new Metadata();
        headers.Add(new Metadata.Entry("Authorization", _token));

        var newOptions = context.Options.WithHeaders(headers);

        var newContext = new ClientInterceptorContext<TRequest, TResponse>(
            context.Method,
            context.Host,
            newOptions);
 
        return base.AsyncUnaryCall(request, newContext, continuation);
    }
}
 

https://github.com/grpc/grpc-dotnet/issues/1255#issuecomment-811635583

Комментарии:

1. Почему мне нужно создавать новый контекст вместо использования текущего контекста?

2. Похоже, что это только один способ получить метаданные инъекции. Также посмотрел на эту проблему и не нашел более подходящего способа. =/ Или вы должны были дополнительно CreateNormalContextInterceptor зарегистрироваться в начале конвейера для создания нормального контекста. xD