我有一个HttpClient,我正在使用一个REST API。但是,我在设置授权标头时遇到了麻烦。我需要将标头设置为我从执行OAuth请求中接收到的令牌。 我看到了一些。net的代码,建议如下:

httpClient.DefaultRequestHeaders.Authorization = new Credential(OAuth.token);

然而,凭据类在WinRT中不存在。有人知道如何设置授权头吗?


当前回答

在这种情况下,你想发送HttpClient请求与持有者令牌,这段代码可以是一个很好的解决方案:

var requestMessage = new HttpRequestMessage
{
    Method = HttpMethod.Post,
    Content = new StringContent(".....", Encoding.UTF8, "application/json"),
    RequestUri = new Uri(".....")
};

requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "Your token");

var response = await _httpClient.SendAsync(requestMessage);

其他回答

这可能有助于设置标题:

WebClient client = new WebClient();

string authInfo = this.credentials.UserName + ":" + this.credentials.Password;
authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
client.Headers["Authorization"] = "Basic " + authInfo;
static async Task<AccessToken> GetToken()
{
        string clientId = "XXX";
        string clientSecret = "YYY";
        string credentials = String.Format("{0}:{1}", clientId, clientSecret);

        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes(credentials)));
            List<KeyValuePair<string, string>> requestData = new List<KeyValuePair<string, string>>();
            requestData.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
            FormUrlEncodedContent requestBody = new FormUrlEncodedContent(requestData);
            var request = await client.PostAsync("https://accounts.spotify.com/api/token", requestBody);
            var response = await request.Content.ReadAsStringAsync();
            return JsonConvert.DeserializeObject<AccessToken>(response);
        }
    }

你也可以使用下面的例子,它使用IHttpClientFactory:

    readonly IHttpClientFactory _httpClientFactory;
    
    public HTTPClientHelper(IHttpClientFactory httpClientFactory, string clientName = null)
    {
        this._httpClientFactory = httpClientFactory;
    }

    public Task<T> GetAsync(string url, string token) {

        var client = _httpClientFactory.CreateClient(_clientName);

        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(JwtBearerDefaults.AuthenticationScheme, token);

        using (HttpResponseMessage response = await _client.GetAsync(url)){
          ......
        }
     }

如果你想重用HttpClient,建议不要使用DefaultRequestHeaders,因为它们是用来发送每个请求的。

你可以试试这个:

var requestMessage = new HttpRequestMessage
    {
        Method = HttpMethod.Post,
        Content = new StringContent("...", Encoding.UTF8, "application/json"),
        RequestUri = new Uri("...")
    };

requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", 
    Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes($"{user}:{password}")));

var response = await _httpClient.SendAsync(requestMessage);

我偶然发现了这条旧线索。我遇到的问题是我知道使用静态HttpClient,但是我的令牌需要每59分钟刷新一次。

所以我可以使用HttpClientFactory,但是因为我的一个项目仍然在. net 4.8中,我创建了一个从HttpClient继承的类,所以我在所有项目中都有类似的代码。需要一个秘密才能获得令牌(我使用identityserver4)。

然后我将它设置为DI中的单例(我在这里使用Ninject):

Bind<MyHttpClient>().ToMethod(c =>
{
    var accessKey = ConfigurationManager.AppSettings["AccessKey"];

    var client = new MyHttpClient(accessKey)
    {
        BaseAddress = new Uri(MyUrls.MyApiBaseUrl)
    };

    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

    return client;
}).InSingletonScope();

然后是类本身——以它用来访问的API命名:

public class MyHttpClient : BaseHttpClient
{
     private static readonly HttpClient _authHttpClient = new HttpClient();
     private string _secret;

     public MyHttpClient(string secret)
     {
         _secret = secret;
     }

    /// <summary>
    /// Add the token to each and every request, cached for 1 minute less than the token's lifetime
    /// </summary>
    /// <param name="request"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        cancellationToken.ThrowIfCancellationRequested();

        var cacheSeconds = 3600 - 60; // Default of 59 minutes

        var token = CacheHelper<string>.Get("MyToken", cacheSeconds * 60, () =>
        {
            var authorityUrl = MyUrls.AuthServerUrl;

            // discover endpoints from metadata
            DiscoveryDocumentResponse disco;
            disco = _authHttpClient.GetDiscoveryDocumentAsync(authorityUrl).Result;
            if (disco.IsError)
            {
                throw new Exception("Error getting discovery document: " + disco.Error);
            }

            // request token
            var tokenResponse = _authHttpClient.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
            {
                Address = disco.TokenEndpoint,

                ClientId = "myapp",
                ClientSecret = _secret,
                Scope = "myapi"
            }).Result;

            if (tokenResponse.IsError)
            {
                throw new Exception("Error getting token: " + tokenResponse.Error);
            }

            if (tokenResponse.ExpiresIn < cacheSeconds + 60)
            {
                throw new Exception($"Token expires in {tokenResponse.ExpiresIn}s, which is less than {cacheSeconds + 60}");
            }

            if (tokenResponse.ExpiresIn > cacheSeconds + 60)
            {
                Log.Warn().Message($"Token expiry in {tokenResponse.ExpiresIn}s, which is greater than {cacheSeconds}").Write();
            }

            return tokenResponse.AccessToken;
        });

        // THIS IS THE BIT - Assign this inside a SendAsync override and you are done!
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
        return base.SendAsync(request, cancellationToken);
    }

}

最后为了完整起见,我的CacheHelper类看起来是这样的:

public static class CacheHelper<T>
{
    private static readonly object _locker = new object();

    public static T Get(string cacheName, int cacheTimeoutSeconds, Func<T> func)
    {
        var obj = MemoryCache.Default.Get(cacheName, null);
        if (obj != null) return (T)obj;

        lock (_locker)
        {
            obj = MemoryCache.Default.Get(cacheName, null);
            if (obj == null)
            {
                obj = func();
                var cip = new CacheItemPolicy
                {
                    AbsoluteExpiration = new DateTimeOffset(DateTime.UtcNow.AddSeconds(cacheTimeoutSeconds))
                };
                MemoryCache.Default.Set(cacheName, obj, cip);
            }
        }

        return (T)obj;
    }
}