Refit和授权头

18

目前,我正在像这样向我的请求添加一个授权头:

文件:SomeFile.cs

public interface ITestApi
{
    [Get("/api/test/{id}")]
    Task<string> GetTest([Header("Authorization")] string authorization, int id);

    [Get("/api/tests/")]
    Task<string> GetTests([Header("Authorization")] string authorization);
}

文件: SomeOtherFile.cs

var username = "xxx";
var password = "yyy";
var authHeader = "Basic " + Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
var baseAddress = "https://my.test.net";
ITestApi myApi = RestService.For<ITestApi>(baseAddress);

int id = 123;
var test= myApi.GetTest(authHeader, id);

var tests = myApi.GetTests(authHeader);

有没有一种方法可以全局设置authHeader,以便我不必在每次调用时将其作为参数传递?(我正在使用Refit版本4.6.48)。换句话说,我想能够像这样进行调用:

是否有办法全局设置authHeader,使得在每个调用中都不需要将其作为参数传递?(我使用的是 Refit 版本 4.6.48)。换句话说,我想以如下方式进行调用:

var test= myApi.GetTest(id);

var tests = myApi.GetTests();
3个回答

20

我找到了一个解决方案:

[Headers("Authorization: Basic")]
public interface ITestApi
{
    [Get("/api/test/{id}")]
    Task<string> GetTest(int id);

    [Get("/api/tests/")]
    Task<string> GetTests();
}


var username = "xxx";
var password = "yyy";
var authHeader = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password));
var baseAddress = "https://my.test.net";

var refitSettings = new RefitSettings()
{
    AuthorizationHeaderValueGetter = () => Task.FromResult(authHeader)
};

ITestApi myApi = RestService.For<ITestApi>(baseAddress, refitSettings);

int id = 123;
var test= myApi.GetTest(id);

var tests = myApi.GetTests();

2
太好了!这里的重要部分是使AuthorizationHeaderValueGetter工作并不被忽略的接口上的属性。[Headers("Authorization: Basic")]必须包括其中的: <scheme>部分,以便Refit调用AuthorizationHeaderValueGetter - jonfuller
如果我们想要一个动态的授权方案,比如这种情况下的“Basic”。 - mr5
这是正确的答案! - Tagi
有没有办法设置标题名称? - Shimmy Weitzhandler

8
答案对我没有用,因为Refit当前简单地 忽略AuthorizationHeaderValueGetter并且请求不包含认证头。

我可以通过向我的HttpClient提供默认的认证头使其工作:

string token = await GetTokenAsync().ConfigureAwait(false);
string endpointUrl = Environment.GetEnvironmentVariable(endpointVariable) ??
                     defaultEndpointUrl ?? DefaultEndpointUrl;

var client = new HttpClient(new HttpClientHandler())
{
    BaseAddress = new Uri(endpointUrl)
};
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

//TODO: remove as this is ignored anyway
//var refitSettings = new RefitSettings
//{
//    AuthorizationHeaderValueGetter = () => Task.FromResult($"{token}")
//};

var builder = RequestBuilder.ForType<TApiClient>();
return RestService.For(client, builder);

GetTokenAsync看起来像下面这样:

public static async Task<string> GetAccessTokenAsync()
{
    // theoretically the token could have expired during the tests, but there is very low chance
    // so returning the same token if one was generated
    lock (SyncLock)
    {
        if (!string.IsNullOrWhiteSpace(Token))
            return Token;
    }

    var client = new HttpClient();
    var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
    {
        Address = $"{IdentityServerUrl}/connect/token",
        ClientId = "MY_CLIENT_APITEST",
        ClientSecret = IdentityServerPass,
        Scope = "api.read"

    }).ConfigureAwait(false);
    tokenResponse.HttpResponse.EnsureSuccessStatusCode();

    lock (SyncLock)
    {
        Token = tokenResponse.AccessToken;
        return Token;
    }
}

使用OAuth令牌grant_type=client_credentials &client_id=example_client_id &client_secret=example_client_secret &scope=user.read - Kiquenet
GetTokenAsync 是什么? - Kiquenet
@Kiquenet 它用于从认证服务器获取有效令牌。 - Alexei - check Codidact
@Kiquenet,我终于记得搜索这段代码并将其添加到答案中了。 - Alexei - check Codidact
太棒了!!你是我的偶像。 - Kiquenet
GetAccessTokenAsync的代码不适用于生产环境。因为每个请求都试图获取此锁定。这是额外的开销。在这种情况下,最佳实践是在锁定之前检查IsNullOrWhiteSpace并返回现有值。 - DerSkythe

7
Refit已经改变了其方法。现在它使用默认的.NET HttpHandler。
var handler = new AuthHandler(credentials.Username, credentials.Password);
var githubApi = RestService.For<IGithubApi>(new HttpClient(handler)
                {
                    BaseAddress = new Uri("https://api.github.com")
                });


public class AuthHandler : HttpClientHandler
{
    private readonly string _username;
    private readonly string _password;

    public AuthHandler(string username, 
        string password)
    {
        _username = username;
        _password = password;
    }

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, 
        CancellationToken cancellationToken)
    {
        request.Headers.Add("User-Agent", "<your user name or app name>");
        request.Headers.Authorization = new AuthenticationHeaderValue("Basic",
            Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(
                $"{_username}:{_password}")));

        return await base.SendAsync(request, cancellationToken)
            .ConfigureAwait(false);
    }
}

2
我无法让它工作。似乎refit删除了头部的授权信息。在没有DI的Xamarin上进行了测试,直接使用。有什么建议吗? - Large

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接