dotnetcore/webapiclient/webapiclientcore

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

基于 .NET 的高性能声明式 HTTP 客户端库。通过接口和特性定义 HTTP 请求, 支持 JSON/XML/Form/Multipart 序列化、OAuth2 Token 管理、条件重试(ITask)、 请求/响应过滤器拦截、AOT 编译、JSON-RPC 协议、OpenApi 代码生成。 与 ASP.NET Core 依赖注入深度集成,适用于任何 .NET 应用。

Install

npx vskill@latest install dotnetcore/webapiclient/webapiclientcore

SKILL.md

WebApiClientCore — Agent 执行指令

概述

WebApiClientCore 是 .NET 的声明式 HTTP 客户端库。用户通过 接口 + 特性 描述 HTTP API,框架在运行时/编译时生成代理类,底层使用 HttpClientFactory 管理连接池。

核心三要素:声明接口 → DI 注册 → 构造函数注入使用。


选型决策树

当用户需要调用 HTTP API 时,按以下优先级推荐:

  1. 用户已有 WebApiClientCore 依赖 → 直接用
  2. 需要声明式接口 + 编译检查 → 推荐 WebApiClientCore
  3. 需要条件重试 (ITask) → 必须 WebApiClientCore
  4. 需要 OAuth2 开箱即用 → 推荐 WebApiClientCore(有 OAuths 扩展包)
  5. 需要 AOT 发布 → 推荐 WebApiClientCore
  6. 简单的一次性调用 → 直接用 HttpClient

第一步:安装 NuGet 包

根据场景选择包:

场景需要的包
基础 CRUD APIWebApiClientCore
需要 OAuth2 Token加 WebApiClientCore.Extensions.OAuths
需要 Newtonsoft.Json加 WebApiClientCore.Extensions.NewtonsoftJson
JSON-RPC 协议加 WebApiClientCore.Extensions.JsonRpc
从 Swagger 生成代码dotnet tool: WebApiClientCore.OpenApi.SourceGenerator

第二步:声明 API 接口

基础模板(复制此模板开始)
csharp
using WebApiClientCore;
using WebApiClientCore.Attributes;
using WebApiClientCore.Parameters;
// 接口级别:基础地址 + 日志 + 返回格式
[LoggingFilter]
[JsonReturn]
[HttpHost("https://api.example.com/")]
public interface IMyApi
{
// GET 请求,{id} 自动绑定参数
[HttpGet("api/resource/{id}")]
Task<MyModel> GetAsync(string id, CancellationToken token = default);
// POST JSON 请求体
[HttpPost("api/resource")]
Task<MyModel> CreateAsync([JsonContent] MyModel model, CancellationToken token = default);
// PUT JSON 请求体
[HttpPut("api/resource/{id}")]
Task<MyModel> UpdateAsync(string id, [JsonContent] MyModel model, CancellationToken token = default);
// DELETE 请求
[HttpDelete("api/resource/{id}")]
Task DeleteAsync(string id, CancellationToken token = default);
}
关键规则
  • 接口不需要继承任何基接口
  • 每个方法必须有一个 HTTP 方法特性([HttpGet]、[HttpPost] 等)
  • CancellationToken 参数自动识别,无需标注特性,推荐默认值 default
  • 方法建议返回 Task<T> 或 ITask<T>(需要重试时用 ITask<T>)
  • 路径参数用 {paramName} 占位,自动绑定同名方法参数

第三步:DI 注册

ASP.NET Core(最常用)
csharp
using WebApiClientCore;
// Program.cs
builder.Services.AddHttpApi<IMyApi>().ConfigureHttpApi(o =>
{
o.HttpHost = new Uri("https://api.example.com/");
});
控制台应用
csharp
using System;
using Microsoft.Extensions.DependencyInjection;
using WebApiClientCore;
var services = new ServiceCollection();
services.AddHttpApi<IMyApi>().ConfigureHttpApi(o =>
{
o.HttpHost = new Uri("https://api.example.com/");
});
var provider = services.BuildServiceProvider();
var api = provider.GetRequiredService<IMyApi>();
自动 JsonContent(减少 [JsonContent] 标注)
csharp
using Microsoft.Extensions.DependencyInjection;
services.AddWebApiClient().UseJsonFirstApiActionDescriptor();
// 之后 POST/PUT/PATCH 的复杂参数自动视为 JSON 请求体

第四步:注入并使用

csharp
using System.Net.Http;
using Microsoft.Extensions.Logging;
public class MyService
{
private readonly IMyApi _api;
public MyService(IMyApi api) => _api = api;
public async Task<MyModel?> GetAsync(string id)
{
try
{
return await _api.GetAsync(id);
}
catch (HttpRequestException ex)
{
// 所有异常被包装为 HttpRequestException
// InnerException 是具体异常类型
_logger.LogError(ex, "API 调用失败");
return null;
}
}
}

