c# - 范围服务为请求创建两个不同的实例

标签 c# asp.net-core dependency-injection

我是 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)的对象相同。

https://andrewlock.net/understanding-scopes-with-ihttpclientfactory-message-handlers/#scope-duration-in-ihttpclientfactory

正如文章所建议的,要使用与处理程序外部相同的作用域实例,您必须依赖 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/

相关文章:

c# - 使用 C# 和 .NET 2.0 连接到本地 MySQL 数据库时出现问题

c# - 在 Windows Phone 8 中同时播放两个音频文件的最佳方式

asp.net-core - ASP.Net Core (MVC6) 无法使用种子用户登录

javascript - 如何在 angularjs 的 app.config 中注入(inject)依赖项?

c# - 重构字典,改变键类型

c# - 使用iphone从PC发送短信

c# - 根据Route在Controller中注入(inject)不同的实现

c# - 将加载配置添加到asp.net core中的注入(inject)依赖项

c# - IoC - 使用注入(inject)的对象参数解析 WPF 窗口

testing - 在 Grails Spock 规范测试中注入(inject)依赖项