解放双手!使用Roslyn生成代码让你的 HTTP 客户端开发变得如此简单

news/2025/11/9 18:31:29/文章来源:https://www.cnblogs.com/mudtools/p/19204659

在现代 .NET 开发中,源代码生成器(Source Generators)是一项强大的功能,它允许开发者在编译时自动生成代码,从而减少样板代码的编写,提高开发效率和代码质量。本文主要介绍使用Roslyn实现两个代码生成器:HttpClientApiSourceGenerator 和 HttpClientApiRegisterSourceGenerator,这两个生成器专门用于简化 HTTP 客户端的开发和配置。

什么是 Mud 代码生成器?

Mud 代码生成器是一个基于 Roslyn 的源代码生成器,用于自动生成数据实体、服务层相关代码,提高开发效率。服务层代码生成包含以下主要功能:

  1. 服务类代码生成 - 根据实体类自动生成服务接口和服务实现类
  2. 依赖注入代码生成 - 自动为类生成构造函数注入代码,包括日志、缓存、用户管理等常用服务
  3. 服务注册代码生成 - 自动生成服务注册扩展方法,简化依赖注入配置
  4. HttpClient API 代码生成 - 自动为标记了 HTTP 方法特性的接口生成 HttpClient 实现类

HttpClient API 源生成器详解

核心功能

HttpClientApiSourceGenerator 是一个专门用于生成 HttpClient 实现类的源代码生成器。它基于 Roslyn 技术,能够自动为标记了 [HttpClientApi] 特性的接口生成完整的 HttpClient 实现类,支持 RESTful API 调用。

工作原理

源生成器的工作流程如下:

  1. 扫描项目中的接口,查找标记了 [HttpClientApi] 特性的接口
  2. 分析接口中定义的方法和参数
  3. 根据 HTTP 方法特性(如 [Get], [Post], [Put] 等)生成相应的实现代码
  4. 处理各种参数特性(如 [Path], [Query], [Body], [Header])
  5. 生成完整的 HttpClient 实现类,包括构造函数、日志记录、错误处理等

使用示例

让我们通过一个具体的示例来了解如何使用这个生成器:

[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);
}

当项目编译时,HttpClientApiSourceGenerator 会自动生成一个实现该接口的类,大致如下:

// 自动生成的代码
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();
}

参数特性详解

生成器支持多种参数特性,以处理不同的 HTTP 请求参数:

Path 参数特性

用于替换 URL 模板中的路径参数:

[Get("/api/users/{userId}/orders/{orderId}")]
Task<OrderDto> GetOrderAsync([Path] string userId, [Path] string orderId);

Query 参数特性

用于生成查询字符串参数:

[Get("/api/users")]
Task<List<UserDto>> GetUsersAsync([Query] string name, [Query] int? page, [Query] int? pageSize);

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);

Header 参数特性

用于设置请求头:

[Get("/api/protected")]
Task<ProtectedData> GetProtectedDataAsync([Header] string authorization);// 自定义头名称
[Get("/api/protected")]
Task<ProtectedData> GetProtectedDataAsync([Header("X-API-Key")] string apiKey);

复杂参数处理

生成器还能处理复杂的参数类型:

复杂查询参数

支持复杂对象作为查询参数,自动展开为键值对:

[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

路径参数自动替换

自动处理 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;}
}

HttpClient API 注册源生成器详解

核心功能

HttpClientApiRegisterSourceGenerator 是另一个重要的组件,它自动为标记了 [HttpClientApi] 特性的接口生成依赖注入注册代码,简化 HttpClient 服务的配置。

工作原理

该生成器的工作流程如下:

  1. 扫描项目中的接口,查找标记了 [HttpClientApi] 特性的接口
  2. 提取特性中的配置参数(如 BaseUrl、Timeout 等)
  3. 生成用于依赖注入的扩展方法
  4. 自动注册接口和实现类到服务容器中

使用示例

首先定义 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);
}

生成器会自动生成以下注册代码:

// 自动生成的代码 - 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;}}
}

配置选项

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 { }

使用方式

在应用程序启动时调用

// 在 Program.cs 或 Startup.cs 中
var builder = WebApplication.CreateBuilder(args);// 自动注册所有 HttpClient API 服务
builder.Services.AddWebApiHttpClient();// 或者与其他服务注册一起使用
builder.Services.AddControllers().AddWebApiHttpClient();

在控制台应用程序中使用

// 在控制台应用程序中
var services = new ServiceCollection();// 注册 HttpClient API 服务
services.AddWebApiHttpClient();var serviceProvider = services.BuildServiceProvider();
var dingTalkApi = serviceProvider.GetRequiredService<IDingTalkApi>();

两个生成器的协同工作

HttpClientApiRegisterSourceGenerator 与 HttpClientApiSourceGenerator 完美配合,提供完整的开发体验:

  1. HttpClientApiSourceGenerator 生成接口的实现类
  2. HttpClientApiRegisterSourceGenerator 生成依赖注入注册代码
  3. 完整的开发体验:定义接口 → 自动生成实现 → 自动注册服务

完整示例

// 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);}
}

高级配置

自定义 HttpClient 配置

如果需要更复杂的 HttpClient 配置,可以在注册后继续配置:

builder.Services.AddWebApiHttpClient().ConfigureHttpClientDefaults(httpClient =>{httpClient.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler{UseProxy = false,AllowAutoRedirect = false});});