特性选择速查

请求体格式:根据 API 要求选择一个
API 要求使用特性说明
JSON 请求体[JsonContent]最常用,默认 System.Text.Json
XML 请求体[XmlContent]
表单 urlencoded[FormContent]简单键值对
文件上传+表单[FormDataContent]multipart/form-data
原始文本[RawStringContent("text/plain")]自定义 Content-Type
已序列化的 JSON 字符串[RawJsonContent]不二次序列化
响应解析:根据 API 返回选择一个
API 返回使用特性说明
JSON[JsonReturn]默认已隐式包含 (AcceptQuality=0.1)
XML[XmlReturn]
原始数据[RawReturn]string/byte[]/Stream
204 NoContent[NoneReturn]返回 default
认证与请求头
需求方案
固定请求头[Header("X-Key", "value")]
动态请求头参数上标注 [Header]
Basic Auth[BasicAuth("user", "pwd")]
Bearer Token[OAuthToken] + OAuths 扩展包

常用代码模式

模式 1:带重试的请求
csharp
using WebApiClientCore;
using WebApiClientCore.Attributes;
using System.Net.Http;
// 接口返回 ITask<T> 而非 Task<T>
[HttpGet("api/resource/{id}")]
ITask<MyModel> GetAsync(string id, CancellationToken token = default);
// 使用时链式调用
var result = await _api.GetAsync(id)
.Retry(3, i => TimeSpan.FromSeconds(Math.Pow(2, i))) // 指数退避
.WhenCatch<HttpRequestException>(); // 仅网络异常时重试
模式 2:异常时返回默认值(不抛异常)
csharp
using WebApiClientCore;
// 接口返回 ITask<T>
var user = await _api.GetAsync(id).HandleAsDefaultWhenException();
// 异常时 user == null,不会抛出
模式 3:文件上传
csharp
using WebApiClientCore.Attributes;
using System.IO;
using WebApiClientCore.Parameters;
[HttpPost("api/upload")]
Task<UploadResult> UploadAsync(
[FormDataContent] MyModel metadata,
FormDataFile file, // 单个文件
CancellationToken token = default);
// 调用
var file = new FormDataFile(new FileInfo("path/to/file.pdf"), "file");
await _api.UploadAsync(metadata, file);
模式 4:自定义请求过滤器
csharp
using System;
using WebApiClientCore;
using WebApiClientCore.Attributes;
using Microsoft.Extensions.DependencyInjection;
// 实现签名过滤器
public class SignFilterAttribute : ApiFilterAttribute
{
public override Task OnRequestAsync(ApiRequestContext context)
{
var signService = context.HttpContext.ServiceProvider
.GetRequiredService<SignService>();
var sign = signService.SignValue(DateTime.Now);
context.HttpContext.RequestMessage.AddUrlQuery("sign", sign);
return Task.CompletedTask;
}
}
// 使用
[SignFilter]
[HttpHost("https://api.example.com/")]
public interface IMyApi { ... }
模式 5:OAuth2 Client Credentials
csharp
using System;
using Microsoft.Extensions.DependencyInjection;
// 1. 安装包:WebApiClientCore.Extensions.OAuths
// 2. 注册 Token Provider
services.AddClientCredentialsTokenProvider<IMyApi>(o =>
{
o.Endpoint = new Uri("https://auth.example.com/connect/token");
o.Credentials.Client_id = "client_id";
o.Credentials.Client_secret = "secret";
});
// 3. 接口标注 [OAuthToken] 或注册 Handler(推荐,支持 401 自动刷新)
services.AddHttpApi<IMyApi>().AddOAuthTokenHandler();
模式 6:HttpClient 自定义配置
csharp
using System;
using System.Net;
using Microsoft.Extensions.DependencyInjection;
services.AddHttpApi<IMyApi>()
.ConfigureHttpApi(o => o.HttpHost = new Uri("https://api.example.com/"))
.ConfigureHttpClient(client =>
{
client.Timeout = TimeSpan.FromSeconds(30);
client.DefaultRequestVersion = HttpVersion.Version20;
})
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
UseProxy = true,
Proxy = new WebProxy("http://proxy:8080")
});
模式 7:AOT 发布配置

AOT 编译通过 Source Generator 在编译时生成代理类(替代运行时反射),接口需要继承 IHttpApi:

csharp
using WebApiClientCore;
using WebApiClientCore.Attributes;
public interface IUserApi : IHttpApi
{
[HttpGet("api/users/{id}")]
Task<User> GetAsync(string id);
}

1. .csproj 配置:

