news 2026/1/9 12:01:53

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
解放双手!使用Roslyn生成代码让你的 HTTP 客户端开发变得如此简单

什么是 Mud 代码生成器?

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

服务类代码生成 - 根据实体类自动生成服务接口和服务实现类

依赖注入代码生成 - 自动为类生成构造函数注入代码,包括日志、缓存、用户管理等常用服务

服务注册代码生成 - 自动生成服务注册扩展方法,简化依赖注入配置

HttpClient API 代码生成 - 自动为标记了 HTTP 方法特性的接口生成 HttpClient 实现类

HttpClient API 源生成器详解

核心功能

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

工作原理

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

扫描项目中的接口,查找标记了 [HttpClientApi] 特性的接口

分析接口中定义的方法和参数

根据 HTTP 方法特性(如 [Get], [Post], [Put] 等)生成相应的实现代码

处理各种参数特性(如 [Path], [Query], [Body], [Header])

生成完整的 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 服务的配置。

工作原理

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

扫描项目中的接口,查找标记了 [HttpClientApi] 特性的接口

提取特性中的配置参数(如 BaseUrl、Timeout 等)

生成用于依赖注入的扩展方法

自动注册接口和实现类到服务容器中

使用示例

首先定义 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 完美配合,提供完整的开发体验:

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

}

}

高级配置

自定义 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

最佳实践

统一配置:在 [HttpClientApi] 特性中统一配置所有 API 的基础设置

合理超时:根据 API 的响应时间设置合理的超时时间

命名规范:遵循接口命名规范(I{ServiceName}Api)

错误处理:在服务层处理 API 调用异常

日志记录:利用生成的日志记录功能监控 API 调用

如何查看生成的代码

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

<PropertyGroup>

<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>

</PropertyGroup>

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/25 13:18:44

关于计算机语言的学习

在这个系列的笔记中&#xff0c;我将记录下自己在学习计算机语言的过程中所积累的一些心得体会&#xff0c;笔记的内容将会包括我对编程语言、标记语言的了解&#xff0c;以及我学习各种语言的具体过程。希望这些笔记能够帮助到那些对编程语言感兴趣的朋友们。下面是这一系列学…

作者头像 李华
网站建设 2025/12/14 10:54:19

LeRobot机器人控制系统完整实战指南:从入门到精通

LeRobot作为一款开源的机器人控制系统框架&#xff0c;为机器人开发者提供了从硬件控制到智能算法的完整解决方案。无论你是机器人初学者还是专业开发者&#xff0c;都能在这个框架中找到适合自己的工具和模块。 【免费下载链接】lerobot &#x1f917; LeRobot: State-of-the-…

作者头像 李华
网站建设 2025/12/14 10:53:12

14、网络存储搭建与管理全攻略

网络存储搭建与管理全攻略 在日常使用中,我们常常面临本地系统存储空间不足,却需要存储大量数据的情况。同时,在多系统、多用户的网络环境下,快速共享数据也成为了一个迫切的需求。Linux 系统为我们提供了多种解决方案,如 Samba、NFS 等,能够帮助我们搭建集中式存储服务…

作者头像 李华
网站建设 2025/12/16 19:40:30

GLM-4.6技术突破:200K上下文+工具调用引领智能体新纪元

GLM-4.6技术突破&#xff1a;200K上下文工具调用引领智能体新纪元 【免费下载链接】GLM-4.6 GLM-4.6在GLM-4.5基础上全面升级&#xff1a;200K超长上下文窗口支持复杂任务&#xff0c;代码性能大幅提升&#xff0c;前端页面生成更优。推理能力增强且支持工具调用&#xff0c;智…

作者头像 李华
网站建设 2026/1/6 7:08:11

5分钟搞定PCSX2模拟器:《真实犯罪:纽约》高清优化终极指南

还在为PCSX2模拟器运行《真实犯罪&#xff1a;纽约》时出现的画面模糊、纹理错位而烦恼吗&#xff1f;本文通过快速上手、核心原理到疑难排错的递进式结构&#xff0c;帮你彻底解决高清渲染问题&#xff0c;让你在1080p分辨率下流畅体验这款经典开放世界游戏。 【免费下载链接】…

作者头像 李华
网站建设 2025/12/29 20:21:37

iOS功能开关架构设计:5步构建企业级远程配置系统

iOS功能开关架构设计&#xff1a;5步构建企业级远程配置系统 【免费下载链接】awesome-ios-architecture :japanese_castle: Better ways to structure iOS apps 项目地址: https://gitcode.com/gh_mirrors/aw/awesome-ios-architecture 在当今快速迭代的移动应用开发环…

作者头像 李华