添加自定义请求头

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

最佳实践

  1. 统一配置:在 [HttpClientApi] 特性中统一配置所有 API 的基础设置
  2. 合理超时:根据 API 的响应时间设置合理的超时时间
  3. 命名规范:遵循接口命名规范(I{ServiceName}Api)
  4. 错误处理:在服务层处理 API 调用异常
  5. 日志记录:利用生成的日志记录功能监控 API 调用

如何查看生成的代码

要查看生成的代码,可以在项目文件中添加以下配置:

<PropertyGroup><EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>

生成的代码将位于 obj/[Configuration]/[TargetFramework]/generated/ 目录下,文件名以 .g.cs 结尾。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/960639.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pandoc用法

要将你的 `文件V5.0_2.docx` 文件转换为 Markdown 格式,使用 **Pandoc** 是一个非常直接的选择。我已经为你准备好了相应的命令,并补充了一些实用技巧。 ### 📝 核心转换命令 在终端或命令行中,导航到你的 `.docx…

JMeter:性能测试利器全解析 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

251109

JT-JY13T1S1Hello, Tourist information center, Mike speaking, how can i help you? Hi, i want to find out about cockery classes. I believe there is some one-day calsses for tourists. Well, they are open…

electron-vite为linux打包成功,但是安装后运行无反应

属于shark插件没有运行 在代码中使用动态导入,如果导入成功就使用shark压缩,如果没有就不压缩。 `// 动态导入 sharp,如果加载失败则优雅降级 let sharp: any = null let sharpAvailable = false async function lo…

吐血推荐!6款超好用的AI论文写作工具

吐血推荐!6款超好用的AI论文写作工具 在学术写作这条漫长的道路上,高效的工具常常能够助力前行。本文会为你详细介绍6款备受赞誉的AI论文写作工具,让你在论文创作时如虎添翼。 1. 鲲鹏智写:一站式论文深度解决方案…

完整教程:金蝶云星瀚 | 生产制造成本核算终极实操手册(从0到1,含两套完整案例)

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

PHP中各种超全局变量使用

在PHP中,超全局变量是由PHP系统预先定义的一套变量,它们在脚本的所有作用域中都是可用的。这意味着开发者不需要执行特定的global语句就能访问这些变量。以下是一些常用的PHP超全局变量以及它们的使用方法和注意事项…

详细介绍:对于返回倒数第 k 个节点、链表的回文结构、链表相交题目的解析

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

实用指南:TensorFlow2 Python深度学习 - TensorFlow2框架入门 - 自动微分和梯度

实用指南:TensorFlow2 Python深度学习 - TensorFlow2框架入门 - 自动微分和梯度pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-…

浏览器Blockstack.org全名字段输入限制缺失漏洞分析

本文详细分析了Blockstack浏览器端全名字段缺乏输入长度限制导致的服务中断风险,包括漏洞发现过程、安全影响评估以及开发团队与安全研究员的讨论过程。漏洞报告 #304073 - browser.blockstack.org 全名字段字符串大小…

2025年维修厂家推荐排行榜单:行业权威解析

摘要 随着制冷行业在2025年的快速发展,维修厂家在保障设备高效运行中扮演关键角色。本文基于行业数据和用户口碑,解析2025年维修厂家排行榜单,重点推荐优质服务商,并提供详细表单供参考,帮助用户选择可靠的合作伙…

2025年维修厂家口碑排行榜:专业制冷服务首选

摘要 2025年维修厂家行业正迎来技术革新与市场需求增长,制冷设备维修服务成为关键领域。本文基于行业数据与用户反馈,推出维修厂家推荐排行榜单,旨在帮助用户选择靠谱的合作伙伴。榜单涵盖口碑、实力、服务等多维度…

行业内专业的维修厂家功能亮点

摘要 随着制冷行业的快速发展,2025年维修厂家市场呈现出技术升级和服务多元化的趋势。本文基于行业数据和用户口碑,解析维修厂家的核心功能亮点,并提供一份权威的维修厂家推荐排行榜单,供用户参考选择。表单排名综…

Dask-权威指南-全-

Dask 权威指南(全)原文:annas-archive.org/md5/4f64056c14690c5478291f8391f41fa7 译者:飞龙 协议:CC BY-NC-SA 4.0第一章:理解 Dask DataFrames 的架构 Dask DataFrames 允许您扩展您的 pandas 工作流。Dask Da…

DevOps-文化中的协作指南-全-

DevOps 文化中的协作指南(全)原文:zh.annas-archive.org/md5/747ac673186de3c38ee667bd2f54b035 译者:飞龙 协议:CC BY-NC-SA 4.0序言 本书面向已经建立起领导地位以及正在走向领导地位的人士。它专注于有效协作—…

WGCLOUD磁盘告警有没有恢复通知

有的 WGCLOUD磁盘告警后,如果我们处理后,系统也会发送恢复通知

疑似 CSP-SB、CSP-JB、NOSb 考题泄露

每日一题,防止变蠢[!] NOSb 考题。 [?] CSP-JB 考题。 [.] CSP-SB 考题。 这种代码难度很低的清新小题还是很有意思的。[?] \(\text{mex}\times \min\)。[?] 树的边双连通分量。[?] 对于 \(n\le 10^5\) 的点 \(11…

人工智能团队的技术工具

人工智能团队的技术工具