C# gRPC客户端拦截器设置授权头部。

6

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

问题在于,我无法找到设置context.Options.Headers的方法。如果我阅读文档,我需要调用WithHeaders方法,并且需要设置新的MetaData,以便我可以添加更多标头。唯一的问题是,它仍然无法创建标头对象。

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

文档:

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://learn.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个回答

4

在调用 return base.AsyncUnaryCall 时,尝试使用设置了所有必要头信息的新上下文 new ClientInterceptorContext 应该会有帮助:

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


为什么我需要创建一个新的上下文而不是使用当前的上下文? - StuiterSlurf
1
看起来只有一种方法可以实现注入元数据。我也研究了这个问题,没有找到更合适的方法。 =/或者你可以在管道开始时注册额外的“CreateNormalContextInterceptor”来创建正常上下文。 xD - Yaroslav

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接