7

我刚刚在 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 失去了库本身的用途。有人可以解释一下我在大局中缺少什么吗?提前致谢

4

1 回答 1

4

我一直在尝试自己弄清楚身份验证,这是我自己在使用 Refit 时的观察。

TL;DR:有一些替代方法可以设置不需要使用 HttpClient 的身份验证,下面的观察 2 和 3。

至少有三种方式来处理身份验证:

1) 如 GitHub 页面中所述,您可以通过 HttpClientHandler 传入 HttpClient 并在处理程序中设置 Authorization 标头。至于为什么需要使用处理程序,我注意到 Refit 将在发出 HTTP 请求之前将 Authorization 标头设置为属性中指定的任何值,如果您在创建 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 。

于 2019-07-21T16:17:13.370 回答