动态 C# API 客户端代理
ABP 可以动态创建 C# API 客户端代理来调用您的远程 HTTP 服务(REST API)。这样,您就不需要处理 HttpClient 和其他低级细节来调用远程服务并获取结果。
动态 C# 代理自动为您处理以下事项:
- 通过考虑 HTTP 方法、路由、查询字符串参数、请求负载和其他细节,将 C# 方法调用映射到远程服务器 HTTP 调用。
- 通过向 HTTP 头添加访问令牌来验证 HTTP 客户端。
- 与 JSON 之间进行序列化和反序列化。
- 处理 HTTP API 版本控制。
- 向请求添加关联 ID、当前租户 ID 和当前文化。
- 正确处理服务器发送的错误消息并抛出适当的异常。
任何类型的 .NET 客户端都可以使用此系统来消费您的 HTTP API。
静态与动态客户端代理
ABP 提供两种类型的客户端代理生成系统。本文档介绍动态客户端代理,它在运行时生成客户端代理。您也可以查看 静态 C# API 客户端代理 文档,了解如何在开发时生成代理。
开发时(静态)客户端代理生成具有性能优势,因为它不需要在运行时获取 HTTP API 定义。但是,每当您更改 API 端点定义时,您都应该重新生成客户端代理代码。另一方面,动态客户端代理在运行时生成,提供更简单的开发体验。
服务接口
您的服务/控制器应实现一个在服务器和客户端之间共享的接口。因此,首先在共享库项目中定义一个服务接口,通常是在 Application.Contracts 项目中(如果您使用启动模板创建解决方案)。
示例:
public interface IBookAppService : IApplicationService
{
Task<List<BookDto>> GetListAsync();
}
您的接口应实现
IRemoteService接口以便自动发现。由于IApplicationService继承了IRemoteService接口,上面的IBookAppService满足此条件。
在您的服务应用程序中实现此类。您可以使用 自动 API 控制器系统 将服务公开为 REST API 端点。
客户端代理生成
启动模板已在
HttpApi.Client项目中预先配置了客户端代理生成。
如果您不使用启动模板,请在包含客户端项目的 .csproj 文件的文件夹中执行以下命令:
abp add-package Volo.Abp.Http.Client
现在,可以创建客户端代理了。示例:
[DependsOn(
typeof(AbpHttpClientModule), //用于创建客户端代理
typeof(BookStoreApplicationContractsModule) //包含应用服务接口
)]
public class MyClientAppModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
//创建动态客户端代理
context.Services.AddHttpClientProxies(
typeof(BookStoreApplicationContractsModule).Assembly
);
}
}
AddHttpClientProxies 方法获取一个程序集,查找给定程序集中的所有服务接口,创建并注册代理类。
端点配置
默认情况下,appsettings.json 文件中的 RemoteServices 部分用于获取远程服务地址。最简单的配置如下所示:
{
"RemoteServices": {
"Default": {
"BaseUrl": "http://localhost:53929/"
}
}
}
更详细的配置请参见下面的“AbpRemoteServiceOptions”部分。
使用
使用起来很简单。只需在客户端应用程序代码中注入服务接口:
public class MyService : ITransientDependency
{
private readonly IBookAppService _bookService;
public MyService(IBookAppService bookService)
{
_bookService = bookService;
}
public async Task DoIt()
{
var books = await _bookService.GetListAsync();
foreach (var book in books)
{
Console.WriteLine($"[BOOK {book.Id}] Name={book.Name}");
}
}
}
此示例注入了上面定义的 IBookAppService 服务接口。每当客户端调用服务方法时,动态客户端代理实现都会进行 HTTP 调用。
IHttpClientProxy 接口
虽然您可以像上面那样注入 IBookAppService 来使用客户端代理,但为了更显式的用法,您可以注入 IHttpClientProxy<IBookAppService>。在这种情况下,您将使用 IHttpClientProxy<T> 接口的 Service 属性。
配置
AbpRemoteServiceOptions
默认情况下,AbpRemoteServiceOptions 会自动从 appsettings.json 中设置。或者,您可以在 模块 的 ConfigureServices 方法中配置它以设置或覆盖它。示例:
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.Configure<AbpRemoteServiceOptions>(options =>
{
options.RemoteServices.Default =
new RemoteServiceConfiguration("http://localhost:53929/");
});
//...
}
多个远程服务端点
上面的示例配置了“Default”远程服务端点。您可能针对不同的服务有不同的端点(例如在微服务架构中,每个微服务有不同的端点)。在这种情况下,您可以将其他端点添加到配置文件中:
{
"RemoteServices": {
"Default": {
"BaseUrl": "http://localhost:53929/"
},
"BookStore": {
"BaseUrl": "http://localhost:48392/"
}
}
}
AddHttpClientProxies 方法可以获取一个额外的参数来指定远程服务名称。示例:
context.Services.AddHttpClientProxies(
typeof(BookStoreApplicationContractsModule).Assembly,
remoteServiceConfigurationName: "BookStore"
);
remoteServiceConfigurationName 参数匹配通过 AbpRemoteServiceOptions 配置的服务端点。如果未定义 BookStore 端点,则会回退到 Default 端点。
远程服务配置提供程序
在某些情况下,您可能需要获取特定远程服务的远程服务配置。为此,您可以使用 IRemoteServiceConfigurationProvider 接口。
示例:获取“BookStore”远程服务的远程服务配置
public class MyService : ITransientDependency
{
private readonly IRemoteServiceConfigurationProvider _remoteServiceConfigurationProvider;
public MyService(IRemoteServiceConfigurationProvider remoteServiceConfigurationProvider)
{
_remoteServiceConfigurationProvider = remoteServiceConfigurationProvider;
}
public async Task GetRemoteServiceConfiguration()
{
var configuration = await _remoteServiceConfigurationProvider.GetConfigurationOrDefaultAsync("BookStore");
Console.WriteLine(configuration.BaseUrl);
}
}
作为默认服务
当您为 IBookAppService 创建服务代理时,可以直接注入 IBookAppService 来使用代理客户端(如使用部分所示)。您可以将 asDefaultServices: false 传递给 AddHttpClientProxies 方法来禁用此功能。
context.Services.AddHttpClientProxies(
typeof(BookStoreApplicationContractsModule).Assembly,
asDefaultServices: false
);
仅当您的应用程序已经有一个服务的实现,并且您不希望用客户端代理覆盖/替换另一个实现时,才需要使用 asDefaultServices: false。
如果禁用了
asDefaultServices,则只能使用IHttpClientProxy<T>接口来使用客户端代理。请参见上面的 IHttpClientProxy 接口部分。
重试/失败逻辑与 Polly 集成
如果您想为客户端代理的失败远程 HTTP 调用添加重试逻辑,可以在模块类的 PreConfigureServices 方法中配置 AbpHttpClientBuilderOptions。
示例:使用 Polly 库在失败时重试 3 次
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpHttpClientBuilderOptions>(options =>
{
options.ProxyClientBuildActions.Add((remoteServiceName, clientBuilder) =>
{
clientBuilder.AddTransientHttpErrorPolicy(policyBuilder =>
policyBuilder.WaitAndRetryAsync(
3,
i => TimeSpan.FromSeconds(Math.Pow(2, i))
)
);
});
});
}
此示例使用了 Microsoft.Extensions.Http.Polly 包。您还需要导入 Polly 命名空间(using Polly;)才能使用 WaitAndRetryAsync 方法。
抠丁客


