C# gRPC客户端拦截器设置Authorization请求头问题排查
我一眼就看出问题所在了——CallOptions是不可变类型!你调用context.Options.WithHeaders()的时候,它不会修改原有的context.Options对象,而是返回一个全新的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 ) { // 1. 初始化或复制原有的Metadata var metadata = new Metadata(); if (context.Options.Headers != null) { // 复制原有请求头,避免丢失其他已设置的头信息 foreach (var entry in context.Options.Headers) { if (!string.Equals(entry.Key, AuthorizationHeader, StringComparison.OrdinalIgnoreCase)) { metadata.Add(entry); } else { _logger.LogWarning("将Authorization请求头替换为已配置的Floof API密钥值。"); } } } else { _logger.LogDebug("添加gRPC选项请求头"); } // 2. 添加我们的Authorization头 metadata.Add(AuthorizationHeader, _apiToken); // 3. 创建新的CallOptions(因为原对象不可变) var newCallOptions = context.Options.WithHeaders(metadata); // 4. 创建新的拦截器上下文,使用更新后的CallOptions var newContext = new ClientInterceptorContext<TRequest, TResponse>( context.Method, context.Host, newCallOptions ); // 5. 用新的上下文继续执行调用 return continuation(request, newContext); } } }
关键修改说明:
- Metadata的处理:我们没有直接修改原有的
Headers,而是创建了一个新的Metadata实例,复制了原有头中除Authorization之外的所有条目,这样既保留了其他业务需要的头,又确保我们的API令牌是最终生效的。 - 不可变的CallOptions:必须通过
WithHeaders()创建新的CallOptions,然后用它生成新的ClientInterceptorContext,因为原context对象的属性都是只读的,无法直接修改。 - 替换逻辑优化:在复制原有头的时候直接过滤掉旧的Authorization头,比先查找再移除更简洁高效。
这样修改后,你的拦截器就能正确地在每个gRPC请求中注入配置好的API令牌了。
内容的提问来源于stack exchange,提问作者StuiterSlurf
相关产品推荐
相关产品推荐

