Mud.HttpUtils.Attributes
2.0.8
dotnet add package Mud.HttpUtils.Attributes --version 2.0.8
NuGet\Install-Package Mud.HttpUtils.Attributes -Version 2.0.8
<PackageReference Include="Mud.HttpUtils.Attributes" Version="2.0.8" />
<PackageVersion Include="Mud.HttpUtils.Attributes" Version="2.0.8" />
<PackageReference Include="Mud.HttpUtils.Attributes" />
paket add Mud.HttpUtils.Attributes --version 2.0.8
#r "nuget: Mud.HttpUtils.Attributes, 2.0.8"
#:package Mud.HttpUtils.Attributes@2.0.8
#addin nuget:?package=Mud.HttpUtils.Attributes&version=2.0.8
#tool nuget:?package=Mud.HttpUtils.Attributes&version=2.0.8
Mud.HttpUtils.Attributes
概述
Mud.HttpUtils.Attributes 是 Mud.HttpUtils 的特性定义层,提供 HTTP API 声明式编程所需的全部特性标注。
仅依赖 Mud.HttpUtils.Abstractions,自身无其他外部依赖。
命名空间约定:本程序集内的全部特性均位于
Mud.HttpUtils.Attributes(G8-13 起,含此前唯一的例外AllowUnmatchedRouteParametersAttribute,原位于Mud.HttpUtils—— 见「破坏性变更」)。 源生成器按简单名匹配特性,因此新增/改名特性前必须核对Mud.HttpUtils.Generator/README.md的匹配表 与HttpClientGeneratorConstants,否则特性会静默失效且无诊断。
目标框架
netstandard2.0
包含内容
核心特性
| 特性 | 用途 | 目标 | 关键属性 |
|---|---|---|---|
HttpClientApiAttribute |
标注 HTTP API 接口 | Interface | ContentType, Timeout, TokenManage, HttpClient, RegistryGroupName, IsAbstract, InheritedFrom |
BasePathAttribute |
标注接口基础路径前缀 | Interface | Path |
HTTP 方法特性
| 特性 | 用途 | 目标 |
|---|---|---|
GetAttribute |
GET 请求 | Method |
PostAttribute |
POST 请求 | Method |
PutAttribute |
PUT 请求 | Method |
DeleteAttribute |
DELETE 请求(支持带请求体) | Method |
PatchAttribute |
PATCH 请求 | Method |
HeadAttribute |
HEAD 请求 | Method |
OptionsAttribute |
OPTIONS 请求 | Method |
所有 HTTP 方法特性继承自 HttpMethodAttribute,支持以下公共属性:
| 属性 | 类型 | 说明 |
|---|---|---|
HttpMethod |
HttpMethod |
HTTP 方法(由构造函数按特性名推导) |
RequestUri |
string? |
请求路径模板(G8-13:原文档误记为 Route,已修正) |
ContentType |
string? |
请求内容类型 |
ResponseContentType |
string? |
响应内容类型 |
ResponseEnableDecrypt |
bool |
响应是否启用解密 |
参数特性
| 特性 | 用途 | 目标 | 关键属性 |
|---|---|---|---|
PathAttribute |
路径参数 | Parameter / Property | Name, Format, UrlEncode |
QueryAttribute |
查询参数 | Parameter / Property | Name, Format, AliasAs, Separator, Prefix, CollectionFormat, TreatAsString, SerializeNull(G8-13:原文档误记 Encode,已修正 —— 编码语义在 QueryMapAttribute.UrlEncode) |
QueryMapAttribute |
查询参数映射(对象/字典展开) | Parameter / Property | PropertySeparator, SerializationMethod, UrlEncode, IncludeNullValues |
RawQueryStringAttribute |
原始查询字符串 | Parameter | (空标记特性,无属性;G8-13:原文档误记 PrependQuestionMark,已删除) |
ArrayQueryAttribute |
数组查询参数 | Parameter | Separator |
HeaderAttribute |
请求头参数 | Parameter / Method / Interface / Property | Name, Value, AliasAs, Replace, FormatString |
BodyAttribute |
请求体参数 | Parameter | ContentType, EnableEncrypt, EncryptSerializeType, EncryptPropertyName, RawString, UseStringContent |
TokenAttribute |
令牌参数 | Parameter / Interface / Method | TokenType, InjectionMode, Name, Scopes, TokenManagerKey, Scheme, RequiresUserId(G8-13:原文档误记 Replace 且漏列 Scheme,已修正) |
FilePathAttribute |
文件路径参数(上传/下载) | Parameter / Property | BufferSize、Overwrite |
FormContentAttribute |
表单内容参数 | Parameter / Class | — |
FormAttribute |
表单字段(URL 编码) | Parameter | FieldName |
MultipartFormAttribute |
多部分表单字段 | Parameter | — |
UploadAttribute |
文件上传参数 | Parameter | FieldName, FileName, ContentType |
缓存特性
| 特性 | 用途 | 目标 | 关键属性 |
|---|---|---|---|
CacheAttribute |
响应缓存标注 | Method | DurationSeconds, CacheKeyTemplate, VaryByUser, UseSlidingExpiration(原 Priority 属性已随 CFG-27 移除,见下文) |
弹性策略特性
| 特性 | 用途 | 目标 | 关键属性 |
|---|---|---|---|
RetryAttribute |
方法级重试策略 | Method | MaxRetries, DelayMilliseconds, UseExponentialBackoff |
TimeoutAttribute |
方法级超时策略 | Method | TimeoutMilliseconds |
CircuitBreakerAttribute |
方法级熔断策略 | Method | FailureThreshold, BreakDurationSeconds, SamplingDurationSeconds, MinimumThroughput |
安全与脱敏特性
| 特性 | 用途 | 目标 | 关键属性 |
|---|---|---|---|
SensitiveDataAttribute |
标记敏感数据属性 | Property(仅属性,CFG-11 收窄) | MaskMode, PrefixLength, SuffixLength |
控制特性
| 特性 | 用途 | 目标 |
|---|---|---|
IgnoreGeneratorAttribute |
忽略代码生成(接口级=完全不介入;方法级=仅跳过该方法) | Interface / Method(E-2 收窄) |
AllowAnyStatusCodeAttribute |
允许任意 HTTP 状态码 | Interface / Method |
HeaderMergeAttribute |
头部合并模式控制 | Interface / Method |
SerializationMethodAttribute |
请求体序列化方法控制 | Interface / Method |
InterfacePathAttribute |
接口级固定路径参数 | Interface |
InterfaceQueryAttribute |
接口级固定查询参数 | Interface |
关联枚举
| 枚举 | 说明 |
|---|---|
HeaderMergeMode |
头部合并模式(Append / Replace / Ignore),配合 HeaderMergeAttribute |
SerializationMethod |
请求体序列化方法(Json / Xml / FormUrlEncoded),配合 SerializationMethodAttribute |
QuerySerializationMethod |
QueryMap 序列化方法(ToString / Json),配合 QueryMapAttribute |
CachePriority |
❌ 已移除(CFG-27):缓存优先级枚举,运行时无消费点 |
TokenInjectionMode(Header/Query/Path/ApiKey/HmacSignature/BasicAuth/Cookie)与SensitiveDataMaskMode(Hide/Mask/TypeOnly)见对应章节。
事件处理特性
| 特性 | 用途 | 目标 |
|---|---|---|
GenerateEventHandlerAttribute |
生成事件处理器 | Class |
安装
<PackageReference Include="Mud.HttpUtils.Attributes" Version="x.x.x" />
使用场景
配合源代码生成器使用(推荐)
当你需要声明式定义 HTTP API 接口,配合 Mud.HttpUtils.Generator 源代码生成器自动生成实现代码时:
using Mud.HttpUtils.Attributes;
[HttpClientApi(HttpClient = "IEnhancedHttpClient")]
public interface IUserApi
{
[Get("/users/{id}")]
Task<UserInfo> GetUserAsync([Path] int id);
[Post("/users")]
Task<UserInfo> CreateUserAsync([Body] CreateUserRequest request);
[Delete("/users/{id}")]
Task<bool> DeleteUserAsync([Path] int id, [Body] DeleteReason reason);
[Get("/users")]
Task<List<UserInfo>> SearchUsersAsync([Query] string keyword);
}
仅需特性定义
当你需要在共享类库中定义 API 接口合同,而不需要引入客户端实现或生成器时:
<PackageReference Include="Mud.HttpUtils.Attributes" Version="x.x.x" />
HttpClientApiAttribute 详解
三种运行模式
// 模式一:默认模式(构造函数依赖 IMudAppContext)
[HttpClientApi]
public interface IDefaultApi { }
// 模式二:TokenManager 模式(构造函数依赖指定的 Token 管理器)
[HttpClientApi(TokenManage = "IFeishuAppManager")]
public interface ITokenApi { }
// 模式三:HttpClient 模式(构造函数依赖指定的 HttpClient 接口,推荐)
[HttpClientApi(HttpClient = "IEnhancedHttpClient")]
public interface IHttpClientApi { }
注意:
HttpClient与TokenManage互斥,同时定义时HttpClient优先。 CFG-27:BaseAddress构造函数与属性已移除(此前为[Obsolete(error: true)])。 使用将产生编译错误CS0117;请通过AddMudHttpClient(clientName, baseAddress)或AddMudHttpGeneratedClient<T>(clientName)配置基地址。
全部属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ContentType |
string |
"application/json" |
默认请求内容类型 |
Timeout |
int |
50 |
超时时间(秒),生成器会在注册代码中生成 client.Timeout 设置 |
TokenManage |
string? |
null |
Token 管理器接口类型全名 |
HttpClient |
string? |
null |
HttpClient 接口类型全名 |
RegistryGroupName |
string? |
null |
注册组名称,影响生成的注册方法名 |
IsAbstract |
bool |
false |
是否生成抽象类 |
InheritedFrom |
string? |
null |
继承的基类名称 |
BodyAttribute 详解
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ContentType |
string? |
null |
请求体内容类型(优先级最高) |
EnableEncrypt |
bool |
false |
是否启用加密 |
EncryptSerializeType |
SerializeType |
Json |
加密序列化类型 |
EncryptPropertyName |
string |
"data" |
加密后的属性名 |
RawString |
bool |
false |
是否作为原始字符串发送(不进行 JSON 序列化,也不调用 ToString()) |
UseStringContent |
bool |
false |
是否将参数作为字符串内容发送(调用 ToString()) |
RawString 用法
当需要直接发送纯文本或预格式化字符串时,使用 RawString = true:
[Post("/api/content")]
Task PostContentAsync([Body(RawString = true)] string content);
UseStringContent 用法
当需要将对象调用 ToString() 后作为字符串内容发送时:
[Post("/api/text")]
Task SendTextAsync([Body(UseStringContent = true)] object message);
TokenAttribute 详解
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
TokenType |
string |
"AccessToken" |
Token 类型标识符(建议使用 TokenTypes 常量类) |
InjectionMode |
TokenInjectionMode |
Header |
Token 注入模式 |
Name |
string? |
null |
自定义 Header/Query 名称 |
Scopes |
string? |
null |
令牌作用域,多个作用域用逗号分隔 |
TokenManagerKey |
string? |
同 TokenType |
令牌管理器查找键,默认与 TokenType 相同,用于解耦业务概念(TokenType)和技术查找键 |
Scheme |
string? |
见说明 | 令牌方案前缀;未指定时按注入模式回退(BasicAuth → "Basic",其余 → "Bearer") |
RequiresUserId |
bool |
false |
是否需要用户 ID,为 true 时通过 ICurrentUserContext 获取 |
TokenManagerKey:当指定此值时,代码生成器将使用此键而非
TokenType从IMudAppContext中查找令牌管理器。此属性用于解耦业务概念和技术查找键,例如多个不同的TokenType可以映射到同一个TokenManager。如果未指定,则使用TokenType作为查找键。
RequiresUserId:当设置为
true时,生成的代码将通过ICurrentUserContext获取当前用户 ID,并将其传递给ITokenProvider以获取用户级令牌。如果未显式指定,则根据TokenType自动推断:TokenType为"UserAccessToken"时默认为true,否则默认为false。
使用 Token 类型
TokenTypes 提供标准化标识符(Bearer、Basic、AccessToken、RefreshToken);
平台自定义类型(如 TenantAccessToken / UserAccessToken)使用字符串字面量:
using Mud.HttpUtils;
[Token(TokenTypes.AccessToken)]
public interface IApi { }
[Token("TenantAccessToken")]
public interface IFeishuApi { }
[Get("/users/{id}")]
Task<User> GetUserAsync(
[Path] int id,
[Token("UserAccessToken")] string? token = null
);
Token 注入模式
| 模式 | 值 | 说明 |
|---|---|---|
Header |
0 | 注入到 HTTP Header(默认) |
Query |
1 | 注入到 URL Query 参数 |
Path |
2 | 注入到 URL Path |
ApiKey |
3 | API Key 认证,通过 IApiKeyProvider 获取密钥注入到请求头 |
HmacSignature |
4 | HMAC 签名认证,通过 IHmacSignatureProvider 计算签名注入到请求头 |
BasicAuth |
5 | HTTP Basic 认证,将凭据编码为 Base64 注入到 Authorization 请求头 |
Cookie |
6 | 注入到 Cookie 请求头 |
// API Key 认证模式
[Token("ApiKey", InjectionMode = TokenInjectionMode.ApiKey, Name = "X-API-Key")]
public interface IApiKeyApi { }
// HMAC 签名认证模式
[Token("Hmac", InjectionMode = TokenInjectionMode.HmacSignature)]
public interface IHmacApi { }
Token Scopes
// 指定令牌作用域
[Token("UserAccessToken", Scopes = "user:read,user:write")]
public interface IScopedApi { }
// 方法级别令牌
[Get("/api/user/profile")]
[Token("UserAccessToken", Scopes = "user:read")]
Task<Profile> GetProfileAsync();
TokenManagerKey 使用
// 使用 TokenManagerKey 解耦业务概念和技术查找键
[Token(TokenType = "UserAccessToken", TokenManagerKey = "FeishuUser")]
public interface IFeishuUserApi { }
// 多个不同的 TokenType 映射到同一个 TokenManager
[Token(TokenType = "UserAccessToken", TokenManagerKey = "FeishuUser")]
public interface IFeishuContactApi { }
RequiresUserId 使用
// 显式指定需要用户 ID
[Token(TokenType = "CustomToken", RequiresUserId = true)]
public interface ICustomUserApi { }
// 方法级别覆盖接口的 RequiresUserId
[Get("/api/public-data")]
[Token(RequiresUserId = false)]
Task<PublicData> GetPublicDataAsync();
CacheAttribute 详解
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
DurationSeconds |
int |
300 |
缓存持续时间(秒) |
CacheKeyTemplate |
string? |
null |
缓存键模板 |
VaryByUser |
bool |
false |
是否按用户区分缓存 |
UseSlidingExpiration |
bool |
false |
✅ 已支持:下沉为 CacheOptions.UseSlidingExpiration,生成代码生效 |
CFG-D04 修正:
UseSlidingExpiration受生成器支持,请勿标记为未生效。CFG-27:
Priority属性(及CachePriority枚举)已移除 —— 生成器从未处理该属性,运行时无消费点; 随之移除诊断HTTPCLIENT019([Cache]当前已无被忽略的属性)。
[Get("/users/{id}")]
[Cache(60, VaryByUser = true)]
Task<User> GetUserAsync([Path] int id);
[Get("/config")]
[Cache(300, CacheKeyTemplate = "config:{0}", UseSlidingExpiration = true)]
Task<Config> GetConfigAsync();
HeaderAttribute 详解
HeaderAttribute 支持应用到参数、方法或接口级别:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
Name |
string? |
null |
请求头名称 |
Value |
object? |
null |
请求头值(方法/接口级别使用) |
AliasAs |
string? |
null |
别名,用于映射到不同的请求头名称 |
Replace |
bool |
false |
是否替换已有的同名请求头 |
FormatString |
string? |
null |
请求头值格式化字符串(如 "N"、"yyyy-MM-ddTHH:mm:ssZ"),支持 string.Format 或 IFormattable |
Format |
string? |
null |
FormatString 的别名 |
// 参数级别
[Get("/api/users")]
Task<List<User>> GetUsersAsync([Header("X-API-Key")] string apiKey);
// 方法级别(添加固定请求头)
[Get("/api/users")]
[Header("Accept", "application/json")]
[Header("X-Request-Source", "Web")]
Task<List<User>> GetUsersAsync();
// 接口级别(所有方法自动携带)
[HttpClientApi]
[Header("X-API-Version", "v2")]
public interface IUserApi { }
FormAttribute 详解
用于 application/x-www-form-urlencoded 请求,标记参数作为表单字段:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
FieldName |
string? |
null |
表单字段名称,未设置时使用参数名 |
[Post("/api/login")]
Task<LoginResult> LoginAsync(
[Form(FieldName = "username")] string user,
[Form(FieldName = "password")] string pass);
MultipartFormAttribute 详解
用于 multipart/form-data 请求,标记参数作为多部分表单字段:
[Post("/api/upload")]
Task<UploadResult> UploadFileAsync(
[MultipartForm] IFormFile file,
[MultipartForm] string description);
UploadAttribute 详解
专用于文件上传场景,支持自定义字段名、文件名和内容类型:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
FieldName |
string? |
null |
表单字段名称,未设置时使用参数名 |
FileName |
string? |
null |
上传的文件名,未设置时使用原始文件名 |
ContentType |
string? |
null |
文件内容类型(MIME),未设置时自动检测 |
// 基本文件上传
[Post("/api/upload")]
Task<UploadResult> UploadAsync([Upload] IFormFile file);
// 自定义字段名和文件名
[Post("/api/upload")]
Task<UploadResult> UploadDocumentAsync(
[Upload(FieldName = "document", FileName = "report.pdf")] IFormFile file);
// 指定内容类型
[Post("/api/upload")]
Task<UploadResult> UploadImageAsync(
[Upload(ContentType = "image/png")] IFormFile image);
FilePathAttribute 详解
用于标记文件路径参数,既可用于上传(将文件内容作为请求体/表单数据发送),也可用于下载(将响应内容写入本地文件)。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
BufferSize |
int |
81920 |
读/写文件时的缓冲区大小(字节),默认 80KB |
Overwrite |
bool |
true |
下载时是否覆盖已存在的文件;设为 false 时若文件已存在将抛出 IOException |
// 上传文件
[Post("/api/upload")]
Task<UploadResult> UploadFileAsync([FilePath] string filePath);
// 自定义缓冲区大小(128KB)
[Post("/api/upload-large")]
Task<UploadResult> UploadLargeFileAsync([FilePath(BufferSize = 131072)] string filePath);
// 下载文件,不覆盖已存在文件
[Get("/api/files/{id}")]
Task DownloadAsync(int id, [FilePath(Overwrite = false)] string savePath);
// 下载文件,带进度报告(通过方法签名中的 IProgress<T> 参数接收)
[Get("/api/files/{id}")]
Task DownloadWithProgressAsync(int id, [FilePath] string savePath, IProgress<long> progress);
SensitiveDataAttribute 详解
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
MaskMode |
SensitiveDataMaskMode |
Mask |
脱敏模式 |
PrefixLength |
int |
2 |
前缀保留长度(Mask 模式) |
SuffixLength |
int |
2 |
后缀保留长度(Mask 模式) |
作用目标(CFG-11):
[SensitiveData]仅对对象属性生效 ——DefaultSensitiveDataMasker通过反射遍历Type.GetProperties()读取该特性;AotSafeSensitiveDataMasker则由编译期注册驱动,完全忽略该特性。AttributeUsage已收窄为仅Property:标注在方法参数上会产生编译错误CS0592(此前允许标注但不会产生任何掩码效果,属静默失效)。 如需对请求体/参数脱敏,请在请求 DTO 的属性上标注,或实现自定义ISensitiveDataMasker。
脱敏模式说明:
| 模式 | 说明 | 示例 |
|---|---|---|
Hide |
完全隐藏 | "***" |
Mask |
部分遮盖 | "张***01" |
TypeOnly |
仅显示类型 | "[String]" |
public class UserRequest
{
public string Name { get; set; }
[SensitiveData(MaskMode = SensitiveDataMaskMode.Mask, PrefixLength = 3, SuffixLength = 4)]
public string IdCard { get; set; }
[SensitiveData(MaskMode = SensitiveDataMaskMode.Hide)]
public string Password { get; set; }
}
BasePathAttribute 详解
支持在接口级别定义统一的路径前缀,避免每个方法重复书写相同的路径段:
| 属性 | 类型 | 说明 |
|---|---|---|
Path |
string |
基础路径前缀,可包含占位符(如 {tenantId}) |
URL 构建规则:
| 情况 | 实际路径 |
|---|---|
| 正常 | [Base Address] + [Base Path] + [Method Path] |
Method Path 以 / 开头 |
[Base Address] + [Method Path](忽略 Base Path) |
| Method Path 是绝对 URL | [Method Path](忽略 Base Address 和 Base Path) |
[HttpClientApi(HttpClient = "IEnhancedHttpClient")]
[BasePath("api/v1")]
public interface IUserApi
{
[Get("users/{id}")] // 实际路径: /api/v1/users/{id}
Task<User> GetUserAsync([Path] int id);
[Get("/admin/users")] // 以 / 开头,忽略 BasePath,实际路径: /admin/users
Task<List<User>> GetAllUsersAsync();
}
// 带占位符的 Base Path
[HttpClientApi(HttpClient = "IEnhancedHttpClient")]
[BasePath("{tenantId}/api/v1")]
public interface ITenantApi
{
[Path("tenantId")]
string TenantId { get; set; }
[Get("users")]
Task<List<User>> GetUsersAsync();
}
QueryMapAttribute 详解
将对象属性或字典键值对展开为 URL 查询参数,适用于动态查询条件场景:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
PropertySeparator |
string |
"_" |
嵌套属性名称分隔符 |
SerializationMethod |
QuerySerializationMethod |
ToString |
序列化方法(ToString / Json) |
UrlEncode |
bool |
true |
是否对查询参数值进行 URL 编码 |
IncludeNullValues |
bool |
false |
是否包含值为 null 的属性 |
// POCO 对象展开
public class SearchCriteria
{
public string? Keyword { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
}
[Get("/api/search")]
Task<SearchResult> SearchAsync([QueryMap] SearchCriteria criteria);
// 调用: api.SearchAsync(new SearchCriteria { Keyword = "test", Page = 1, PageSize = 10 });
// 生成: /api/search?Keyword=test&Page=1&PageSize=10
// 字典类型
[Get("/api/search")]
Task<SearchResult> SearchAsync([QueryMap] IDictionary<string, object> filters);
// 自定义序列化
[Get("/api/search")]
Task<SearchResult> SearchAsync(
[QueryMap(PropertySeparator = ".", SerializationMethod = QuerySerializationMethod.Json)]
SearchCriteria criteria);
QuerySerializationMethod 枚举
| 值 | 说明 |
|---|---|
ToString |
调用 ToString() 方法转换为字符串(默认) |
Json |
使用 JSON 序列化器序列化为 JSON 字符串 |
RawQueryStringAttribute 详解
直接传递原始查询字符串,不做任何编码或处理:
G8-13 修正:
RawQueryStringAttribute是空标记特性(无任何属性)。 原文档记录的PrependQuestionMark从未存在于该类型上;?的补齐由生成代码统一负责 (__url += (__url.Contains("?") ? "&" : "?") + …),无需也不可由特性控制。
[Get("/api/search")]
Task<SearchResult> SearchAsync([RawQueryString] string queryString);
// 调用: api.SearchAsync("keyword=test&page=1");
// 生成: /api/search?keyword=test&page=1
接口级动态属性
PathAttribute、QueryAttribute 和 HeaderAttribute 现在支持应用到接口属性(AttributeTargets.Property),用于定义全局参数(接口级 Header 属性支持 Replace、FormatString、AliasAs 参数):
[HttpClientApi(HttpClient = "IEnhancedHttpClient")]
[BasePath("{tenantId}/api/v1")]
public interface ITenantApi
{
[Path("tenantId")]
string TenantId { get; set; }
[Query("apiKey")]
string ApiKey { get; set; }
[Query("locale")]
string? Locale { get; set; }
[Get("users")]
Task<List<User>> GetUsersAsync();
[Get("users/{id}")]
Task<User> GetUserAsync([Path] int id);
}
// 使用
var api = serviceProvider.GetRequiredService<ITenantApi>();
api.TenantId = "tenant-123";
api.ApiKey = "my-api-key";
api.Locale = "zh-CN";
await api.GetUsersAsync();
// 实际请求: /tenant-123/api/v1/users?apiKey=my-api-key&locale=zh-CN
优先级:方法参数优先级高于接口属性。如果方法参数与接口属性同名,方法参数值会覆盖接口属性值。
接口级 Header 属性:
[Header]可标记在接口属性上,作为所有方法的动态请求头。支持Replace(替换同名请求头)与FormatString(格式化值,如 GUID 的"N");当HeaderMergeMode为Ignore时该属性 Header 被跳过,Replace时先移除同名头再添加;若 Header 名为Authorization且存在 TokenManager,则该属性 Header 由 Token 注入机制处理而被跳过。属性级 Header 在方法参数 Header 之后、接口级静态 Header 之前生成(动态优先于静态)。
[HttpClientApi(HttpClient = "IEnhancedHttpClient")]
[BasePath("{tenantId}/api/v1")]
public interface ITenantApi
{
[Path("tenantId")] string TenantId { get; set; }
[Query("apiKey")] string ApiKey { get; set; }
// 接口级 Header 动态属性
[Header("X-App-Version")] string AppVersion { get; set; }
[Header("X-Trace-Id", FormatString = "N")] Guid TraceId { get; set; }
[Get("users")]
Task<List<User>> GetUsersAsync();
}
RetryAttribute 详解
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
MaxRetries |
int |
3 |
最大重试次数 |
DelayMilliseconds |
int |
1000 |
基础延迟时间(毫秒) |
UseExponentialBackoff |
bool |
true |
是否使用指数退避 |
[Get("/api/data")]
[Retry(MaxRetries = 5, DelayMilliseconds = 2000, UseExponentialBackoff = true)]
Task<Data> GetDataAsync();
TimeoutAttribute 详解
| 属性 | 类型 | 说明 |
|---|---|---|
TimeoutMilliseconds |
int |
超时时间(毫秒) |
[Get("/api/slow")]
[Timeout(60000)] // 60 秒超时
Task<Data> GetSlowDataAsync();
CircuitBreakerAttribute 详解
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
FailureThreshold |
int |
5 |
失败阈值(简单模式为连续失败次数,高级模式为失败率百分比) |
BreakDurationSeconds |
int |
30 |
熔断持续时间(秒) |
SamplingDurationSeconds |
int |
0 |
采样窗口时间(秒),大于 0 时启用高级熔断策略 |
MinimumThroughput |
int |
10 |
采样窗口内最小请求数(仅高级模式生效) |
[Get("/api/unstable")]
[CircuitBreaker(FailureThreshold = 5, BreakDurationSeconds = 30)]
Task<Data> GetUnstableDataAsync();
HeaderMergeAttribute 详解
控制接口级与方法级同名 HTTP 头部的合并策略:
| 模式 | 说明 |
|---|---|
Append |
追加模式:接口级和方法级的同名头部都会被添加(默认) |
Replace |
替换模式:方法级头部替换接口级同名头部 |
Ignore |
忽略模式:方法级头部被忽略,只使用接口级头部 |
[HttpClientApi]
[Header("Accept", "application/json")]
[HeaderMerge(HeaderMergeMode.Replace)]
public interface IUserApi
{
[Get("/api/users")]
[Header("Accept", "text/plain")]
Task<string> GetUsersAsTextAsync();
}
SerializationMethodAttribute 详解
指定接口或方法级别的请求体序列化方式,方法级优先于接口级:
| 值 | 说明 |
|---|---|
Json |
使用 JSON 序列化(默认) |
Xml |
使用 XML 序列化 |
FormUrlEncoded |
使用表单 URL 编码序列化 |
[HttpClientApi]
[SerializationMethod(SerializationMethod.Xml)]
public interface IXmlApi
{
[Post("/api/data")]
Task SendDataAsync([Body] DataModel data); // XML 序列化
[Post("/api/json-data")]
[SerializationMethod(SerializationMethod.Json)]
Task SendJsonDataAsync([Body] DataModel data); // 方法级覆盖为 JSON
}
InterfacePathAttribute 详解
在接口级别添加固定路径参数,自动替换 URL 模板中的占位符:
| 属性 | 类型 | 说明 |
|---|---|---|
Name |
string |
路径参数名(占位符名称) |
Value |
string? |
路径参数值 |
[HttpClientApi]
[InterfacePath("tenantId", "default-tenant")]
public interface IUserApi
{
[Get("/api/tenants/{tenantId}/users/{userId}")]
Task<User> GetUserAsync(int userId);
// 实际请求: /api/tenants/default-tenant/users/123
}
InterfaceQueryAttribute 详解
在接口级别添加固定查询参数,自动附加到所有方法的 URL:
| 属性 | 类型 | 说明 |
|---|---|---|
Name |
string |
查询参数名 |
Value |
string? |
查询参数值 |
[HttpClientApi]
[InterfaceQuery("api_version", "2.0")]
[InterfaceQuery("client_id", "my-app")]
public interface IUserApi
{
[Get("/api/users/{id}")]
Task<User> GetUserAsync(int id);
// 实际请求: /api/users/1?api_version=2.0&client_id=my-app
}
GenerateEventHandlerAttribute 详解
用于标记类,指示源代码生成器为该类生成事件处理器实现(常用于 Webhook / 事件订阅场景,如飞书事件订阅)。提供两个构造函数:GenerateEventHandler() 与 GenerateEventHandler(string? eventType)。
| 属性 | 类型 | 说明 |
|---|---|---|
EventType |
string? |
事件类型标识符,对应构造函数参数 eventType |
HandlerClassName |
string? |
生成的处理器类名称(默认按约定生成) |
HandlerNamespace |
string? |
生成的处理器类所在命名空间 |
InheritedFrom |
string? |
生成的处理器类继承的基类名称 |
ConstructorParameters |
string? |
构造函数参数字符串,用于生成构造函数签名(如 "ILogger logger, IEmailService email") |
ConstructorBaseCall |
string? |
构造函数基类调用字符串,用于生成 base(...) 调用(如 "logger") |
HeaderType |
string? |
反序列化事件请求头所用的请求头类型 |
[GenerateEventHandler(
EventType = "UserCreatedEvent",
HandlerClassName = "UserCreatedEventHandler",
HandlerNamespace = "MyApp.Handlers",
InheritedFrom = "BaseEventHandler",
ConstructorParameters = "ILogger logger, IEmailService emailService",
ConstructorBaseCall = "logger"
)]
public class UserCreatedEvent
{
public string UserId { get; set; }
public string UserName { get; set; }
}
HttpJsonSerializableAttribute 详解(Native AOT 支持)
标注在需纳入 JSON 源生成的实体/DTO 上(支持 class / struct / record),用于 Native AOT 场景。Scaffolder 会按此特性聚合生成 JsonSerializerContext 源文件,使 STJ 源生成在 AOT 下获得类型元数据。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
SerializerClassName |
string? |
null(自动派生) |
生成的 Context 类名(不含 JsonContext 后缀);同名实体合并进同一 Context |
NamingPolicy |
JsonNamingPolicyHint |
Default(自动推导) |
JSON 命名策略:Default(按 [JsonPropertyName] 模式自动推导)/ CamelCase / SnakeCaseLower / SnakeCaseUpper / KebabCaseLower / KebabCaseUpper |
[HttpJsonSerializable(SerializerClassName = "FeishuAI", NamingPolicy = JsonNamingPolicyHint.SnakeCaseLower)]
public class ContractFileUploadRequest { ... }
工作流:标注实体 → 运行
Mud.HttpUtils.JsonContextScaffolder脚手架(dotnet mud-jsonctx --project <你的.csproj>)自动生成XxxJsonContext.g.cs→ 在 .NET 8+ 启动注册services.AddMudHttpClientJsonContext(FeishuAIJsonContext.Default)。HttpContentSerializerFactory.BuildOptions会自动合并消费方 resolver 与库内置MudHttpJsonContext.Default。编译期保障:若类型标注了
[HttpJsonSerializable]却未被任何JsonSerializerContext覆盖,源生成器会发出AOT006编译诊断(可由Mud.HttpUtils.CodeFixes的AotJsonContextCodeFixProvider一键修复);[HttpClientApi]接口的闭合泛型返回/Body 类型未被覆盖则发出AOT004/AOT005。CI 严格模式(-p:AotStrictMode=true)下这些诊断升级为 Error。
详见 Mud.HttpUtils.JsonContextScaffolder 工具文档 与 Mud.HttpUtils.Generator 文档 的 AOT 诊断章节。
多态序列化与 [JsonDerivedType](P0-2)
当 [HttpClientApi] 接口方法的响应类型声明为基类,且基类标注了 [JsonDerivedType] 参与多态序列化时,所有声明的派生类型也必须被 JsonSerializerContext 覆盖。否则 AOT 下反序列化派生实例会抛 NotSupportedException。
// 正确:Dog 已被 Context 覆盖
[JsonDerivedType(typeof(Dog))]
public class Animal { public string Name { get; set; } }
public class Dog : Animal { public string Breed { get; set; } }
// AOT004 会报:Dog 未被 Context 覆盖
未标注
[JsonDerivedType]的非 sealed 类不会被 STJ 按多态处理,无需额外检查。
AOT 安全脱敏(P0-3)
AotSafeSensitiveDataMasker 要求显式 Register<T>() 注册脱敏规则。当类型未注册、但其某个基类已注册时,不会套用基类规则(基类规则看不到派生类新增的敏感字段,套用会导致明文输出),两种取值都只输出类型占位串:
enableBaseTypeFallback |
回退输出 | 适用场景 |
|---|---|---|
false(默认) |
[TypeName] |
不需要区分"基类已注册"这一线索 |
true |
[TypeName, BaseType=BaseTypeName] |
便于定位"注册了基类、忘了派生类"的遗漏 |
两者都会发出一次性告警(注入了 ILogger 走日志,否则回退 Console.Error),且都不输出任何字段值。请为每个需要脱敏的派生类型显式调用 Register<T>()。
内置 MudHttpJsonContext 的 Dictionary<string, object> 处置(P1-6)
库内置兜底 Context MudHttpJsonContext 不再注册 Dictionary<string, object>:源生成的 typeof(object) 元数据在 Native AOT 下对非基元运行时值会抛 NotSupportedException,属"看起来能编译、上线才崩"的潜伏雷。库内无该注册的调用方;Dictionary<string, string> 注册保留。
消费方若确需以 object 为值的字典参与 JSON 序列化,请在自己的 JsonSerializerContext 上挂 ObjectToInferredTypesConverter:
[JsonSourceGenerationOptions(Converters = [typeof(ObjectToInferredTypesConverter)])]
[JsonSerializable(typeof(Dictionary<string, object>))]
internal partial class AppJsonContext : JsonSerializerContext;
非泛型
IEncryptableHttpClient.EncryptContent(object, ...)内部会构造Dictionary<string, object>,该重载已标注[Obsolete]+[RequiresUnreferencedCode]/[RequiresDynamicCode],AOT 场景请改用泛型重载EncryptContent<T>。
QueryAttribute 详解
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
Name |
string? |
null |
查询参数名称 |
FormatString |
string? |
null |
格式化字符串(如日期格式 "yyyy-MM-dd") |
Format |
string? |
null |
FormatString 的别名 |
AliasAs |
string? |
null |
别名,用于映射到不同的查询参数名 |
Separator |
string? |
null |
数组元素分隔符。设置后数组序列化为单个参数(如 ?ids=1;2;3);为 null 则多个同名参数 |
// 基本用法
[Get("/api/users")]
Task<List<User>> GetUsersAsync([Query] string? keyword, [Query] int page = 1);
// 自定义参数名和格式
[Get("/api/users")]
Task<List<User>> GetUsersAsync([Query("page_size")] int pageSize);
// 数组分隔符
[Get("/api/users")]
Task<List<User>> GetUsersAsync([Query(Separator = ",")] int[] ids);
// 生成: /api/users?ids=1,2,3
// 方法级别添加固定查询参数
[Get("/api/users")]
[Query("status", "active")]
Task<List<User>> GetActiveUsersAsync();
设计原则
- 轻量级:仅依赖 Abstractions,无其他传递依赖
- netstandard2.0 兼容性:确保在尽可能多的项目中可用
- 特性属性类型均为基础类型:
string、int、bool、enum,无复杂依赖 - 与生成器解耦:特性可在不引用生成器的项目中使用,便于接口定义共享
- 安全优先:内置
SensitiveDataAttribute支持敏感数据脱敏,CacheAttribute支持缓存控制 - 弹性策略:内置
RetryAttribute、TimeoutAttribute、CircuitBreakerAttribute支持方法级弹性策略配置 - 灵活控制:
HeaderMergeAttribute、SerializationMethodAttribute、InterfacePathAttribute、InterfaceQueryAttribute提供精细化的请求控制
| 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
- Mud.HttpUtils.Abstractions (>= 2.0.8)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Mud.HttpUtils.Attributes:
| Package | Downloads |
|---|---|
|
Mud.HttpUtils
Mud HttpUtils 元包,自动引用 Abstractions、Attributes、Client 和 Resilience 子模块;Native AOT 与裁剪兼容(System.Text.Json)。 |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 2.0.8 | 46 | 9/22/2026 | |
| 2.0.7 | 289 | 9/18/2026 | |
| 2.0.6 | 143 | 9/17/2026 | |
| 2.0.5 | 109 | 9/17/2026 | |
| 2.0.4 | 148 | 9/16/2026 | |
| 2.0.2 | 238 | 7/14/2026 | |
| 2.0.1 | 176 | 7/13/2026 | |
| 2.0.0 | 566 | 7/11/2026 | |
| 2.0.0-rc5 | 186 | 7/8/2026 | |
| 2.0.0-rc4 | 189 | 7/7/2026 | |
| 2.0.0-rc3 | 244 | 7/3/2026 | |
| 2.0.0-rc2 | 1,027 | 5/13/2026 | |
| 2.0.0-rc1 | 527 | 5/9/2026 | |
| 2.0.0-preview6 | 218 | 5/6/2026 | |
| 2.0.0-preview5 | 188 | 5/3/2026 | |
| 2.0.0-preview4 | 228 | 4/30/2026 | |
| 2.0.0-preview3 | 477 | 4/29/2026 | |
| 2.0.0-preview2 | 202 | 4/28/2026 | |
| 2.0.0-preview1 | 173 | 4/27/2026 |