Mud 服务代码生成器
功能介绍
Mud 服务代码生成器是一个基于 Roslyn 的源代码生成器,用于自动生成服务层相关代码,提高开发效率。它包含以下主要功能:
- 服务类代码生成 - 根据实体类自动生成服务接口和服务实现类
- 依赖注入代码生成 - 自动为类生成构造函数注入代码,包括日志、缓存、用户管理等常用服务
- 服务注册代码生成 - 自动生成服务注册扩展方法,简化依赖注入配置
- HttpClient API 代码生成 - 自动为标记了 HTTP 方法特性的接口生成 HttpClient 实现类
项目参数配置
在使用 Mud 服务代码生成器时,可以通过在项目文件中配置以下参数自定义生成行为:
通用配置参数
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> <!-- 在obj目录下保存生成的代码 -->
<!-- 依赖注入相关配置 -->
<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> <!-- 需要生成代码的接口实现程序集 -->
<!-- DTO生成相关配置 -->
<EntityAttachAttributes>SuppressSniffer</EntityAttachAttributes> <!-- 实体类加上Attribute特性配置,多个特性时使用','分隔 -->
</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 调用。
基本用法
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=IT2. 路径参数自动替换
自动处理 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. 组合使用多个参数特性
[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 调用
生成代码查看
要查看生成的代码,可以在项目文件中添加以下配置:
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>生成的代码将位于 obj/[Configuration]/[TargetFramework]/generated/ 目录下,文件名以 .g.cs 结尾。
维护者
许可证
本项目采用MIT许可证模式:
免责声明
本项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任。