项目

RestSharp 示例

RestSharp 最好作为 API 代理类的基础。每个 API 可能需要不同的 RestClient 设置。因此,专用的 API 类(及其接口)可以为不同 RestClient 实例提供良好的隔离,并使其易于测试。

例如,让我们看一个使用 OAuth2 机器到机器身份验证的简单 Twitter API v2 客户端。要使其工作,您需要访问 Twitter 开发者门户,拥有一个项目,并在该项目中有一个启用 OAuth2 的批准应用。

客户端模型

在实现 API 客户端之前,我们需要为其创建一个模型。模型包括客户端的抽象,它包含我们感兴趣要实现的 API 调用的功能。此外,客户端模型还应包含必要的请求和响应模型。通常,这些是简单的类或记录,没有逻辑,通常称为 DTO(数据传输对象)。

这个示例从一个获取单个 Twitter 用户的函数开始。首先,我们定义 API 客户端接口:

public interface ITwitterClient {
    Task<TwitterUser> GetUser(string user);
}

由于该函数返回一个 TwitterUser 实例,我们需要为此定义一个模型:

public record TwitterUser(string Id, string Name, string Username);

客户端实现

完成上述步骤后,我们可以实现接口并添加所有必要代码块,以获得功能完备的 API 客户端。

客户端类需要以下内容:

  • 用于传递 API 凭据的构造函数
  • 配置了 Twitter API 基础 URI 的 RestClient 实例包装器
  • 支持使用 Twitter OAuth2 认证授权客户端的认证器
  • 获取用户的实际函数(实现 ITwitterClient 接口)

创建认证器的详细说明见 下文

以下是客户端实现可能的样子:

public class TwitterClient : ITwitterClient, IDisposable {
    readonly RestClient _client;

    public TwitterClient(string apiKey, string apiKeySecret) {
        var options = new RestClientOptions("https://api.twitter.com/2");
        _client = new RestClient(options);
    }

    public async Task<TwitterUser> GetUser(string user) {
        var response = await _client.GetJsonAsync<TwitterSingleObject<TwitterUser>>(
            "users/by/username/{user}",
            new { user }
        );
        return response!.Data;
    }

    record TwitterSingleObject<T>(T Data);

    public void Dispose() {
        _client?.Dispose();
        GC.SuppressFinalize(this);
    }
}

还可以使用 ASP.NET Core 选项来配置客户端,而不是将凭据作为字符串传递。例如,我们可以添加一个用于 Twitter 客户端选项的类,并在构造函数中使用它:

public class TwitterClientOptions(string ApiKey, string ApiSecret);

public TwitterClient(IOptions<TwitterClientOptions> options) {
    var opt = new RestClientOptions("https://api.twitter.com/2");
    _client = new RestClient(options);
}

然后,您可以使用 ASP.NET Core 的依赖注入容器注册和配置客户端。

目前,客户端实际上不会工作,因为 Twitter API 需要身份验证。这将在下一节中介绍。

认证器

在调用 API 本身之前,我们需要获取访问令牌。Twitter 提供了一个端点 https://api.twitter.com/oauth2/token。由于它遵循 OAuth2 规范,这段代码可用于为其他供应商创建认证器。

首先,我们需要一个模型来反序列化令牌端点响应。OAuth2 使用蛇形命名法为属性命名,因此我们需要为模型属性添加 JsonPropertyName 属性:

record TokenResponse {
    [JsonPropertyName("token_type")]
    public string TokenType { get; init; }
    [JsonPropertyName("access_token")]
    public string AccessToken { get; init; }
}

接下来,我们创建认证器本身。它需要 API 密钥和 API 密钥秘密,以便使用基本 HTTP 认证调用令牌端点。此外,我们还可以扩展参数列表,将基 URL 转换为更通用的 OAuth2 认证器。

创建认证器最简单的方法是继承 AuthenticatorBase 基类:

public class TwitterAuthenticator : AuthenticatorBase {
    readonly string _baseUrl;
    readonly string _clientId;
    readonly string _clientSecret;

    public TwitterAuthenticator(string baseUrl, string clientId, string clientSecret) : base("") {
        _baseUrl      = baseUrl;
        _clientId     = clientId;
        _clientSecret = clientSecret;
    }

    protected override async ValueTask<Parameter> GetAuthenticationParameter(string accessToken) {
        Token = string.IsNullOrEmpty(Token) ? await GetToken() : Token;
        return new HeaderParameter(KnownHeaders.Authorization, Token);
    }
}

当客户端首次使用该认证器进行调用时,它会发现 Token 属性为空。然后,它将调用 GetToken 函数获取一次令牌,并后续重复使用。

现在,我们需要在类中实现 GetToken 函数:

async Task<string> GetToken() {
    var options = new RestClientOptions(_baseUrl){
        Authenticator = new HttpBasicAuthenticator(_clientId, _clientSecret),
    };
    using var client = new RestClient(options);

    var request = new RestRequest("oauth2/token")
        .AddParameter("grant_type", "client_credentials");
    var response = await client.PostAsync<TokenResponse>(request);
    return $"{response!.TokenType} {response!.AccessToken}";
}

由于我们需要调用令牌端点,因此需要自己创建一个短期使用的 RestClient 实例。与实际的 Twitter 客户端不同,它将使用 HttpBasicAuthenticator 以用户名和密码形式发送 API 密钥和秘密。然后,我们将客户端释放,因为我们只使用它一次。

在这里,我们添加了一个 POST 参数 grant_type,其值为 client_credentials。目前,这是唯一支持的值。

POST 请求将默认使用 application/x-www-form-urlencoded 内容类型。

**注意:**本页面提供的示例代码是生产代码。例如,如果在令牌尚未获取到时同时发出多个请求,认证器可能会产生不期望的副作用。可以通过使用信号量或同步调用解决,而不是简单地这样做。

最后的话

此页面演示了如何实现一个可配置的、具有自身接口的类型化的 API 客户端。这里没有涵盖应用程序中使用客户端的方式,因为不同的应用程序类型和目标框架有它们自己的惯用 HTTP 客户端用法。

在本文档中