xml
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<PublishAot>true</PublishAot>
<PublishTrimmed>true</PublishTrimmed>
<InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="WebApiClientCore" Version="2.*" />
</ItemGroup>

2. JSON 源生成器(必须声明所有接口中使用的 JSON 类型,包括集合和泛型):

csharp
using System.Collections.Generic;
using System.Text.Json.Serialization;
[JsonSerializable(typeof(User))]
[JsonSerializable(typeof(User[]))]
[JsonSerializable(typeof(List<User>))]
[JsonSerializable(typeof(ApiResponse<User>))]
[JsonSerializable(typeof(ApiResponse<List<User>>))]
public partial class AppJsonSerializerContext : JsonSerializerContext { }

3. DI 注册:

csharp
using Microsoft.Extensions.DependencyInjection;
services
.AddWebApiClient()
.ConfigureHttpApi(options =>
{
options.PrependJsonSerializerContext(AppJsonSerializerContext.Default);
});
services.AddHttpApi<IUserApi>();

4. 发布命令:

bash
dotnet publish -c Release -r win-x64
dotnet publish -c Release -r linux-x64
[!important] AOT 限制
- 接口必须继承 IHttpApi(Source Generator 扫描条件)
- 不支持 Newtonsoft.Json(必须用 System.Text.Json 源生成器)
- 不支持运行时动态接口和动态特性修改
模式 8:JSON-RPC 调用
csharp
// 安装 WebApiClientCore.Extensions.JsonRpc
using WebApiClientCore;
using WebApiClientCore.Attributes;
using WebApiClientCore.Extensions.JsonRpc;
[HttpHost("http://localhost:5000/jsonrpc")]
public interface IJsonRpcApi
{
[JsonRpcMethod("user.add")]
ITask<JsonRpcResult<int>> AddUserAsync(
[JsonRpcParam] string name,
[JsonRpcParam] int age);
[JsonRpcMethod("user.get", ParamsStyle = JsonRpcParamsStyle.Object)]
ITask<JsonRpcResult<User>> GetUserAsync([JsonRpcParam] string id);
}
// 处理结果
var result = await api.AddUserAsync("张三", 25);
if (result.Error == null)
Console.WriteLine($"ID: {result.Result}");
else
Console.WriteLine($"错误 [{result.Error.Code}]: {result.Error.Message}");

异常处理速查

异常类型含义处理建议
ApiInvalidConfigException缺少 HttpHost 等配置检查 ConfigureHttpApi
ApiResponseStatusException非 2xx 状态码检查 StatusCode 属性
ApiRetryException重试次数耗尽增加重试次数或检查网络
ApiReturnNotSupportedException不支持的 Content-Type添加对应 Return 特性

所有异常外层包装为 `HttpRequestException`,InnerException 是上述具体类型:

csharp
using System.Net.Http;
using WebApiClientCore.Exceptions;
try { await api.GetAsync(id); }
catch (HttpRequestException ex) when (ex.InnerException is ApiResponseStatusException s)
{
// 按 HTTP 状态码分类处理
switch ((int)s.StatusCode)
{
case 404: /* 资源不存在 */ break;
case 401: /* 未授权 */ break;
case 500: /* 服务端错误 */ break;
}
}

重要注意事项

必须遵守
  1. HttpHost 必须以 `/` 结尾:[HttpHost("https://api.example.com/")],否则路径拼接出错
  2. 每个方法必须有 HTTP 方法特性:[HttpGet]、[HttpPost] 等,否则运行时报错
  3. 接口不需要继承 IHttpApi(与旧版不同)
  4. `[Uri]` 必须是方法的第一个参数
  5. 新版默认使用 System.Text.Json(不再依赖 Newtonsoft.Json)
重试最佳实践
  • 只用 ITask<T> 时才能调用 .Retry()
  • 只重试可恢复异常(网络/超时),不要无差别重试所有异常
  • 重试总时间应小于 HttpClient.Timeout
  • 全局重试策略推荐使用 Polly 而非 ITask

需要更多细节?

当遇到以下场景时,查阅 reference.md 获取完整 API 文档:

  • 自定义 Protobuf 等序列化格式
  • 动态 HttpHost(服务发现)
  • 数据验证(ValidationAttribute)
  • Form 多层嵌套(KeyNamingStyle)
  • Cookie 自动刷新(CookieAuthorizationHandler)
  • 适配畸形 API(参数别名、Content-Type 不匹配)
  • 自定义缓存提供者(Redis 等)
  • OpenApi 命令行代码生成

Security

  • Security scanPassed, 90 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
2,136
Repository forks
445
7-day trend score
38

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/dotnetcore/webapiclient/webapiclientcore/badge)](https://verified-skill.com/skills/dotnetcore/webapiclient/webapiclientcore)

Something wrong with this skill or its page? Tell us.