C# gRPC 客户端拦截器设置授权 header

C# gRPC client interceptor set Authorization header

我正在尝试为配置的 API 令牌始终设置的 gRCP 客户端创建一个拦截器。

问题是我找不到设置 context.Options.Headers 的方法。如果我正在阅读文档,我需要调用 WithHeaders 方法并且需要设置新的元数据以便我可以添加更多 headers。唯一的问题是仍然没有创建 headers object.

有人知道我做错了什么吗?

文档:

https://grpc.github.io/grpc/csharp/api/Grpc.Core.CallOptions.html?q=calloptions

代码:

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);
        }
    }
}

在调用 return base.AsyncUnaryCall 时尝试传递新上下文 new ClientInterceptorContext 并设置所有需要的 headers 应该会有所帮助:

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