C# 无法从托管服务中的缓存附加身份验证令牌

C# 无法从托管服务中的缓存附加身份验证令牌,c#,.net,asp.net-core,.net-core,C#,.net,Asp.net Core,.net Core,我是.net新手,我非常想了解应用程序的这种行为。我有一个名为GetOrg()的函数,它基本上请求API端点并获取数据,以获取我需要的数据,以便在每次请求中传递Auth令牌。要获取身份验证令牌,我有另一个函数调用GetAccessToken获取令牌并将其保存在缓存中。我创建了一个名为httpclient,它将令牌附加到NonProductionEnv客户端的每个请求 现在的问题是,当我在托管服务中设置GetOrg()时,如下图所示,它没有在没有身份验证令牌的情况下附加令牌和请求API端点,但是如

我是.net新手,我非常想了解应用程序的这种行为。我有一个名为
GetOrg()
的函数,它基本上请求API端点并获取数据,以获取我需要的数据,以便在每次请求中传递Auth令牌。要获取身份验证令牌,我有另一个函数调用
GetAccessToken
获取令牌并将其保存在缓存中。我创建了一个名为
httpclient
,它将令牌附加到
NonProductionEnv
客户端的每个请求

现在的问题是,当我在托管服务中设置
GetOrg()
时,如下图所示,它没有在没有身份验证令牌的情况下附加令牌和请求API端点,但是如果我在控制器中设置
GetOrg()
,它就可以正常工作

托管服务:

public class TokenService : DelegatingHandler, IHostedService
{
    public IConfiguration Configuration { get; }
    protected IMemoryCache _cache;
    private Timer _timer;
    public IHttpClientFactory _clientFactory;
    protected HttpClient _client_NP;
    private readonly IServiceScopeFactory _scopeFactory;

    public TokenService(IConfiguration configuration, IMemoryCache memoryCache, IHttpClientFactory clientFactory, IServiceScopeFactory scopeFactory)
    {
        Configuration = configuration;
        _cache = memoryCache;
        _clientFactory = clientFactory;
        _scopeFactory = scopeFactory;

        // NamedClients foreach Env.
        _client_NP = _clientFactory.CreateClient("NonProductionEnv");
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _timer = new Timer(GetAccessToken, null, 0, 3300000);
        // Thread.Sleep(2000);
        _timer = new Timer(Heartbeat, null, 1000, 1000);
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        //Timer does not have a stop. 
        _timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }

    public async Task<Token> GetToken(Uri authenticationUrl, Dictionary<string, string> authenticationCredentials)
    {
        HttpClient client = new HttpClient();
        FormUrlEncodedContent content = new FormUrlEncodedContent(authenticationCredentials);
        HttpResponseMessage response = await client.PostAsync(authenticationUrl, content);

        if (response.StatusCode != System.Net.HttpStatusCode.OK)
        {
            string message = String.Format("POST failed. Received HTTP {0}", response.StatusCode);
            throw new ApplicationException(message);
        }

        string responseString = await response.Content.ReadAsStringAsync();
        Token token = JsonConvert.DeserializeObject<Token>(responseString);

        return token;
    }

    private void GetAccessToken(object state)
    {
        Dictionary<string, string> authenticationCredentials_np = Configuration.GetSection("NonProductionEnvironment:Credentials").GetChildren().Select(x => new KeyValuePair<string, string>(x.Key, x.Value)).ToDictionary(x => x.Key, x => x.Value);
        Token token_np = GetToken(new Uri(Configuration["NonProductionEnvironment:URL"]), authenticationCredentials_np).Result;

        _cache.Set("np", token_np.AccessToken);
    }

    public void Heartbeat(object state)
    {
        // Discard the result
        _ = GetOrg();
    }

    public async Task GetOrg()
    {
        var request = new HttpRequestMessage(HttpMethod.Get, "organizations");
        var response = await _client_NP.SendAsync(request);
        var json = await response.Content.ReadAsStringAsync();
        OrganizationsClass.OrgsRootObject model = JsonConvert.DeserializeObject<OrganizationsClass.OrgsRootObject>(json);

        using (var scope = _scopeFactory.CreateScope())
        {
            var _DBcontext = scope.ServiceProvider.GetRequiredService<PCFStatusContext>();

            foreach (var item in model.resources)
            {
                var g = Guid.Parse(item.guid);
                var x = _DBcontext.Organizations.FirstOrDefault(o => o.OrgGuid == g);
                if (x == null)
                {
                    _DBcontext.Organizations.Add(new Organizations
                    {
                        OrgGuid = g,
                        Name = item.name,
                        CreatedAt = item.created_at,
                        UpdatedAt = item.updated_at,
                        Timestamp = DateTime.Now,
                        Foundation = 3
                    });
                }
                else if (x.UpdatedAt != item.updated_at)
                {
                    x.CreatedAt = item.created_at;
                    x.UpdatedAt = item.updated_at;
                    x.Timestamp = DateTime.Now;
                }
            }

            await GetSpace();
            await _DBcontext.SaveChangesAsync();
        }
    }

}
下面的日志每秒生成一次,因为我需要每秒调用api端点

