Mud.ServiceCodeGenerator
1.2.6
See the version list below for details.
dotnet add package Mud.ServiceCodeGenerator --version 1.2.6
NuGet\Install-Package Mud.ServiceCodeGenerator -Version 1.2.6
<PackageReference Include="Mud.ServiceCodeGenerator" Version="1.2.6" />
<PackageVersion Include="Mud.ServiceCodeGenerator" Version="1.2.6" />
<PackageReference Include="Mud.ServiceCodeGenerator" />
paket add Mud.ServiceCodeGenerator --version 1.2.6
#r "nuget: Mud.ServiceCodeGenerator, 1.2.6"
#:package Mud.ServiceCodeGenerator@1.2.6
#addin nuget:?package=Mud.ServiceCodeGenerator&version=1.2.6
#tool nuget:?package=Mud.ServiceCodeGenerator&version=1.2.6
Mud 服务代码生成器
功能介绍
Mud 服务代码生成器是一个基于 Roslyn 的源代码生成器,用于自动生成服务层相关代码,提高开发效率。它包含以下主要功能:
- 服务类代码生成 - 根据实体类自动生成服务接口和服务实现类
- 依赖注入代码生成 - 自动为类生成构造函数注入代码,包括日志、缓存、用户管理等常用服务
- 服务注册代码生成 - 自动生成服务注册扩展方法,简化依赖注入配置
- HttpClient API 代码生成 - 自动为标记了 HTTP 方法特性的接口生成 HttpClient 实现类
- HttpClient API 包装代码生成 - 为 HttpClient API 接口生成包装接口和实现类,简化 Token 管理等复杂逻辑
项目参数配置
在使用 Mud 服务代码生成器时,可以通过在项目文件中配置以下参数自定义生成行为:
通用配置参数
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<DefaultCacheManagerType>ICacheManager</DefaultCacheManagerType>
<DefaultUserManagerType>IUserManager</DefaultUserManagerType>
<DefaultLoggerVariable>_logger</DefaultLoggerVariable>
<DefaultCacheManagerVariable>_cacheManager</DefaultCacheManagerVariable>
<DefaultUserManagerVariable>_userManager</DefaultUserManagerVariable>
<ServiceGenerator>true</ServiceGenerator>
<EntitySuffix>Entity</EntitySuffix>
<ImpAssembly>Mud.System</ImpAssembly>
<EntityAttachAttributes>SuppressSniffer</EntityAttachAttributes>
</PropertyGroup>
<ItemGroup>
<CompilerVisibleProperty Include="DefaultCacheManagerType" />
<CompilerVisibleProperty Include="DefaultUserManagerType" />
<CompilerVisibleProperty Include="DefaultLoggerVariable" />
<CompilerVisibleProperty Include="DefaultCacheManagerVariable" />
<CompilerVisibleProperty Include="DefaultUserManagerVariable" />
<CompilerVisibleProperty Include="ServiceGenerator" />
<CompilerVisibleProperty Include="EntitySuffix" />
<CompilerVisibleProperty Include="ImpAssembly" />
<CompilerVisibleProperty Include="EntityAttachAttributes" />
</ItemGroup>
依赖项配置
<ItemGroup>
<PackageReference Include="Mud.ServiceCodeGenerator" Version="1.2.3"/>
</ItemGroup>
依赖注入代码生成
使用各种注入特性为类自动生成构造函数注入代码:
[ConstructorInject] // 字段构造函数注入
[LoggerInject] // 日志注入
[CacheInject] // 缓存管理器注入
[UserInject] // 用户管理器注入
[CustomInject(VarType = "IRepository<SysUser>", VarName = "_userRepository")] // 自定义注入
public partial class SysUserService
{
// 生成的代码将包含以下内容:
// 1. 构造函数参数
// 2. 私有只读字段
// 3. 构造函数赋值语句
}
自动服务注册代码生成
使用 [AutoRegister] 和 [AutoRegisterKeyed] 特性自动生成服务注册代码,简化依赖注入配置:
// 自动注册服务到DI容器
[AutoRegister]
[AutoRegister<ISysUserService>]
[AutoRegisterKeyed<ISysUserService>("user")]
public partial class SysUserService : ISysUserService
{
// 生成的代码将包含服务注册扩展方法
}
构造函数注入详解
ConstructorInjectAttribute 字段注入
使用 [ConstructorInject] 特性可以将类中已存在的字段通过构造函数注入初始化。该注入方式会扫描类中的所有私有只读字段,并为其生成相应的构造函数参数和赋值语句。
示例:
[ConstructorInject]
public partial class UserService
{
private readonly IUserRepository _userRepository;
private readonly IRoleRepository _roleRepository;
// 生成的代码将包含:
public UserService(IUserRepository userRepository, IRoleRepository roleRepository)
{
_userRepository = userRepository;
_roleRepository = roleRepository;
}
}
LoggerInjectAttribute 日志注入
使用 [LoggerInject] 特性可以为类注入 ILogger<> 类型的日志记录器。该注入会自动生成 ILoggerFactory 参数,并在构造函数中创建对应类的 Logger 实例。
示例:
[LoggerInject]
public partial class UserService
{
// 生成的代码将包含:
private readonly ILogger<UserService> _logger;
public UserService(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<UserService>();
}
}
CacheInjectAttribute 缓存管理器注入
使用 [CacheInject] 特性可以注入缓存管理器实例。默认类型为 ICacheManager,默认字段名为 _cacheManager,可通过项目配置修改。
示例:
[CacheInject]
public partial class UserService
{
// 生成的代码将包含:
private readonly ICacheManager _cacheManager;
public UserService(ICacheManager cacheManager)
{
_cacheManager = cacheManager;
}
}
项目配置示例:
<PropertyGroup>
<DefaultCacheManagerType>MyCustomCacheManager</DefaultCacheManagerType>
<DefaultCacheManagerVariable>_myCacheManager</DefaultCacheManagerVariable>
</PropertyGroup>
UserInjectAttribute 用户管理器注入
使用 [UserInject] 特性可以注入用户管理器实例。默认类型为 IUserManager,默认字段名为 _userManager,可通过项目配置修改。
示例:
[UserInject]
public partial class UserService
{
// 生成的代码将包含:
private readonly IUserManager _userManager;
public UserService(IUserManager userManager)
{
_userManager = userManager;
}
}
项目配置示例:
<PropertyGroup>
<DefaultUserManagerType>MyCustomUserManager</DefaultUserManagerType>
<DefaultUserManagerVariable>_myUserManager</DefaultUserManagerVariable>
</PropertyGroup>
OptionsInjectAttribute 配置项注入
使用 [OptionsInject] 特性可以根据指定的配置项类型注入配置实例。支持泛型语法,提供更简洁的配置方式。
示例:
// 传统方式
[OptionsInject(OptionType = "TenantOptions")]
// 泛型方式(推荐)
[OptionsInject<TenantOptions>]
public partial class UserService
{
// 生成的代码将包含:
private readonly TenantOptions _tenantOptions;
public UserService(IOptions<TenantOptions> tenantOptions)
{
_tenantOptions = tenantOptions.Value;
}
}
CustomInjectAttribute 自定义注入
使用 [CustomInject] 特性可以注入任意类型的依赖项。支持泛型语法,提供更简洁的类型安全配置方式。
示例:
// 传统方式
[CustomInject(VarType = "IRepository<SysUser>", VarName = "_userRepository")]
[CustomInject(VarType = "INotificationService", VarName = "_notificationService")]
// 泛型方式(推荐)
[CustomInject<IRepository<SysUser>>(VarName = "_userRepository")]
[CustomInject<INotificationService>(VarName = "_notificationService")]
public partial class UserService
{
// 生成的代码将包含:
private readonly IRepository<SysUser> _userRepository;
private readonly INotificationService _notificationService;
public UserService(IRepository<SysUser> userRepository, INotificationService notificationService)
{
_userRepository = userRepository;
_notificationService = notificationService;
}
}
组合注入示例
多种注入特性可以组合使用,生成器会自动合并所有注入需求。推荐使用泛型语法以获得更好的类型安全性:
[ConstructorInject]
[LoggerInject]
[CacheInject]
[UserInject]
[OptionsInject<TenantOptions>]
[CustomInject<IRepository<SysUser>>(VarName = "_userRepository")]
public partial class UserService
{
private readonly IRoleRepository _roleRepository;
private readonly IPermissionRepository _permissionRepository;
// 生成的代码将包含所有注入项:
private readonly ILogger<UserService> _logger;
private readonly ICacheManager _cacheManager;
private readonly IUserManager _userManager;
private readonly TenantOptions _tenantOptions;
private readonly IRepository<SysUser> _userRepository;
private readonly IRoleRepository _roleRepository;
private readonly IPermissionRepository _permissionRepository;
public UserService(
ILoggerFactory loggerFactory,
ICacheManager cacheManager,
IUserManager userManager,
IOptions<TenantOptions> tenantOptions,
IRepository<SysUser> userRepository,
IRoleRepository roleRepository,
IPermissionRepository permissionRepository)
{
_logger = loggerFactory.CreateLogger<UserService>();
_cacheManager = cacheManager;
_userManager = userManager;
_tenantOptions = tenantOptions.Value;
_userRepository = userRepository;
_roleRepository = roleRepository;
_permissionRepository = permissionRepository;
}
}
自动服务注册代码生成
AutoRegisterSourceGenerator 自动为标记了 [AutoRegister] 和 [AutoRegisterKeyed] 特性的类生成服务注册代码,简化依赖注入配置。
AutoRegisterAttribute 自动注册
使用 [AutoRegister] 特性自动将服务注册到DI容器中:
// 基本用法:注册实现类本身
[AutoRegister]
public class UserService
{
// 生成的注册代码:services.AddScoped<UserService>();
}
// 注册为接口实现
[AutoRegister<IUserService>]
public class UserService : IUserService
{
// 生成的注册代码:services.AddScoped<IUserService, UserService>();
}
// 指定生命周期
[AutoRegister(ServiceLifetime.Singleton)]
[AutoRegister<IUserService>(ServiceLifetime.Transient)]
public class UserService : IUserService
{
// 生成的注册代码:
// services.AddSingleton<UserService>();
// services.AddTransient<IUserService, UserService>();
}
AutoRegisterKeyedAttribute 键控服务注册
使用 [AutoRegisterKeyed] 特性注册键控服务(Microsoft.Extensions.DependencyInjection 8.0+):
// 键控服务注册
[AutoRegisterKeyed("user")]
[AutoRegisterKeyed<IUserService>("user")]
public class UserService : IUserService
{
// 生成的注册代码:
// services.AddKeyedScoped<UserService>("user");
// services.AddKeyedScoped<IUserService, UserService>("user");
}
// 键控服务指定生命周期
[AutoRegisterKeyed<IUserService>("user", ServiceLifetime.Singleton)]
public class UserService : IUserService
{
// 生成的注册代码:services.AddKeyedSingleton<IUserService, UserService>("user");
}
生成的注册代码
自动生成的注册扩展方法位于 AutoRegisterExtension 类中:
// 自动生成的代码
public static partial class AutoRegisterExtension
{
/// <summary>
/// 自动注册标注的服务
/// </summary>
public static IServiceCollection AddAutoRegister(this IServiceCollection services)
{
services.AddScoped<UserService>();
services.AddScoped<IUserService, UserService>();
services.AddKeyedScoped<UserService>("user");
services.AddKeyedScoped<IUserService, UserService>("user");
return services;
}
}
使用方式
在应用程序启动时调用生成的扩展方法:
var builder = WebApplication.CreateBuilder(args);
// 自动注册所有标记的服务
builder.Services.AddAutoRegister();
// 或者与其他注册一起使用
builder.Services
.AddControllers()
.AddAutoRegister();
特性组合使用
自动注册特性可以与其他注入特性组合使用:
[AutoRegister<IUserService>]
[ConstructorInject]
[LoggerInject]
[CacheInject]
public class UserService : IUserService
{
private readonly IUserRepository _userRepository;
// 同时生成构造函数注入和服务注册代码
}
忽略字段注入
对于某些不需要通过构造函数注入的字段,可以使用 [IgnoreGenerator] 特性标记:
[ConstructorInject]
public partial class UserService
{
private readonly IUserRepository _userRepository;
[IgnoreGenerator]
private readonly string _connectionString = "default_connection_string"; // 不会被注入
// 只有_userRepository会被构造函数注入
}
HttpClient API 代码生成
HttpClientApiSourceGenerator 自动为标记了 [HttpClientApi] 特性的接口生成 HttpClient 实现类,支持 RESTful API 调用。该生成器提供了完整的 HTTP 请求生命周期管理,包括部分方法事件钩子、参数自动处理、错误处理和日志记录。
新功能亮点
1. 部分方法事件钩子
为每个 HTTP 方法自动生成4个事件钩子方法,支持完整的请求生命周期监控:
[HttpClientApi]
public interface IExampleApi
{
[Get("/api/users/{id}")]
Task<UserDto> GetUserAsync([Path] string id);
}
// 生成的实现类包含以下部分方法:
public partial class ExampleApi : IExampleApi
{
// 方法级别事件钩子
partial void OnGetUserBefore(HttpRequestMessage request, string url);
partial void OnGetUserAfter(HttpResponseMessage response, string url);
partial void OnGetUserFail(HttpResponseMessage response, string url);
partial void OnGetUserError(Exception error, string url);
// 接口级别事件钩子
partial void OnExampleApiRequestBefore(HttpRequestMessage request, string url);
partial void OnExampleApiRequestAfter(HttpResponseMessage response, string url);
partial void OnExampleApiRequestFail(HttpResponseMessage response, string url);
partial void OnExampleApiRequestError(Exception error, string url);
}
2. 改进的参数特性支持
支持多种参数特性,自动处理不同类型的参数:
[HttpClientApi]
public interface IAdvancedApi
{
// 路径参数 - 自动替换 URL 模板中的占位符
[Get("/api/users/{userId}/orders/{orderId}")]
Task<OrderDto> GetOrderAsync([Path] string userId, [Path] string orderId);
// 查询参数 - 自动生成查询字符串
[Get("/api/search")]
Task<List<UserDto>> SearchUsersAsync([Query] string name, [Query] int? page);
// 请求头参数 - 自动设置请求头
[Get("/api/protected")]
Task<ProtectedData> GetProtectedDataAsync([Header("Authorization")] string token);
// 请求体参数 - 自动序列化为 JSON
[Post("/api/users")]
Task<UserDto> CreateUserAsync([Body] UserDto user);
// 复杂查询参数对象
[Get("/api/search")]
Task<List<UserDto>> AdvancedSearchAsync([Query] UserSearchCriteria criteria);
}
public class UserSearchCriteria
{
public string Name { get; set; }
public int? Age { get; set; }
public string Department { get; set; }
}
3. 高级参数特性配置
支持参数特性的高级配置选项:
[HttpClientApi]
public interface IConfigurableApi
{
// 自定义查询参数名称和格式
[Get("/api/users")]
Task<List<UserDto>> GetUsersAsync(
[Query(Name = "user_name", FormatString = "UPPER")] string name,
[Query(FormatString = "D2")] int? page);
// 自定义请求内容类型
[Post("/api/data")]
Task<ResponseDto> SendDataAsync(
[Body(ContentType = "application/xml", UseStringContent = true)] string xmlData);
// 自定义路径参数格式
[Get("/api/orders/{orderId:guid}")]
Task<OrderDto> GetOrderAsync([Path(FormatString = "N")] Guid orderId);
}
基本用法
1. 定义 HTTP API 接口
[HttpClientApi]
public interface IDingTalkApi
{
[Get("/api/v1/user/{id}")]
Task<UserDto> GetUserAsync([Query] string id);
[Post("/api/v1/user")]
Task<UserDto> CreateUserAsync([Body] UserDto user);
[Put("/api/v1/user/{id}")]
Task<UserDto> UpdateUserAsync([Path] string id, [Body] UserDto user);
[Delete("/api/v1/user/{id}")]
Task<bool> DeleteUserAsync([Path] string id);
}
2. 生成的 HttpClient 实现类
自动生成的实现类包含完整的 HTTP 请求处理逻辑:
// 自动生成的代码
public partial class DingTalkApi : IDingTalkApi
{
private readonly HttpClient _httpClient;
private readonly ILogger<DingTalkApi> _logger;
private readonly JsonSerializerOptions _jsonSerializerOptions;
public DingTalkApi(HttpClient httpClient, ILogger<DingTalkApi> logger)
{
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_jsonSerializerOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false,
PropertyNameCaseInsensitive = true
};
}
public async Task<UserDto> GetUserAsync(string id)
{
// 自动生成的 HTTP GET 请求逻辑
_logger.LogDebug("开始HTTP GET请求: {Url}", "/api/v1/user/{id}");
var url = $"/api/v1/user/{id}";
using var request = new HttpRequestMessage(HttpMethod.Get, url);
// 处理查询参数
var queryParams = new List<string>();
if (id != null)
queryParams.Add($"id={id}");
if (queryParams.Any())
url += "?" + string.Join("&", queryParams);
// 发送请求并处理响应
// ... 完整的请求处理逻辑
}
}
支持的 HTTP 方法特性
支持所有标准的 HTTP 方法:
[HttpClientApi]
public interface IExampleApi
{
[Get("/api/resource/{id}")]
Task<ResourceDto> GetResourceAsync([Path] string id);
[Post("/api/resource")]
Task<ResourceDto> CreateResourceAsync([Body] ResourceDto resource);
[Put("/api/resource/{id}")]
Task<ResourceDto> UpdateResourceAsync([Path] string id, [Body] ResourceDto resource);
[Delete("/api/resource/{id}")]
Task<bool> DeleteResourceAsync([Path] string id);
[Patch("/api/resource/{id}")]
Task<ResourceDto> PatchResourceAsync([Path] string id, [Body] object patchData);
[Head("/api/resource/{id}")]
Task<bool> CheckResourceExistsAsync([Path] string id);
[Options("/api/resource")]
Task<HttpResponseMessage> GetResourceOptionsAsync();
}
参数特性详解
1. Path 参数特性
用于替换 URL 模板中的路径参数:
[Get("/api/users/{userId}/orders/{orderId}")]
Task<OrderDto> GetOrderAsync([Path] string userId, [Path] string orderId);
2. Query 参数特性
用于生成查询字符串参数:
[Get("/api/users")]
Task<List<UserDto>> GetUsersAsync(
[Query] string name,
[Query] int? page,
[Query] int? pageSize);
3. Body 参数特性
用于设置请求体内容:
[Post("/api/users")]
Task<UserDto> CreateUserAsync([Body] UserDto user);
// 支持自定义内容类型
[Post("/api/users")]
Task<UserDto> CreateUserAsync([Body(ContentType = "application/xml")] UserDto user);
// 支持字符串内容
[Post("/api/logs")]
Task LogMessageAsync([Body(UseStringContent = true)] string message);
4. Header 参数特性
用于设置请求头:
[Get("/api/protected")]
Task<ProtectedData> GetProtectedDataAsync([Header] string authorization);
// 自定义头名称
[Get("/api/protected")]
Task<ProtectedData> GetProtectedDataAsync([Header("X-API-Key")] string apiKey);
复杂参数处理
1. 复杂查询参数
支持复杂对象作为查询参数,自动展开为键值对:
[Get("/api/search")]
Task<List<UserDto>> SearchUsersAsync([Query] UserSearchCriteria criteria);
public class UserSearchCriteria
{
public string Name { get; set; }
public int? Age { get; set; }
public string Department { get; set; }
}
// 生成的查询字符串:?Name=John&Age=30&Department=IT
2. 路径参数自动替换
自动处理 URL 模板中的路径参数:
[Get("/api/users/{userId}/orders/{orderId}/items/{itemId}")]
Task<OrderItemDto> GetOrderItemAsync(
[Path] string userId,
[Path] string orderId,
[Path] string itemId);
// 自动替换:/api/users/123/orders/456/items/789
错误处理与日志记录
生成的代码包含完整的错误处理和日志记录:
public async Task<UserDto> GetUserAsync(string id)
{
try
{
_logger.LogDebug("开始HTTP GET请求: {Url}", "/api/v1/user/{id}");
// 请求处理逻辑
using var response = await _httpClient.SendAsync(request);
var responseContent = await response.Content.ReadAsStringAsync();
_logger.LogDebug("HTTP请求完成: {StatusCode}, 响应长度: {ContentLength}",
(int)response.StatusCode, responseContent?.Length ?? 0);
if (!response.IsSuccessStatusCode)
{
_logger.LogError("HTTP请求失败: {StatusCode}, 响应: {Response}",
(int)response.StatusCode, responseContent);
throw new HttpRequestException($"HTTP请求失败: {(int)response.StatusCode} - {response.ReasonPhrase}");
}
// 响应处理逻辑
}
catch (Exception ex)
{
_logger.LogError(ex, "HTTP请求异常: {Url}", url);
throw;
}
}
配置选项
1. 自定义 JsonSerializerOptions
生成的构造函数包含默认的 JsonSerializerOptions 配置:
_jsonSerializerOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = false,
PropertyNameCaseInsensitive = true
};
2. 支持可空返回值
自动处理可空返回值类型:
[Get("/api/users/{id}")]
Task<UserDto?> GetUserOrNullAsync([Path] string id);
使用示例
1. 在依赖注入中注册
// 在 Startup.cs 或 Program.cs 中
services.AddHttpClient<IDingTalkApi, DingTalkApi>(client =>
{
client.BaseAddress = new Uri("https://api.dingtalk.com");
client.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0");
});
2. 在服务中使用
public class UserService
{
private readonly IDingTalkApi _dingTalkApi;
public UserService(IDingTalkApi dingTalkApi)
{
_dingTalkApi = dingTalkApi;
}
public async Task<UserDto> GetUserAsync(string userId)
{
return await _dingTalkApi.GetUserAsync(userId);
}
}
部分方法事件钩子使用指南
1. 方法级别事件钩子
为每个 HTTP 方法自动生成4个事件钩子,可在自定义实现中重写:
public partial class ExampleApi
{
// 请求执行前调用 - 可用于修改请求
partial void OnGetUserBefore(HttpRequestMessage request, string url)
{
// 添加自定义请求头
request.Headers.Add("X-Custom-Header", "custom-value");
// 记录请求日志
_logger.LogInformation("开始调用 GetUser API: {Url}", url);
}
// 请求成功后调用 - 可用于处理响应
partial void OnGetUserAfter(HttpResponseMessage response, string url)
{
// 记录成功响应
_logger.LogInformation("GetUser API 调用成功: {StatusCode}", (int)response.StatusCode);
// 验证响应内容
if (!response.Headers.Contains("X-RateLimit-Remaining"))
{
_logger.LogWarning("API 响应缺少速率限制信息");
}
}
// 请求失败时调用 (HTTP 状态码非 2xx) - 可用于错误处理
partial void OnGetUserFail(HttpResponseMessage response, string url)
{
// 处理特定错误状态码
if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
{
_logger.LogWarning("用户不存在: {Url}", url);
}
else if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
// 触发重新认证流程
_authService.RefreshToken();
}
}
// 请求发生异常时调用 - 可用于异常处理
partial void OnGetUserError(Exception error, string url)
{
// 记录异常详细信息
_logger.LogError(error, "GetUser API 调用异常: {Url}", url);
// 发送异常通知
_notificationService.SendErrorNotification(error, url);
}
}
2. 接口级别事件钩子
为整个接口类生成4个全局事件钩子,适用于所有方法:
public partial class ExampleApi
{
// 所有方法请求前调用
partial void OnExampleApiRequestBefore(HttpRequestMessage request, string url)
{
// 添加全局请求头
request.Headers.Add("X-Request-ID", Guid.NewGuid().ToString());
request.Headers.Add("X-Timestamp", DateTime.UtcNow.ToString("O"));
// 全局请求验证
if (string.IsNullOrEmpty(url))
{
throw new ArgumentException("URL 不能为空", nameof(url));
}
}
// 所有方法请求成功后调用
partial void OnExampleApiRequestAfter(HttpResponseMessage response, string url)
{
// 全局响应处理
var rateLimit = response.Headers.GetValues("X-RateLimit-Remaining").FirstOrDefault();
if (!string.IsNullOrEmpty(rateLimit) && int.Parse(rateLimit) < 10)
{
_logger.LogWarning("API 速率限制即将达到: {Remaining}", rateLimit);
}
}
// 所有方法请求失败时调用
partial void OnExampleApiRequestFail(HttpResponseMessage response, string url)
{
// 全局错误处理
if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
{
var retryAfter = response.Headers.RetryAfter?.Delta;
if (retryAfter.HasValue)
{
_logger.LogWarning("达到速率限制,将在 {RetryAfter} 后重试", retryAfter.Value);
}
}
}
// 所有方法请求异常时调用
partial void OnExampleApiRequestError(Exception error, string url)
{
// 全局异常处理
if (error is System.Net.Http.HttpRequestException httpError)
{
_logger.LogError(httpError, "网络请求异常: {Url}", url);
}
else if (error is System.Text.Json.JsonException jsonError)
{
_logger.LogError(jsonError, "JSON 序列化异常: {Url}", url);
}
}
}
3. 事件钩子的执行顺序
请求生命周期中事件钩子的执行顺序如下:
1. On{InterfaceName}ApiRequestBefore (接口级别)
2. On{MethodName}Before (方法级别)
3. 执行 HTTP 请求
4. On{MethodName}After (方法级别) - 如果请求成功
On{MethodName}Fail (方法级别) - 如果请求失败 (HTTP 状态码非 2xx)
5. On{InterfaceName}ApiRequestAfter (接口级别) - 如果请求成功
On{InterfaceName}ApiRequestFail (接口级别) - 如果请求失败
6. On{MethodName}Error (方法级别) - 如果发生异常
On{InterfaceName}ApiRequestError (接口级别) - 如果发生异常
4. 高级事件钩子应用场景
4.1 请求重试机制
public partial class ResilientApi
{
private int _retryCount = 0;
partial void OnGetDataBefore(HttpRequestMessage request, string url)
{
// 在重试时添加延迟
if (_retryCount > 0)
{
Thread.Sleep(TimeSpan.FromSeconds(_retryCount * 2));
}
}
partial void OnGetDataFail(HttpResponseMessage response, string url)
{
// 如果是暂时性错误,触发重试
if (IsTransientError(response.StatusCode) && _retryCount < 3)
{
_retryCount++;
_logger.LogWarning("第 {RetryCount} 次重试: {Url}", _retryCount, url);
// 重新调用方法 (需要实现重试逻辑)
// 注意:实际实现中需要在方法外部处理重试
}
}
partial void OnGetDataAfter(HttpResponseMessage response, string url)
{
// 重置重试计数器
_retryCount = 0;
}
private bool IsTransientError(System.Net.HttpStatusCode statusCode)
{
return statusCode == System.Net.HttpStatusCode.RequestTimeout ||
statusCode == System.Net.HttpStatusCode.TooManyRequests ||
statusCode == System.Net.HttpStatusCode.InternalServerError ||
statusCode == System.Net.HttpStatusCode.ServiceUnavailable;
}
}
4.2 请求监控和指标收集
public partial class MonitoredApi
{
private readonly IMetricsCollector _metrics;
partial void OnGetUserBefore(HttpRequestMessage request, string url)
{
// 开始计时
request.Properties["StartTime"] = DateTime.UtcNow;
}
partial void OnGetUserAfter(HttpResponseMessage response, string url)
{
// 计算请求耗时
if (response.RequestMessage?.Properties.TryGetValue("StartTime", out var startTimeObj) == true)
{
var startTime = (DateTime)startTimeObj;
var duration = DateTime.UtcNow - startTime;
// 收集指标
_metrics.RecordApiCall("GetUser", duration, true);
_logger.LogInformation("GetUser API 调用耗时: {Duration}ms", duration.TotalMilliseconds);
}
}
partial void OnGetUserFail(HttpResponseMessage response, string url)
{
// 记录失败指标
_metrics.RecordApiCall("GetUser", TimeSpan.Zero, false);
}
}
4.3 安全审计
public partial class AuditedApi
{
private readonly IAuditLogger _auditLogger;
partial void OnUpdateUserBefore(HttpRequestMessage request, string url)
{
// 记录操作审计
var user = _userContext.CurrentUser;
_auditLogger.LogOperation(user?.Id, "UpdateUser",
$"开始更新用户数据: {url}", AuditLevel.Info);
}
partial void OnUpdateUserAfter(HttpResponseMessage response, string url)
{
// 记录成功审计
var user = _userContext.CurrentUser;
_auditLogger.LogOperation(user?.Id, "UpdateUser",
$"用户数据更新成功: {(int)response.StatusCode}", AuditLevel.Info);
}
partial void OnUpdateUserFail(HttpResponseMessage response, string url)
{
// 记录失败审计
var user = _userContext.CurrentUser;
_auditLogger.LogOperation(user?.Id, "UpdateUser",
$"用户数据更新失败: {(int)response.StatusCode}", AuditLevel.Warning);
}
}
高级功能
1. 组合使用多个参数特性
[Post("/api/users/{userId}/permissions")]
Task<bool> AssignPermissionsAsync(
[Path] string userId,
[Body] List<string> permissions,
[Header("X-Request-ID")] string requestId,
[Query] bool? overwrite);
2. 自定义内容序列化
[Post("/api/data")]
Task<ResponseDto> SendDataAsync([Body(ContentType = "application/xml", UseStringContent = true)] string xmlData);
HttpClient API 注册代码生成
HttpClientApiRegisterSourceGenerator 自动为标记了 [HttpClientApi] 特性的接口生成依赖注入注册代码,简化 HttpClient 服务的配置。
基本用法
1. 定义 HTTP API 接口
[HttpClientApi("https://api.dingtalk.com", Timeout = 30)]
public interface IDingTalkApi
{
[Get("/api/v1/user/{id}")]
Task<UserDto> GetUserAsync([Query] string id);
[Post("/api/v1/user")]
Task<UserDto> CreateUserAsync([Body] UserDto user);
}
[HttpClientApi("https://api.wechat.com", Timeout = 60)]
public interface IWeChatApi
{
[Get("/api/v1/user/{id}")]
Task<UserDto> GetUserAsync([Query] string id);
}
2. 生成的注册代码
自动生成的依赖注入注册扩展方法:
// 自动生成的代码 - HttpClientApiExtensions.g.cs
using System;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.Extensions.DependencyInjection
{
public static class HttpClientApiExtensions
{
public static IServiceCollection AddWebApiHttpClient(this IServiceCollection services)
{
services.AddHttpClient<global::YourNamespace.IDingTalkApi, global::YourNamespace.DingTalkApi>(client =>
{
client.BaseAddress = new Uri("https://api.dingtalk.com");
client.Timeout = TimeSpan.FromSeconds(30);
});
services.AddHttpClient<global::YourNamespace.IWeChatApi, global::YourNamespace.WeChatApi>(client =>
{
client.BaseAddress = new Uri("https://api.wechat.com");
client.Timeout = TimeSpan.FromSeconds(60);
});
return services;
}
}
}
配置选项
1. HttpClientApi 特性参数
// 基本配置
[HttpClientApi("https://api.example.com")]
public interface IExampleApi { }
// 配置超时时间
[HttpClientApi("https://api.example.com", Timeout = 120)]
public interface IExampleApi { }
// 使用命名参数
[HttpClientApi(BaseUrl = "https://api.example.com", Timeout = 60)]
public interface IExampleApi { }
2. 生成的 HttpClient 配置
生成的注册代码包含以下配置:
- BaseAddress: 从 [HttpClientApi] 特性的第一个参数获取
- Timeout: 从 Timeout 命名参数获取,默认 100 秒
- 服务注册: 使用 AddHttpClient 方法注册接口和实现类
使用方式
1. 在应用程序启动时调用
// 在 Program.cs 或 Startup.cs 中
var builder = WebApplication.CreateBuilder(args);
// 自动注册所有 HttpClient API 服务
builder.Services.AddWebApiHttpClient();
// 或者与其他服务注册一起使用
builder.Services
.AddControllers()
.AddWebApiHttpClient();
2. 在控制台应用程序中使用
// 在控制台应用程序中
var services = new ServiceCollection();
// 注册 HttpClient API 服务
services.AddWebApiHttpClient();
var serviceProvider = services.BuildServiceProvider();
var dingTalkApi = serviceProvider.GetRequiredService<IDingTalkApi>();
与 HttpClientApiSourceGenerator 配合使用
HttpClientApiRegisterSourceGenerator 与 HttpClientApiSourceGenerator 完美配合:
- HttpClientApiSourceGenerator 生成接口的实现类
- HttpClientApiRegisterSourceGenerator 生成依赖注入注册代码
- 完整的开发体验:定义接口 → 自动生成实现 → 自动注册服务
完整示例
// 1. 定义接口
[HttpClientApi("https://api.dingtalk.com", Timeout = 30)]
public interface IDingTalkApi
{
[Get("/api/v1/user/{id}")]
Task<UserDto> GetUserAsync([Query] string id);
}
// 2. 自动生成实现类 (由 HttpClientApiSourceGenerator 生成)
// public partial class DingTalkApi : IDingTalkApi { ... }
// 3. 自动生成注册代码 (由 HttpClientApiRegisterSourceGenerator 生成)
// public static class HttpClientApiExtensions { ... }
// 4. 在应用程序中使用
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddWebApiHttpClient(); // 自动注册
var app = builder.Build();
// 5. 在服务中注入使用
public class UserService
{
private readonly IDingTalkApi _dingTalkApi;
public UserService(IDingTalkApi dingTalkApi)
{
_dingTalkApi = dingTalkApi;
}
public async Task<UserDto> GetUserAsync(string userId)
{
return await _dingTalkApi.GetUserAsync(userId);
}
}
高级配置
1. 自定义 HttpClient 配置
如果需要更复杂的 HttpClient 配置,可以在注册后继续配置:
builder.Services.AddWebApiHttpClient()
.ConfigureHttpClientDefaults(httpClient =>
{
httpClient.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
UseProxy = false,
AllowAutoRedirect = false
});
});
2. 添加自定义请求头
builder.Services.AddHttpClient<IDingTalkApi, DingTalkApi>(client =>
{
client.BaseAddress = new Uri("https://api.dingtalk.com");
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0");
client.DefaultRequestHeaders.Add("X-API-Key", "your-api-key");
});
错误处理
注册生成器会自动处理以下错误情况:
- 无效的 [HttpClientApi] 特性:忽略没有有效特性的接口
- 特性参数验证:确保 BaseUrl 和 Timeout 参数的有效性
- 命名空间处理:正确处理全局命名空间引用
生成的代码结构
obj/Debug/net8.0/generated/
├── Mud.ServiceCodeGenerator/
├── HttpClientApiSourceGenerator/
│ └── YourNamespace.DingTalkApi.g.cs
└── HttpClientApiRegisterSourceGenerator/
└── HttpClientApiExtensions.g.cs
最佳实践
- 统一配置:在 [HttpClientApi] 特性中统一配置所有 API 的基础设置
- 合理超时:根据 API 的响应时间设置合理的超时时间
- 命名规范:遵循接口命名规范(I{ServiceName}Api)
- 错误处理:在服务层处理 API 调用异常
- 日志记录:利用生成的日志记录功能监控 API 调用
HttpClient API 注册代码生成
HttpClientApiRegisterSourceGenerator 自动为标记了 [HttpClientApi] 特性的接口生成依赖注入注册代码,简化 HttpClient 服务的配置。
基本用法
1. 定义 HTTP API 接口
[HttpClientApi("https://api.dingtalk.com", Timeout = 30)]
public interface IDingTalkApi
{
[Get("/api/v1/user/{id}")]
Task<UserDto> GetUserAsync([Query] string id);
[Post("/api/v1/user")]
Task<UserDto> CreateUserAsync([Body] UserDto user);
}
[HttpClientApi("https://api.wechat.com", Timeout = 60)]
public interface IWeChatApi
{
[Get("/api/v1/user/{id}")]
Task<UserDto> GetUserAsync([Query] string id);
}
2. 生成的注册代码
自动生成的依赖注入注册扩展方法:
// 自动生成的代码 - HttpClientApiExtensions.g.cs
using System;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.Extensions.DependencyInjection
{
public static class HttpClientApiExtensions
{
public static IServiceCollection AddWebApiHttpClient(this IServiceCollection services)
{
services.AddHttpClient<global::YourNamespace.IDingTalkApi, global::YourNamespace.DingTalkApi>(client =>
{
client.BaseAddress = new Uri("https://api.dingtalk.com");
client.Timeout = TimeSpan.FromSeconds(30);
});
services.AddHttpClient<global::YourNamespace.IWeChatApi, global::YourNamespace.WeChatApi>(client =>
{
client.BaseAddress = new Uri("https://api.wechat.com");
client.Timeout = TimeSpan.FromSeconds(60);
});
return services;
}
}
}
配置选项
1. HttpClientApi 特性参数
// 基本配置
[HttpClientApi("https://api.example.com")]
public interface IExampleApi { }
// 配置超时时间
[HttpClientApi("https://api.example.com", Timeout = 120)]
public interface IExampleApi { }
// 使用命名参数
[HttpClientApi(BaseUrl = "https://api.example.com", Timeout = 60)]
public interface IExampleApi { }
2. 生成的 HttpClient 配置
生成的注册代码包含以下配置:
- BaseAddress: 从 [HttpClientApi] 特性的第一个参数获取
- Timeout: 从 Timeout 命名参数获取,默认 100 秒
- 服务注册: 使用 AddHttpClient 方法注册接口和实现类
使用方式
1. 在应用程序启动时调用
// 在 Program.cs 或 Startup.cs 中
var builder = WebApplication.CreateBuilder(args);
// 自动注册所有 HttpClient API 服务
builder.Services.AddWebApiHttpClient();
// 或者与其他服务注册一起使用
builder.Services
.AddControllers()
.AddWebApiHttpClient();
2. 在控制台应用程序中使用
// 在控制台应用程序中
var services = new ServiceCollection();
// 注册 HttpClient API 服务
services.AddWebApiHttpClient();
var serviceProvider = services.BuildServiceProvider();
var dingTalkApi = serviceProvider.GetRequiredService<IDingTalkApi>();
与 HttpClientApiSourceGenerator 配合使用
HttpClientApiRegisterSourceGenerator 与 HttpClientApiSourceGenerator 完美配合:
- HttpClientApiSourceGenerator 生成接口的实现类
- HttpClientApiRegisterSourceGenerator 生成依赖注入注册代码
- 完整的开发体验:定义接口 → 自动生成实现 → 自动注册服务
完整示例
// 1. 定义接口
[HttpClientApi("https://api.dingtalk.com", Timeout = 30)]
public interface IDingTalkApi
{
[Get("/api/v1/user/{id}")]
Task<UserDto> GetUserAsync([Query] string id);
}
// 2. 自动生成实现类 (由 HttpClientApiSourceGenerator 生成)
// public partial class DingTalkApi : IDingTalkApi { ... }
// 3. 自动生成注册代码 (由 HttpClientApiRegisterSourceGenerator 生成)
// public static class HttpClientApiExtensions { ... }
// 4. 在应用程序中使用
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddWebApiHttpClient(); // 自动注册
var app = builder.Build();
// 5. 在服务中注入使用
public class UserService
{
private readonly IDingTalkApi _dingTalkApi;
public UserService(IDingTalkApi dingTalkApi)
{
_dingTalkApi = dingTalkApi;
}
public async Task<UserDto> GetUserAsync(string userId)
{
return await _dingTalkApi.GetUserAsync(userId);
}
}
高级配置
1. 自定义 HttpClient 配置
如果需要更复杂的 HttpClient 配置,可以在注册后继续配置:
builder.Services.AddWebApiHttpClient()
.ConfigureHttpClientDefaults(httpClient =>
{
httpClient.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
UseProxy = false,
AllowAutoRedirect = false
});
});
2. 添加自定义请求头
builder.Services.AddHttpClient<IDingTalkApi, DingTalkApi>(client =>
{
client.BaseAddress = new Uri("https://api.dingtalk.com");
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestHeaders.Add("User-Agent", "MyApp/1.0");
client.DefaultRequestHeaders.Add("X-API-Key", "your-api-key");
});
生成的代码结构
obj/Debug/net8.0/generated/
├── Mud.ServiceCodeGenerator/
├── HttpClientApiSourceGenerator/
│ └── YourNamespace.DingTalkApi.g.cs
└── HttpClientApiRegisterSourceGenerator/
└── HttpClientApiExtensions.g.cs
最佳实践
- 统一配置:在 [HttpClientApi] 特性中统一配置所有 API 的基础设置
- 合理超时:根据 API 的响应时间设置合理的超时时间
- 命名规范:遵循接口命名规范(I{ServiceName}Api)
- 错误处理:在服务层处理 API 调用异常
- 日志记录:利用生成的日志记录功能监控 API 调用
HttpClient API 包装代码生成
HttpClientApiWrapSourceGenerator 及其子类为标记了 [HttpClientApiWrap] 特性的接口生成包装接口和实现类,用于简化 Token 管理等复杂逻辑。该生成器包含两个主要组件:
- HttpClientApiInterfaceWrapSourceGenerator - 生成包装接口(.Wrap.g.cs)
- HttpClientApiWrapClassSourceGenerator - 生成包装实现类(.WrapImpl.g.cs)
功能特点
- 自动 Token 管理:自动处理标记了 [Token] 特性的参数
- 错误处理和日志记录:包含完整的异常处理和日志记录
- XML 注释保留:自动保留原始方法的 XML 文档注释
- 重载方法支持:正确处理重载方法的 XML 注释
- 灵活的配置:支持自定义 Token 管理接口和包装接口名称
基本用法
1. 定义 Token 管理接口
/// <summary>
/// Token管理接口
/// </summary>
public interface ITokenManage
{
Task<string> GetTokenAsync();
}
/// <summary>
/// 钉钉Token管理接口
/// </summary>
public interface IDingTokenManage
{
Task<string> GetTokenAsync();
}
2. 定义 HTTP API 接口并添加包装特性
/// <summary>
/// 测试场景1:使用默认Token管理接口(ITokenManage)
/// </summary>
[HttpClientApi("https://api.dingtalk.com", Timeout = 60)]
[HttpClientApiWrap("ITokenManage")]
public interface ISingleTestApi
{
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="token">访问令牌</param>
/// <param name="birthday">生日日期</param>
/// <returns>用户信息</returns>
[Get("/api/v1/user/{birthday}")]
Task<UserDto> GetUserAsync([Token][Header("x-token")] string token, [Path("yyyy-MM-dd")] DateTime birthday);
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="birthday">生日日期</param>
/// <param name="token">访问令牌</param>
/// <returns>用户信息</returns>
[Get("/api/v1/user/{birthday}")]
Task<UserDto> GetUserAsync([Path("yyyy-MM-dd")] DateTime birthday, [Token][Header("x-token")] string token);
/// <summary>
/// 搜索用户
/// </summary>
/// <param name="input">搜索条件</param>
/// <param name="age">年龄</param>
/// <param name="token">访问令牌</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>用户列表</returns>
[Get("/api/v1/user/{birthday}")]
Task<UserDto> GetUser1Async([Query] SysDeptQueryInput input, [Query] int? age, [Token][Header("x-token")] string token, CancellationToken cancellationToken = default);
}
3. 生成的包装接口代码
自动生成的包装接口:
// 自动生成的代码 - ISingleTestApi.Wrap.g.cs
using System;
using System.Threading.Tasks;
namespace YourNamespace
{
/// <summary>
/// ISingleTestApi的包装接口
/// </summary>
public partial interface ISingleTestApiWrap
{
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="birthday">生日日期</param>
/// <returns>用户信息</returns>
Task<UserDto> GetUserAsync(DateTime birthday);
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="birthday">生日日期</param>
/// <returns>用户信息</returns>
Task<UserDto> GetUserAsync(DateTime birthday);
/// <summary>
/// 搜索用户
/// </summary>
/// <param name="input">搜索条件</param>
/// <param name="age">年龄</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>用户列表</returns>
Task<UserDto> GetUser1Async(SysDeptQueryInput input, int? age, CancellationToken cancellationToken = default);
}
}
4. 生成的包装实现类代码
自动生成的包装实现类:
// 自动生成的代码 - ISingleTestApi.WrapImpl.g.cs
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace YourNamespace
{
/// <summary>
/// ISingleTestApi的包装实现类
/// </summary>
internal partial class SingleTestApiWrap : ISingleTestApiWrap
{
private readonly ISingleTestApi _singleTestApi;
private readonly ITokenManage _tokenManage;
private readonly ILogger<SingleTestApiWrap> _logger;
public SingleTestApiWrap(ISingleTestApi singleTestApi, ITokenManage tokenManage, ILogger<SingleTestApiWrap> logger)
{
_singleTestApi = singleTestApi;
_tokenManage = tokenManage;
_logger = logger;
}
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="birthday">生日日期</param>
/// <returns>用户信息</returns>
public async Task<UserDto> GetUserAsync(DateTime birthday)
{
try
{
var token = await _tokenManage.GetTokenAsync();
if (string.IsNullOrEmpty(token))
{
_logger.LogWarning("获取到的Token为空!");
}
return await _singleTestApi.GetUserAsync(token, birthday);
}
catch (Exception x)
{
_logger.LogError(x, "执行GetUserAsync操作失败:{message}", x.Message);
throw;
}
}
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="birthday">生日日期</param>
/// <returns>用户信息</returns>
public async Task<UserDto> GetUserAsync(DateTime birthday)
{
try
{
var token = await _tokenManage.GetTokenAsync();
if (string.IsNullOrEmpty(token))
{
_logger.LogWarning("获取到的Token为空!");
}
return await _singleTestApi.GetUserAsync(birthday, token);
}
catch (Exception x)
{
_logger.LogError(x, "执行GetUserAsync操作失败:{message}", x.Message);
throw;
}
}
/// <summary>
/// 搜索用户
/// </summary>
/// <param name="input">搜索条件</param>
/// <param name="age">年龄</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>用户列表</returns>
public async Task<UserDto> GetUser1Async(SysDeptQueryInput input, int? age, CancellationToken cancellationToken = default)
{
try
{
var token = await _tokenManage.GetTokenAsync();
if (string.IsNullOrEmpty(token))
{
_logger.LogWarning("获取到的Token为空!");
}
return await _singleTestApi.GetUser1Async(input, age, token, cancellationToken);
}
catch (Exception x)
{
_logger.LogError(x, "执行GetUser1Async操作失败:{message}", x.Message);
throw;
}
}
}
}
高级配置选项
1. 使用指定的 Token 管理接口
// 使用指定的Token管理接口
[HttpClientApi("https://api.dingtalk.com", Timeout = 60)]
[HttpClientApiWrap(TokenManage = "IDingTokenManage")]
public interface ISingleTestApi2
{
[Get("/api/v1/user/{birthday}")]
Task<UserDto> GetUserAsync([Token][Header("x-token")] string token, [Path("yyyy-MM-dd")] DateTime birthday);
}
2. 自定义包装接口名称
// 自定义包装接口名称
[HttpClientApi("https://api.dingtalk.com", Timeout = 60)]
[HttpClientApiWrap(TokenManage = "ITokenManage", WrapInterface = "IDingTalkUserWrap")]
public interface ISingleTestApi3
{
[Get("/api/v1/user/{birthday}")]
Task<UserDto> GetUserAsync([Token][Header("x-token")] string token, [Path("yyyy-MM-dd")] DateTime birthday);
}
依赖注入注册
使用 HttpClientRegistrationGenerator 自动生成依赖注入注册代码:
// 自动生成的注册代码 - HttpClientApiExtensions.g.cs
public static class HttpClientApiExtensions
{
/// <summary>
/// 注册所有标记了 [HttpClientApi] 特性的接口及其 HttpClient 实现
/// </summary>
public static IServiceCollection AddWebApiHttpClient(this IServiceCollection services)
{
// 注册基本HttpClient API
services.AddHttpClient<ISingleTestApi, SingleTestApi>(client =>
{
client.BaseAddress = new Uri("https://api.dingtalk.com");
client.Timeout = TimeSpan.FromSeconds(60);
});
// 注册包装API的瞬时服务
AddWebApiHttpClientWrap(services);
return services;
}
/// <summary>
/// 注册所有包装接口及其包装实现类的瞬时服务
/// </summary>
public static IServiceCollection AddWebApiHttpClientWrap(this IServiceCollection services)
{
// 注册包装接口和实现类
services.AddTransient<ISingleTestApiWrap, SingleTestApiWrap>();
return services;
}
}
使用方式
1. 在应用程序中注册服务
// 在 Program.cs 或 Startup.cs 中
var builder = WebApplication.CreateBuilder(args);
// 注册 Token 管理服务
builder.Services.AddScoped<ITokenManage, YourTokenManageImplementation>();
// 自动注册所有 HttpClient API 和包装服务
builder.Services.AddWebApiHttpClient();
2. 在服务中使用包装接口
public class UserService
{
private readonly ISingleTestApiWrap _singleTestApiWrap;
public UserService(ISingleTestApiWrap singleTestApiWrap)
{
_singleTestApiWrap = singleTestApiWrap;
}
public async Task<UserDto> GetUserAsync(DateTime birthday)
{
// 无需手动处理 Token,包装类会自动处理
return await _singleTestApiWrap.GetUserAsync(birthday);
}
}
功能优势
- 简化 Token 管理:自动处理 Token 获取和传递
- 统一错误处理:提供一致的异常处理和日志记录
- 代码复用:避免在每个 API 调用中重复 Token 处理逻辑
- 易于测试:可以轻松模拟 Token 管理接口进行单元测试
- 可扩展性:支持自定义 Token 管理策略
最佳实践
- 统一的 Token 管理:为不同类型的 API 使用不同的 Token 管理接口
- 合理的日志级别:根据业务需求设置适当的日志级别
- 异常处理策略:在包装类中实现合适的异常处理策略
- 性能考虑:考虑 Token 缓存机制以提高性能
- 安全性:确保 Token 的存储和传输安全
生成的代码结构
obj/Debug/net8.0/generated/
├── Mud.ServiceCodeGenerator/
├── HttpClientApiSourceGenerator/
│ └── YourNamespace.ISingleTestApi.g.cs
├── HttpClientApiInterfaceWrapSourceGenerator/
│ └── YourNamespace.ISingleTestApi.Wrap.g.cs
├── HttpClientApiWrapClassSourceGenerator/
│ └── YourNamespace.ISingleTestApi.WrapImpl.g.cs
└── HttpClientApiRegisterSourceGenerator/
└── HttpClientApiExtensions.g.cs
HttpClient API 包装代码生成
HttpClientApiWrapSourceGenerator 及其子类为标记了 [HttpClientApiWrap] 特性的接口生成包装接口和实现类,用于简化 Token 管理等复杂逻辑。该生成器包含两个主要组件:
- HttpClientApiInterfaceWrapSourceGenerator - 生成包装接口(.Wrap.g.cs)
- HttpClientApiWrapClassSourceGenerator - 生成包装实现类(.WrapImpl.g.cs)
功能特点
- 自动 Token 管理:自动处理标记了 [Token] 特性的参数
- 错误处理和日志记录:包含完整的异常处理和日志记录
- XML 注释保留:自动保留原始方法的 XML 文档注释
- 重载方法支持:正确处理重载方法的 XML 注释
- 灵活的配置:支持自定义 Token 管理接口和包装接口名称
基本用法
1. 定义 Token 管理接口
/// <summary>
/// Token管理接口
/// </summary>
public interface ITokenManage
{
Task<string> GetTokenAsync();
}
/// <summary>
/// 钉钉Token管理接口
/// </summary>
public interface IDingTokenManage
{
Task<string> GetTokenAsync();
}
2. 定义 HTTP API 接口并添加包装特性
/// <summary>
/// 测试场景1:使用默认Token管理接口(ITokenManage)
/// </summary>
[HttpClientApi("https://api.dingtalk.com", Timeout = 60)]
[HttpClientApiWrap("ITokenManage")]
public interface ISingleTestApi
{
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="token">访问令牌</param>
/// <param name="birthday">生日日期</param>
/// <returns>用户信息</returns>
[Get("/api/v1/user/{birthday}")]
Task<UserDto> GetUserAsync([Token][Header("x-token")] string token, [Path("yyyy-MM-dd")] DateTime birthday);
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="birthday">生日日期</param>
/// <param name="token">访问令牌</param>
/// <returns>用户信息</returns>
[Get("/api/v1/user/{birthday}")]
Task<UserDto> GetUserAsync([Path("yyyy-MM-dd")] DateTime birthday, [Token][Header("x-token")] string token);
/// <summary>
/// 搜索用户
/// </summary>
/// <param name="input">搜索条件</param>
/// <param name="age">年龄</param>
/// <param name="token">访问令牌</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>用户列表</returns>
[Get("/api/v1/user/{birthday}")]
Task<UserDto> GetUser1Async([Query] SysDeptQueryInput input, [Query] int? age, [Token][Header("x-token")] string token, CancellationToken cancellationToken = default);
}
3. 生成的包装接口代码
自动生成的包装接口:
// 自动生成的代码 - ISingleTestApi.Wrap.g.cs
using System;
using System.Threading.Tasks;
namespace YourNamespace
{
/// <summary>
/// ISingleTestApi的包装接口
/// </summary>
public partial interface ISingleTestApiWrap
{
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="birthday">生日日期</param>
/// <returns>用户信息</returns>
Task<UserDto> GetUserAsync(DateTime birthday);
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="birthday">生日日期</param>
/// <returns>用户信息</returns>
Task<UserDto> GetUserAsync(DateTime birthday);
/// <summary>
/// 搜索用户
/// </summary>
/// <param name="input">搜索条件</param>
/// <param name="age">年龄</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>用户列表</returns>
Task<UserDto> GetUser1Async(SysDeptQueryInput input, int? age, CancellationToken cancellationToken = default);
}
}
4. 生成的包装实现类代码
自动生成的包装实现类:
// 自动生成的代码 - ISingleTestApi.WrapImpl.g.cs
using System;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace YourNamespace
{
/// <summary>
/// ISingleTestApi的包装实现类
/// </summary>
internal partial class SingleTestApiWrap : ISingleTestApiWrap
{
private readonly ISingleTestApi _singleTestApi;
private readonly ITokenManage _tokenManage;
private readonly ILogger<SingleTestApiWrap> _logger;
public SingleTestApiWrap(ISingleTestApi singleTestApi, ITokenManage tokenManage, ILogger<SingleTestApiWrap> logger)
{
_singleTestApi = singleTestApi;
_tokenManage = tokenManage;
_logger = logger;
}
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="birthday">生日日期</param>
/// <returns>用户信息</returns>
public async Task<UserDto> GetUserAsync(DateTime birthday)
{
try
{
var token = await _tokenManage.GetTokenAsync();
if (string.IsNullOrEmpty(token))
{
_logger.LogWarning("获取到的Token为空!");
}
return await _singleTestApi.GetUserAsync(token, birthday);
}
catch (Exception x)
{
_logger.LogError(x, "执行GetUserAsync操作失败:{message}", x.Message);
throw;
}
}
/// <summary>
/// 获取用户信息
/// </summary>
/// <param name="birthday">生日日期</param>
/// <returns>用户信息</returns>
public async Task<UserDto> GetUserAsync(DateTime birthday)
{
try
{
var token = await _tokenManage.GetTokenAsync();
if (string.IsNullOrEmpty(token))
{
_logger.LogWarning("获取到的Token为空!");
}
return await _singleTestApi.GetUserAsync(birthday, token);
}
catch (Exception x)
{
_logger.LogError(x, "执行GetUserAsync操作失败:{message}", x.Message);
throw;
}
}
/// <summary>
/// 搜索用户
/// </summary>
/// <param name="input">搜索条件</param>
/// <param name="age">年龄</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>用户列表</returns>
public async Task<UserDto> GetUser1Async(SysDeptQueryInput input, int? age, CancellationToken cancellationToken = default)
{
try
{
var token = await _tokenManage.GetTokenAsync();
if (string.IsNullOrEmpty(token))
{
_logger.LogWarning("获取到的Token为空!");
}
return await _singleTestApi.GetUser1Async(input, age, token, cancellationToken);
}
catch (Exception x)
{
_logger.LogError(x, "执行GetUser1Async操作失败:{message}", x.Message);
throw;
}
}
}
}
高级配置选项
1. 使用指定的 Token 管理接口
// 使用指定的Token管理接口
[HttpClientApi("https://api.dingtalk.com", Timeout = 60)]
[HttpClientApiWrap(TokenManage = "IDingTokenManage")]
public interface ISingleTestApi2
{
[Get("/api/v1/user/{birthday}")]
Task<UserDto> GetUserAsync([Token][Header("x-token")] string token, [Path("yyyy-MM-dd")] DateTime birthday);
}
2. 自定义包装接口名称
// 自定义包装接口名称
[HttpClientApi("https://api.dingtalk.com", Timeout = 60)]
[HttpClientApiWrap(TokenManage = "ITokenManage", WrapInterface = "IDingTalkUserWrap")]
public interface ISingleTestApi3
{
[Get("/api/v1/user/{birthday}")]
Task<UserDto> GetUserAsync([Token][Header("x-token")] string token, [Path("yyyy-MM-dd")] DateTime birthday);
}
依赖注入注册
使用 HttpClientRegistrationGenerator 自动生成依赖注入注册代码:
// 自动生成的注册代码 - HttpClientApiExtensions.g.cs
public static class HttpClientApiExtensions
{
/// <summary>
/// 注册所有标记了 [HttpClientApi] 特性的接口及其 HttpClient 实现
/// </summary>
public static IServiceCollection AddWebApiHttpClient(this IServiceCollection services)
{
// 注册基本HttpClient API
services.AddHttpClient<ISingleTestApi, SingleTestApi>(client =>
{
client.BaseAddress = new Uri("https://api.dingtalk.com");
client.Timeout = TimeSpan.FromSeconds(60);
});
// 注册包装API的瞬时服务
AddWebApiHttpClientWrap(services);
return services;
}
/// <summary>
/// 注册所有包装接口及其包装实现类的瞬时服务
/// </summary>
public static IServiceCollection AddWebApiHttpClientWrap(this IServiceCollection services)
{
// 注册包装接口和实现类
services.AddTransient<ISingleTestApiWrap, SingleTestApiWrap>();
return services;
}
}
使用方式
1. 在应用程序中注册服务
// 在 Program.cs 或 Startup.cs 中
var builder = WebApplication.CreateBuilder(args);
// 注册 Token 管理服务
builder.Services.AddScoped<ITokenManage, YourTokenManageImplementation>();
// 自动注册所有 HttpClient API 和包装服务
builder.Services.AddWebApiHttpClient();
2. 在服务中使用包装接口
public class UserService
{
private readonly ISingleTestApiWrap _singleTestApiWrap;
public UserService(ISingleTestApiWrap singleTestApiWrap)
{
_singleTestApiWrap = singleTestApiWrap;
}
public async Task<UserDto> GetUserAsync(DateTime birthday)
{
// 无需手动处理 Token,包装类会自动处理
return await _singleTestApiWrap.GetUserAsync(birthday);
}
}
功能优势
- 简化 Token 管理:自动处理 Token 获取和传递
- 统一错误处理:提供一致的异常处理和日志记录
- 代码复用:避免在每个 API 调用中重复 Token 处理逻辑
- 易于测试:可以轻松模拟 Token 管理接口进行单元测试
- 可扩展性:支持自定义 Token 管理策略
最佳实践
- 统一的 Token 管理:为不同类型的 API 使用不同的 Token 管理接口
- 合理的日志级别:根据业务需求设置适当的日志级别
- 异常处理策略:在包装类中实现合适的异常处理策略
- 性能考虑:考虑 Token 缓存机制以提高性能
- 安全性:确保 Token 的存储和传输安全
生成的代码结构
obj/Debug/net8.0/generated/
├── Mud.ServiceCodeGenerator/
├── HttpClientApiSourceGenerator/
│ └── YourNamespace.ISingleTestApi.g.cs
├── HttpClientApiInterfaceWrapSourceGenerator/
│ └── YourNamespace.ISingleTestApi.Wrap.g.cs
├── HttpClientApiWrapClassSourceGenerator/
│ └── YourNamespace.ISingleTestApi.WrapImpl.g.cs
└── HttpClientApiRegisterSourceGenerator/
└── HttpClientApiExtensions.g.cs
生成代码查看
要查看生成的代码,可以在项目文件中添加以下配置:
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>
生成的代码将位于 obj/[Configuration]/[TargetFramework]/generated/ 目录下,文件名以 .g.cs 结尾。
维护者
许可证
本项目采用MIT许可证模式:
免责声明
本项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任。
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Mud.ServiceCodeGenerator:
| Package | Downloads |
|---|---|
|
Mud.Feishu
MudFeishu 用于简化.NET与飞书(Feishu)API 集成。提供一组类型安全的客户端和服务,便于在 .NET 应用程序中调用飞书 API。 |
|
|
Mud.Feishu.Abstractions
Mud.Feishu.Abstractions 是 MudFeishu 库的 WebSocket 事件订阅和 Webhook 事件订阅组件抽象层,专门用于处理飞书事件订阅。它提供了基于策略模式的完整事件处理机制,使开发人员能够轻松地在 .NET 应用程序中接收和处理飞书实时事件。 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 1.3.9 | 258 | 12/11/2025 | |
| 1.3.8 | 285 | 12/10/2025 | |
| 1.3.7 | 286 | 12/5/2025 | |
| 1.3.6 | 449 | 12/1/2025 | |
| 1.3.5 | 331 | 11/30/2025 | |
| 1.3.4 | 137 | 11/28/2025 | |
| 1.3.3 | 187 | 11/27/2025 | |
| 1.3.2 | 195 | 11/25/2025 | |
| 1.3.1 | 172 | 11/25/2025 | |
| 1.3.0 | 408 | 11/24/2025 | |
| 1.2.9 | 359 | 11/23/2025 | |
| 1.2.8 | 550 | 11/21/2025 | |
| 1.2.7 | 633 | 11/19/2025 | |
| 1.2.6 | 622 | 11/18/2025 | |
| 1.2.5 | 509 | 11/17/2025 | |
| 1.2.4 | 500 | 11/12/2025 | |
| 1.2.3 | 416 | 11/6/2025 | |
| 1.2.2 | 415 | 11/4/2025 | |
| 1.2.1 | 421 | 10/31/2025 | |
| 1.2.0 | 472 | 10/28/2025 | |
| 1.1.9 | 469 | 10/15/2025 | |
| 1.1.8 | 451 | 10/9/2025 | |
| 1.1.7 | 396 | 10/4/2025 | |
| 1.1.6 | 467 | 9/30/2025 | |
| 1.1.5 | 478 | 9/29/2025 | |
| 1.1.4 | 445 | 7/11/2025 | |
| 1.1.3 | 267 | 12/31/2024 | |
| 1.1.2 | 237 | 12/30/2024 | |
| 1.1.1 | 229 | 12/30/2024 | |
| 1.1.0 | 234 | 12/29/2024 | |
| 1.0.9.4 | 232 | 12/28/2024 | |
| 1.0.9.3 | 239 | 12/28/2024 | |
| 1.0.9.1 | 244 | 12/26/2024 | |
| 1.0.9 | 234 | 12/26/2024 | |
| 1.0.8 | 238 | 12/25/2024 | |
| 1.0.7 | 238 | 12/25/2024 | |
| 1.0.6 | 234 | 12/25/2024 | |
| 1.0.5 | 239 | 12/25/2024 | |
| 1.0.4 | 235 | 12/25/2024 | |
| 1.0.3 | 236 | 12/25/2024 | |
| 1.0.2 | 238 | 12/25/2024 | |
| 1.0.1 | 237 | 12/25/2024 | |
| 0.3.1 | 244 | 12/26/2024 |