Inkslab.Net 1.2.25

dotnet add package Inkslab.Net --version 1.2.25
                    
NuGet\Install-Package Inkslab.Net -Version 1.2.25
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Inkslab.Net" Version="1.2.25" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Inkslab.Net" Version="1.2.25" />
                    
Directory.Packages.props
<PackageReference Include="Inkslab.Net" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Inkslab.Net --version 1.2.25
                    
#r "nuget: Inkslab.Net, 1.2.25"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Inkslab.Net@1.2.25
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Inkslab.Net&version=1.2.25
                    
Install as a Cake Addin
#tool nuget:?package=Inkslab.Net&version=1.2.25
                    
Install as a Cake Tool

Inkslab

Inkslab.Net 是什么?

Inkslab.Net 是一个可链式调用的 HTTP 请求客户端,基于 HttpClient 实现,提供:

  • 流式 API:URL → 参数 → 头 → 内容 → 转换 → 校验 → 发送。
  • 统一序列化Json / Xml / Form / Body,配合 JsonCast<T> / XmlCast<T> / CustomCast<T> 反序列化。
  • 失败重试与认证刷新When(...).ThenAsync(...) 条件重试链。
  • 数据验证DataVerify(...).Success(...).Fail(...) 语义化结果处理。
  • 流下载DownloadAsync
  • 请求初始化钩子IRequestInitialize(全局头、鉴权注入等)。

安装

dotnet add package Inkslab.Net

Inkslab.Net 通过 DI 暴露 IRequestFactory,推荐与 Inkslab.DI 或 ASP.NET Core 自带的 IServiceCollection 一起使用。


快速入门

1. 获取请求器

public class MyService
{
    private readonly IRequestFactory _factory;
    public MyService(IRequestFactory factory) => _factory = factory;

    public Task<string> GetAsync() =>
        _factory.CreateRequestable("https://api.example.com/users")
                .AppendQueryString("page", 1)
                .AppendQueryString("size", 20)
                .GetAsync();
}

2. 查询参数

.AppendQueryString("?keyword=test&page=1")
.AppendQueryString("name", "tom")
.AppendQueryString("time", DateTime.UtcNow, "yyyy-MM-ddTHH:mm:ssZ")
.AppendQueryString(new { PageIndex = 1, PageSize = 20 }, NamingType.KebabCase)

说明:多次调用同一参数名 → 追加为数组;仅在命中 ThenAsync 认证重试时覆盖。

3. 请求头

.AssignHeader("Authorization", "Bearer <token>")
.AssignHeaders(new Dictionary<string, string>
{
    ["X-Trace-Id"] = traceId,
    ["X-Tenant"]   = tenantId
});

核心契约

IRequestFactory src/Inkslab.Net/IRequestFactory.cs

public interface IRequestFactory
{
    IRequestable CreateRequestable(string requestUri);
}

链式接口族

接口 作用 源文件
IRequestableBase<T> 请求头、Query 参数 IRequestableBase.cs
IRequestable 基础请求器(编码、内容设置) IRequestable.cs
IRequestableEncoding Body 编码(JSON/XML/Form/Body) IRequestableContent.cs
IRequestableContent 已设置内容后的请求器 IRequestableContent.cs
IDeserializeRequestable JsonCast / XmlCast / CustomCast IDeserializeRequestable.cs
IWhenRequestable / IThenRequestable 条件重试链 IWhenRequestable.cs
IRequestableDataVerify<T> 结果校验 → Success / Fail IRequestableDataVerify.cs
IStreamRequestable DownloadAsync 流下载 IStreamRequestable.cs
IRequestInitialize 全局初始化钩子 IRequestInitialize.cs

发送请求

每个发送方法默认 timeout = 1000ms;流下载默认 10000ms。所有方法均接受可选的 CancellationToken

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));

await r.GetAsync(cancellationToken: cts.Token);
await r.PostAsync(3000, cts.Token);            // timeout=3000ms,带取消令牌
await r.DeleteAsync();
await r.PutAsync();
await r.PatchAsync();
await r.HeadAsync();
await r.SendAsync("CONNECT");                  // 自定义 HTTP 方法

var stream = await r.DownloadAsync(30000, cts.Token);  // 流下载,默认 10000ms

请求体

