我刚刚在GitHub上发现了Refit库(链接:https://github.com/reactiveui/refit(。除了我在这个巨大世界中的第一步之外,我还试图理解为什么当我们需要对 API 服务进行 http 调用时,使用这个库而不是使用通常的HttpClient会派上用场。通过阅读,我理解了自己创建httpClient,设置标头和其他配置的原因,太过旧和低级。这就是改装发生的地方。然后,我尝试向前迈出一步并阅读身份验证部分。我注意到,根据库的 github 页面,为了使身份验证工作,我们需要再次处理我们最终设法摆脱的HttpClient。官方页面上显示的示例是:
class AuthenticatedHttpClientHandler : HttpClientHandler
{
private readonly Func<Task<string>> getToken;
public AuthenticatedHttpClientHandler(Func<Task<string>> getToken)
{
if (getToken == null) throw new ArgumentNullException("getToken");
this.getToken = getToken;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// See if the request has an authorize header
var auth = request.Headers.Authorization;
if (auth != null)
{
var token = await getToken().ConfigureAwait(false);
request.Headers.Authorization = new AuthenticationHeaderValue(auth.Scheme, token);
}
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
}
class LoginViewModel
{
AuthenticationContext context = new AuthenticationContext(...);
private async Task<string> GetToken()
{
// The AcquireTokenAsync call will prompt with a UI if necessary
// Or otherwise silently use a refresh token to return
// a valid access token
var token = await context.AcquireTokenAsync("http://my.service.uri/app", "clientId", new Uri("callback://complete"));
return token;
}
public async void LoginAndCallApi()
{
var api = RestService.For<IMyRestService>(new HttpClient(new AuthenticatedHttpClientHandler(GetToken)) { BaseAddress = new Uri("https://the.end.point/") });
var location = await api.GetLocationOfRebelBase();
}
}
我想知道我在这里错过了什么概念。该库的目的是使用更高级的代码,设置足以调用 API 服务的接口。此目的在身份验证部分之前实现,因为所有 Http 设置等都是在后台故意进行的。但是一旦我们进入这个领域,我们就会再次发现 HttpHandlers、HttpRequestMessages 和 HttpClients 失去了库本身的目的。有人可以解释一下我在大局中缺少什么吗?提前致谢
我一直在尝试自己弄清楚身份验证,这是我自己在使用 Refit 时的观察。
TL;DR:有一些替代方法可以设置不需要使用 HttpClient 的身份验证,下面的观察 2 和 3。
至少有三种方法可以处理身份验证:
1( 如 GitHub 页面中所述,您可以使用 HttpClientHandler 传入 HttpClient,并在处理程序中设置授权标头。关于为什么需要使用处理程序,我注意到 Refit 会在发出 HTTP 请求之前将授权标头设置为属性中指定的任何值,如果您在创建 Refit 实例之前在 HttpClient 中设置标头,它将不起作用,例如这将不起作用:
[Get("/secretStuff")]
[Headers("Authorization: Bearer")]
Task<Location> GetLocationOfRebelBase();
. . .
var client = new HttpClient() { BaseAddress = new Uri("https://the.end.point/") };
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", someToken);
var api = RestService.For<IMyRestService>(client);
var location = await api.GetLocationOfRebelBase();
授权标头将为"授权:持有者",令牌将不存在。您需要在发出HTTP请求之前在HttpClientHandler(或DelgatingHandler(中更改HttpClient。
2(在创建Refit api客户端的新实例时,将基址传递给RestService.For而不是HttpClient,并指定AuthorizationHeaderValueGetter,例如:
var gitHubApi = RestService.For<IGitHubApi>("https://api.github.com", new RefitSettings {
AuthorizationHeaderValueGetter = () => {
var token = SomeMethodToGetAToken();
Task.FromResult(token);
}
});
3( 将令牌传递到 api 方法中,例如:
[Get("/users/{user}")]
Task<User> GetUser(string user, [Header("Authorization")] string authorization);
这在 Refit GitHub 页面中提到:https://github.com/reactiveui/refit#dynamic-headers。