寻求创建API/WebAPI客户端包的设计模式与指导方案
WebAPI客户端库设计指南与模式
核心设计原则
- 优先做好封装:把HTTP方法、请求头、序列化规则这些通信细节全藏起来,使用者只需要调用业务相关的方法,不用管底层怎么实现
- 极简配置:只对外开放必要的配置项(比如API基础地址、超时时间、认证密钥),其他默认规则由你统一管控
- 保持一致性:所有API方法的命名、参数结构、返回值格式统一,降低使用者的学习成本
- 预留扩展点但把控核心:可以给使用者留自定义拦截器、序列化器的口子,但核心通信逻辑必须由你控制,不能让使用者随意修改
常用设计模式
1. 门面模式(Facade)
提供一个统一的入口类(比如CompanyApiClient),把分散的业务API(用户、订单、权限等)聚合起来,也可以按业务拆分成子门面(UserApiClient、OrderApiClient),让使用者调用起来更直观。
示例代码:
public class CompanyApiClient { private readonly UserApiClient _userClient; private readonly OrderApiClient _orderClient; public CompanyApiClient(HttpClient httpClient) { _userClient = new UserApiClient(httpClient); _orderClient = new OrderApiClient(httpClient); } // 直接暴露子模块方法,或者提供子模块的访问属性 public Task<UserDto> GetUserById(int id) => _userClient.GetById(id); public Task<OrderDto> CreateOrder(CreateOrderRequest request) => _orderClient.Create(request); }
2. 适配器模式(Adapter)
如果内部API混用了不同通信协议(比如部分REST、部分gRPC),用适配器把不同的实现统一成相同的对外接口,使用者不用关心底层协议差异。
示例代码:
// 统一对外接口 public interface IUserApi { Task<UserDto> GetById(int id); } // REST协议实现 public class RestUserApi : IUserApi { private readonly HttpClient _httpClient; public RestUserApi(HttpClient httpClient) => _httpClient = httpClient; public async Task<UserDto> GetById(int id) { var response = await _httpClient.GetAsync($"users/{id}"); response.EnsureSuccessStatusCode(); return await response.Content.ReadFromJsonAsync<UserDto>(); } } // 未来换成gRPC时,只需要新增适配器,对外接口不变 public class GrpcUserApi : IUserApi { private readonly UserService.UserServiceClient _client; public GrpcUserApi(UserService.UserServiceClient client) => _client = client; public async Task<UserDto> GetById(int id) { var response = await _client.GetUserByIdAsync(new GetUserByIdRequest { Id = id }); return MapToUserDto(response.User); } }
3. 拦截器模式(Interceptor)
用来统一处理请求前后的通用逻辑,比如自动加认证头、记录请求日志、统一异常处理、重试机制,这部分完全由你管控,使用者不用操心。
示例代码(以.NET HttpClient为例):
public class ApiRequestHandler : DelegatingHandler { protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // 请求前:自动添加统一认证头 request.Headers.Add("X-Company-Api-Key", GetApiKeyFromConfig()); // 发送请求 var response = await base.SendAsync(request, cancellationToken); // 请求后:统一处理异常 if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); throw new CompanyApiException(response.StatusCode, errorContent); } return response; } }
具体实现步骤
- 统一DTO模型:把API的请求/响应数据模型都放在客户端库里,确保所有使用者用的是同一套结构,避免版本不一致导致的问题
- 简化配置管理:提供简洁的配置方式,推荐用依赖注入传入配置对象,示例:
public class CompanyApiConfig { public string BaseUrl { get; set; } public int TimeoutSeconds { get; set; } = 30; public string ApiKey { get; set; } } - 集成依赖注入:针对主流框架(比如.NET、Spring Boot)提供DI扩展方法,让使用者一行代码就能完成注入,示例:
public static class ServiceCollectionExtensions { public static IServiceCollection AddCompanyApiClient(this IServiceCollection services, Action<CompanyApiConfig> configure) { var config = new CompanyApiConfig(); configure(config); services.AddSingleton(config); services.AddHttpClient<CompanyApiClient>(client => { client.BaseAddress = new Uri(config.BaseUrl); client.Timeout = TimeSpan.FromSeconds(config.TimeoutSeconds); }).AddHttpMessageHandler<ApiRequestHandler>(); services.AddTransient<ApiRequestHandler>(); return services; } } - 自定义异常类型:封装统一的异常(比如
CompanyApiException),包含API返回的错误码和消息,方便使用者捕获和处理 - 内置文档与示例:在库的README里写清楚快速开始步骤、方法说明、常见问题,比如:
快速开始:
- 安装包:
Install-Package Company.Api.Client - 在Startup中注入:
services.AddCompanyApiClient(config => { config.BaseUrl = "https://api.company.com"; config.ApiKey = "your-api-key"; }); - 在业务类中调用:
private readonly CompanyApiClient _apiClient; public MyService(CompanyApiClient apiClient) => _apiClient = apiClient; public async Task DoSomething() { var user = await _apiClient.GetUserById(123); // 业务逻辑 }
- 安装包:
最佳实践
- 版本同步:客户端库版本和API版本保持对应,比如库版本
v1.2.3对应API的v1版本,API升级到v2时发布新的客户端库版本(或者支持多版本切换) - 向后兼容:尽量不修改现有方法的签名,新增功能用新方法,确保老版本使用者不用改代码
- 测试覆盖:在客户端库里写集成测试,调用真实API(或Mock),确保通信逻辑正确,也方便你在API变更时验证兼容性
- 隐藏底层细节:别把HttpClient这类底层对象暴露给使用者,防止他们修改你管控的通信规则
内容的提问来源于stack exchange,提问作者Mate
相关产品推荐
相关产品推荐