方法 Content-Type 说明
.Json(obj) / .Json<T>(obj, naming) application/json 自动调用 IJsonHelper
.Xml(obj) / .Xml<T>(obj) application/xml 基于 XmlHelper
.Form(dict) application/x-www-form-urlencoded 纯键值对(IEnumerable<KV<string,string>>
.Form(multipart) multipart/form-data 传入 MultipartFormDataContent
.Form(body, NamingType) 自动检测 智能路由:值含 FileInfomultipart;否则 → form-urlencoded
.Body(str, contentType) 自定义 原始字符串内容

Form 智能路由示例(最常用的对象/字典重载):

// IEnumerable<KeyValuePair<string, object>>:值含 FileInfo 时自动 multipart
var fields = new Dictionary<string, object>
{
    ["title"] = "报告",
    ["file"]  = new FileInfo("report.pdf")   // 触发 multipart/form-data
};
await factory.CreateRequestable(url).Form(fields).PostAsync();

// 对象重载:同上,属性含 FileInfo → multipart
await factory.CreateRequestable(url)
    .Form(new { title = "报告", file = new FileInfo("report.pdf") }, NamingType.SnakeCase)
    .PostAsync();

响应反序列化

// JSON(泛型)
var dto = await r.Json(req)
                 .JsonCast<ServResult<User>>()
                 .PostAsync();

// JSON(匿名类型)
var anon = await r.JsonCast(new { Code = 0, Data = default(User) })
                  .GetAsync();

// XML(泛型)
var dto = await r.XmlCast<ServResult>()
                 .GetAsync();

// XML(匿名类型)
var anon = await r.XmlCast(new { Code = 0 })
                  .GetAsync();

// 自定义:字符串 → T(框架自动检测 HTTP 状态,非 2xx 时工厂不执行)
var dto = await r.CustomCast(body => Parse(body))
                 .GetAsync();

// 自定义:原始响应 → T(不检测 HTTP 状态,完全由工厂决定如何处理)
var dto = await r.CustomCast(async (response, ct) =>
                 {
                     var bytes = await response.Content.ReadAsByteArrayAsync();
                     return Decode(bytes);
                 })
                 .GetAsync();

条件重试 / 认证刷新

var data = await factory.CreateRequestable("https://api.example.com/me")
    .AssignHeader("Authorization", $"Bearer {token}")
    .When(status => status == HttpStatusCode.Unauthorized)
    .ThenAsync(async (req, _) =>
    {
        token = await RefreshTokenAsync();
        req.AssignHeader("Authorization", $"Bearer {token}");   // 会覆盖同名头
    })
    .JsonCast<ServResult<UserInfo>>()
    .GetAsync();

多条件 OR 重试:在同一 ThenAsync 前叠加多个 When/.Or()

var data = await factory.CreateRequestable(url)
    .When(s => s == HttpStatusCode.Unauthorized)
    .Or(s => s == HttpStatusCode.Forbidden)          // 任一条件满足即触发重试
    .ThenAsync(async (req, _) =>
    {
        req.AssignHeader("Authorization", $"Bearer {await RefreshTokenAsync()}");
    })
    .JsonCast<ServResult<UserInfo>>()
    .GetAsync();

注意:每一组 When/Or → ThenAsync 仅执行一次,避免死循环。多个独立重试机制可链式追加(ThenAsync 后可再接 When)。


数据验证

public class ServResult
{
    public int    Code      { get; set; }
    public bool   Success   { get => Code == 0; set { } }
    public string Msg       { get; set; }
    public DateTime Timestamp { get; set; }
}
public class ServResult<T> : ServResult { public T Data { get; set; } }

int userId = await factory.CreateRequestable("https://api.example.com/user")
    .Json(payload)
    .JsonCast<ServResult<int>>()
    .DataVerify(r => r.Success)
    .Success (r => r.Data)
    .Fail    (r => new BusiException(r.Msg, r.Code))
    .PostAsync();

全局初始化钩子 IRequestInitialize

public class AuthInitializer : IRequestInitialize
{
    public void Initialize(IRequestableBase req)
        => req.AssignHeader("X-Tenant", TenantContext.Current);
}

// 注册(启动前)
SingletonPools.TryAdd<IRequestInitialize, AuthInitializer>();

编码与异常容忍

.UseEncoding(Encoding.UTF8)
.JsonCatch<MyResult>(ex => MyResult.Empty)   // 反序列化失败兜底
.XmlCatch<MyResult>(ex => MyResult.Empty)

单元测试

参见 tests/Inkslab.Net.Tests/UnitTest1.cs


说明要点

  • 请求方式:显式支持 GET / DELETE / POST / PUT / HEAD / PATCH;任意方法使用 SendAsync(method);流场景使用 DownloadAsync
  • JsonCast 依赖:需有 IJsonHelper 实现(推荐 Inkslab.Json)。
  • XML 反序列化:使用 System.Xml.Serialization,请配合 [XmlElement] / [XmlIgnore] 标注。
  • 超时:以毫秒为单位,所有发送方法第一参数均为 double timeout
  • 并发IRequestFactoryHttpClient 一致,推荐单例注入复用连接池。
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  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 netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
.NET Framework net461 is compatible.  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 tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.6.1

  • .NETStandard 2.1

  • net6.0

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.25 96 6/9/2026
1.2.24 127 4/22/2026
1.2.23 127 3/27/2026
1.2.22 115 3/24/2026
1.2.20 116 3/24/2026
1.2.18 237 10/9/2025
1.2.17 237 8/15/2025
1.2.16 178 6/7/2025
1.2.14 267 11/22/2024
1.2.13 579 9/10/2024
1.2.12 254 9/10/2024
1.2.11 327 8/4/2024
1.2.10 270 7/29/2024
1.2.9 225 7/29/2024
1.2.8.5 2,632 5/15/2024
1.2.8 3,337 3/26/2024
1.2.7 284 3/1/2024
1.2.5 476 1/2/2024
1.2.4.1 292 12/20/2023
1.2.4 276 12/12/2023
Loading failed