我是 Asp.Net core 6 的新手,正在研究在请求中接收不记名 token 的 GraphQL API。然后 API 调用另一个 Web API 并在 header 中传递相同的不记名 token 。下面是我的代码的样子-
程序.cs:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<HeaderHandler>();
builder.Services.AddScoped<TokenContainer>();
//builder.Services.AddScoped<IFooGateway, FooGateway>();
builder.Services.AddHttpClient<IFooGateway, FooGateway>((c) =>
{
c.BaseAddress = new Uri(builder.Configuration["FooApiUrl"]);
})
.AddHttpMessageHandler<HeaderHandler>();
builder.Services.AddTransient<GraphApiService>();
var app = builder.Build();
app.UseMiddleware<HeaderMiddleware>();
app.MapGraphQL();
app.Run();
HeaderMiddleware.cs
public class HeaderMiddleware
{
//TokenContainer _tokenContainer;
private readonly RequestDelegate _requestDelegate;
public HeaderMiddleware()
{
}
public HeaderMiddleware(RequestDelegate requestDelegate)
{
_requestDelegate = requestDelegate;
//_tokenContainer = tokenContainer;
}
public async Task InvokeAsync(HttpContext context, TokenContainer tokenContainer)
{
var header = context.Request.Headers.Authorization;
tokenContainer.SetToken(header);
await _requestDelegate(context);
}
token 容器.cs:
public class TokenContainer
{
public string BearerToken { get; private set; }
public void SetToken(string token) => BearerToken = token;
}
HeaderHandler.cs:
public class HeaderHandler : DelegatingHandler
{
TokenContainer _tokenContainer;
public HeaderHandler()
{
}
public HeaderHandler(TokenContainer tokenContainer)
{
_tokenContainer = tokenContainer;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// for every request sent via the http client, intercept & add the bearer token header.
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", _tokenContainer.BearerToken);
// continue with request pipeline
return await base.SendAsync(request, cancellationToken);
}
}
FooGateway.cs:
public class FooGateway : IFooGateway
{
private readonly HttpClient _httpClient;
private readonly IConfiguration _configuration;
private readonly string _context = String.Empty;
public FooGateway(HttpClient httpClient, IConfiguration configuration)
{
_configuration = configuration;
_context = configuration["FooContext"];
_httpClient = httpClient;
}
public void DoSomething()
{
_httpClient.PostAsync("/blabla");
}
}
因此,我们的想法是,每个传入请求的承载 token 都将存储在一个名为 TokenContainer 的类中,HttpHandler 会将其添加到所有传出请求中。 但是,发生的情况是 token 存储在 TokenContainer 中,但 HeaderHandler 在其构造函数中获得了 TokenContainer 的不同实例,其 BearerToken 属性设置为 null。 有人可以解释为什么中间件中的相同 TokenContainer 实例没有传递到 HeaderHandler 中吗?
最佳答案
您看到的问题是因为 HttpMessageHandler 的生命周期不与请求的生命周期相同:通常,同一个处理程序将在许多请求中重复使用,并在到期时单独控制定时器等。
您不应期望注入(inject)到您的消息处理程序中的服务与注册为作用域时在其外部注入(inject)的对象相同。
正如文章所建议的,要使用与处理程序外部相同的作用域实例,您必须依赖 IHttpContextAccessor
来访问当前的 HttpContext
并获取服务从那里。所以你的处理程序实现看起来像这样:
public class HeaderHandler : DelegatingHandler
{
IHttpContextAccessor _httpContextAccessor;
public HeaderHandler()
{
}
public HeaderHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var tokenContainer = _httpContextAccessor
.HttpContext
.RequestServices
.GetRequiredService<TokenContainer>();
// for every request sent via the http client, intercept & add the bearer token header.
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", tokenContainer.BearerToken);
// continue with request pipeline
return await base.SendAsync(request, cancellationToken);
}
}
这应该确保 TokenContainer
实例在您当前的请求和 http 调用中是相同的。
请记住,要添加此功能,您需要像这样添加访问器:
services.AddHttpContextAccessor();
关于c# - 范围服务为请求创建两个不同的实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73152550/