正在收听:正在收听: 应用程序已启动。按Ctrl+C组合键关闭。 信息:System.Net.Http.HttpClient.NonProductionNV.LogicalHandler[100] 开始处理HTTP请求获取信息: System.Net.Http.HttpClient.NonProductionEnv.ClientHandler[100] 发送HTTP请求获取信息:System.Net.HTTP.HttpClient.NonProductionEnv.ClientHandler[101] 53.3973ms后收到HTTP响应-未经授权


注:我对.net及其工作原理了解不多。

您的
HttpClient
正在注册一个静态值,该值在应用程序启动时从缓存中检索。此时,托管服务尚未运行,所以缓存中还没有任何值。一旦缓存中最终有了一个值,标头就已经设置很久了,您永远不会重置它

在这里,缓存实际上是完全不必要的。您也不需要在实际的客户端注册中设置
授权
头。相反,只需将
GetAccessToken
方法修改为:

private void GetAccessToken(object state)
{
    Dictionary<string, string> authenticationCredentials_np = Configuration.GetSection("NonProductionEnvironment:Credentials").GetChildren().Select(x => new KeyValuePair<string, string>(x.Key, x.Value)).ToDictionary(x => x.Key, x => x.Value);
    Token token_np = GetToken(new Uri(Configuration["NonProductionEnvironment:URL"]), authenticationCredentials_np).Result;

    _client_NP.DefaultRequestHeaders.Add("Authorization", $"Bearer {token_np.AccessToken}");
}
private void GetAccessToken(对象状态)
{
字典身份验证凭据\u np=Configuration.GetSection(“非生产环境:凭据”).GetChildren().Select(x=>newkeyValuePair(x.Key,x.Value)).ToDictionary(x=>x.Key,x=>x.Value);
令牌\u np=GetToken(新Uri(配置[“非生产环境:URL]”),authenticationCredentials\u np);
_client_NP.DefaultRequestHeaders.Add(“Authorization”、$“Bearer{token_NP.AccessToken}”);
}

您的
HttpClient
正在注册一个静态值,该值在应用程序启动时从缓存中检索。此时,托管服务尚未运行,所以缓存中还没有任何值。一旦缓存中最终有了一个值,标头就已经设置很久了,您永远不会重置它

在这里,缓存实际上是完全不必要的。您也不需要在实际的客户端注册中设置
授权
头。相反,只需将
GetAccessToken
方法修改为:

private void GetAccessToken(object state)
{
    Dictionary<string, string> authenticationCredentials_np = Configuration.GetSection("NonProductionEnvironment:Credentials").GetChildren().Select(x => new KeyValuePair<string, string>(x.Key, x.Value)).ToDictionary(x => x.Key, x => x.Value);
    Token token_np = GetToken(new Uri(Configuration["NonProductionEnvironment:URL"]), authenticationCredentials_np).Result;

    _client_NP.DefaultRequestHeaders.Add("Authorization", $"Bearer {token_np.AccessToken}");
}
private void GetAccessToken(对象状态)
{
字典身份验证凭据\u np=Configuration.GetSection(“非生产环境:凭据”).GetChildren().Select(x=>newkeyValuePair(x.Key,x.Value)).ToDictionary(x=>x.Key,x=>x.Value);
令牌\u np=GetToken(新Uri(配置[“非生产环境:URL]”),authenticationCredentials\u np);
_client_NP.DefaultRequestHeaders.Add(“Authorization”、$“Bearer{token_NP.AccessToken}”);
}

DefaultHeaders给了我一个错误
“HttpClient”不包含“DefaultHeaders”的定义,并且找不到接受“HttpClient”类型的第一个参数的可访问扩展方法“DefaultHeaders”(是否缺少using指令或程序集引用?)
因此我将其替换为
DefaultRequestHeaders
,它给了我
未处理的异常:System.FormatException:无法添加值,因为标题“Authorization”不支持多个值。
很抱歉,它是
DefaultRequestHeaders
。我将更新我的答案。程序运行,但行为奇怪有时它附加了令牌,有时不是问题所在?DefaultHeaders给了我一个错误
“HttpClient”不包含“DefaultHeaders”的定义,并且没有可访问的扩展方法“DefaultHeaders”可以接受“HttpClient”类型的第一个参数找不到(是否缺少using指令或程序集引用?
因此我将其替换为
DefaultRequestHeaders
,它给我的
未处理异常:System.FormatException:无法添加值,因为标头“Authorization”不支持多个值。
很抱歉,它是
DefaultRequestHeaders
。我会更新我的答案。程序运行,但行为很奇怪有时它附加了令牌,有时没有什么问题?