我正在开发一个调用Web服务的UWP应用程序。为此,我使用来自Windows.Web.Http命名空间的HttpClient对象,并将IHttpFilter对象传递给它的构造函数。此过滤器负责身份验证过程。我根据此link建立了我的解决方案,并且身份验证逻辑基于this
我不知道我做错了什么,但我得到了这个例外:一个方法在意外时间被调用。 (来自HRESULT的异常:0x8000000E)
我测试的场景是令牌无效,尽管它被认为是有效的。在这种情况下(现在> TokenManager.Token.ExpiresOn)条件为假,然后添加授权头(带有无效令牌),然后发送请求,然后检查http响应代码和www-authenticate头并且如果需要,必须通过刷新令牌请求新的访问令牌以进行重试。当我在抛出异常时(第二次)到达此行时:response = await InnerFilter.SendRequestAsync(request).AsTask(cancellationToken,progress);
我不知道我做错了什么。我已经看到了另外一个问题,人们遇到了这个错误,通常是因为他们试图在不等待任务完成的情况下获得任务的结果,但我在使用await关键字所有异步方法。
public class AuthFilter : HttpFilter
{
public override IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress> SendRequestAsync(HttpRequestMessage request)
{
return AsyncInfo.Run<HttpResponseMessage, HttpProgress>(async (cancellationToken, progress) =>
{
var retry = true;
if (TokenManager.TokenExists)
{
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
if (now > TokenManager.Token.ExpiresOn)
{
retry = false;
await RefreshTokenAsync();
}
request.Headers.Authorization = new HttpCredentialsHeaderValue(TokenManager.Token.TokenType, TokenManager.Token.AccessToken);
}
HttpResponseMessage response = await InnerFilter.SendRequestAsync(request).AsTask(cancellationToken, progress);
cancellationToken.ThrowIfCancellationRequested();
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
var authHeader = response.Headers.WwwAuthenticate.SingleOrDefault(x => x.Scheme == "Bearer");
if (authHeader != null)
{
var challenge = ParseChallenge(authHeader.Parameters);
if (challenge.Error == "token_expired" && retry)
{
var success = await RefreshTokenAsync();
if (success)
{
request.Headers.Authorization = new HttpCredentialsHeaderValue(TokenManager.Token.TokenType, TokenManager.Token.AccessToken);
response = await InnerFilter.SendRequestAsync(request).AsTask(cancellationToken, progress);
}
}
}
}
return response;
});
}
private async Task<bool> RefreshTokenAsync()
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Accept.Add(new HttpMediaTypeWithQualityHeaderValue("application/json"));
var content = new HttpStringContent(JsonConvert.SerializeObject(new { RefreshToken = TokenManager.Token.RefreshToken }), UnicodeEncoding.Utf8, "application/json");
var response = await httpClient.PostAsync(SettingsService.Instance.WebApiUri.Append("api/login/refresh-token"), content);
if (response.IsSuccessStatusCode)
TokenManager.Token = JsonConvert.DeserializeObject<Token>(await response.Content.ReadAsStringAsync());
return response.IsSuccessStatusCode;
}
}
private (string Realm, string Error, string ErrorDescription) ParseChallenge(IEnumerable<HttpNameValueHeaderValue> input)
{
var realm = input.SingleOrDefault(x => x.Name == "realm")?.Value ?? string.Empty;
var error = input.SingleOrDefault(x => x.Name == "error")?.Value ?? string.Empty;
var errorDescription = input.SingleOrDefault(x => x.Name == "error_description")?.Value ?? string.Empty;
return (realm, error, errorDescription);
}
public override void Dispose()
{
InnerFilter.Dispose();
GC.SuppressFinalize(this);
}
}
public abstract class HttpFilter : IHttpFilter
{
public IHttpFilter InnerFilter { get; set; }
public HttpFilter()
{
InnerFilter = new HttpBaseProtocolFilter();
}
public abstract IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress> SendRequestAsync(HttpRequestMessage request);
public abstract void Dispose();
}
修改
根据这些链接(link,link),我似乎无法两次发送相同的请求。但我需要重新发送相同的请求,但具有不同的身份验证标头。我怎样才能做到这一点?
答案 0 :(得分:1)
好的,经过长时间的努力,我最终做到了这一点:
public override IAsyncOperationWithProgress<HttpResponseMessage, HttpProgress> SendRequestAsync(HttpRequestMessage request)
{
return AsyncInfo.Run<HttpResponseMessage, HttpProgress>(async (cancellationToken, progress) =>
{
var retry = true;
if (TokenManager.TokenExists)
{
var now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
if (now > TokenManager.Token.ExpiresOn)
{
retry = false;
await RefreshTokenAsync();
}
request.Headers.Authorization = new HttpCredentialsHeaderValue(TokenManager.Token.TokenType, TokenManager.Token.AccessToken);
}
HttpResponseMessage response = await InnerFilter.SendRequestAsync(request).AsTask(cancellationToken, progress);
cancellationToken.ThrowIfCancellationRequested();
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
var authHeader = response.Headers.WwwAuthenticate.SingleOrDefault(x => x.Scheme == "Bearer");
if (authHeader != null)
{
var challenge = ParseChallenge(authHeader.Parameters);
if (challenge.Error == "token_expired" && retry)
{
var secondRequest = request.Clone();
var success = await RefreshTokenAsync();
if (success)
{
secondRequest.Headers.Authorization = new HttpCredentialsHeaderValue(TokenManager.Token.TokenType, TokenManager.Token.AccessToken);
response = await InnerFilter.SendRequestAsync(secondRequest).AsTask(cancellationToken, progress);
}
}
}
}
return response;
});
}
public static HttpRequestMessage Clone(this HttpRequestMessage request)
{
var clone = new HttpRequestMessage(request.Method, request.RequestUri)
{
Content = request.Content
};
foreach (KeyValuePair<string, object> prop in request.Properties.ToList())
{
clone.Properties.Add(prop);
}
foreach (KeyValuePair<string, string> header in request.Headers.ToList())
{
clone.Headers.Add(header.Key, header.Value);
}
return clone;
}
因为我需要重新发送请求,所以我再次请求克隆第一个请求。