The .NET counterpart to dexpace/java-sdk and
dexpace/python-sdk. It is an HTTP-client
toolkit, not an HTTP client: it provides immutable HTTP models, a transport SPI, a staged pipeline
with retry, redirect, auth and instrumentation policies, and pagination, with SSE and more to come.
Consuming libraries plug in a concrete transport via the IHttpClient / IAsyncHttpClient interfaces.
The public API follows .NET idioms — record and readonly record struct for immutable models,
interfaces for SPIs, Task / IAsyncDisposable for the async-first surface, and
System.Net.Http as the reference transport — while keeping the same architectural shape as the
Java and Python ports.
Pre-release: nothing is published yet, and every package is at 0.0.1-alpha.1. Three NuGet packages are built:
| Package | What it is |
|---|---|
Dexpace.Sdk.Core |
The toolkit: HTTP models, bodies, the transport SPI, errors, the staged pipeline and its policies (operation timeout, redirect, retry, idempotency, Date, client identity, instrumentation), auth (credentials, token cache, auth policies), pagination, options, diagnostics and the ISerde seam |
Dexpace.Sdk.Http.SystemNet |
The reference transport over System.Net.Http.HttpClient |
Dexpace.Sdk.Serialization.SystemTextJson |
The ISerde codec over source-generated System.Text.Json |
What is not built yet — the domain-model rework (query parameters, request options and conditions), file, form and multipart bodies, the execution-context and recovery chains, layered configuration and body logging, RFC 7235 challenges and Digest auth, tri-state PATCH, SSE, the transport conformance kit and the DI package — is scheduled by the v1 roadmap, and what the first release needs is in docs/first-release.md. The normative specification is docs/product-spec/ and the .NET design is docs/sdk-design-dotnet/; docs/README.md indexes the rest.
dotnet-sdk/
├── Dexpace.Sdk.sln
├── Directory.Build.props # shared compiler, analyzer, library-gate and package settings
├── Directory.Build.targets # packs each library README; pins the ILLink pack
├── Directory.Packages.props # central package versions (single source of truth)
├── BannedSymbols.txt # banned-API list for src/
├── .editorconfig # formatting + analyzer severities
├── global.json # pinned .NET SDK + the Microsoft.Testing.Platform runner
├── src/
│ ├── Dexpace.Sdk.Core/ # toolkit; no transport, no concrete codec
│ │ ├── Http/Common/ # Method, Protocol, MediaType, HttpHeaderName, Headers
│ │ ├── Http/Request/ # Request, RequestBody
│ │ ├── Http/Response/ # Response, ResponseBody, Status
│ │ ├── Client/ # IHttpClient, IAsyncHttpClient, bridges
│ │ ├── Pipeline/ # HttpPipeline, PipelineBuilder, DexpacePipeline, Policies/
│ │ ├── Auth/ # credentials and the access-token cache
│ │ ├── Pagination/ # AsyncPageable<T>, Page<T>, strategies
│ │ ├── Configuration/ # DexpaceClientOptions and sub-options
│ │ ├── Diagnostics/ # ActivitySource, Meter, UrlRedactor
│ │ ├── Serialization/ # the ISerde seam
│ │ └── Errors/ # SdkException hierarchy
│ ├── Dexpace.Sdk.Http.SystemNet/ # reference transport over System.Net.Http.HttpClient
│ └── Dexpace.Sdk.Serialization.SystemTextJson/ # ISerde over source-generated System.Text.Json
├── tests/
│ ├── Dexpace.Sdk.Core.Tests/ # core against in-memory fakes only
│ ├── Dexpace.Sdk.Http.SystemNet.Tests/ # the transport, incl. loopback wire tests
│ ├── Dexpace.Sdk.Serialization.SystemTextJson.Tests/
│ ├── Dexpace.Sdk.TestSupport/ # shared fakes (not packed)
│ └── Dexpace.Sdk.AotSmoke/ # NativeAOT smoke consumer
├── scripts/ci/ # coverage gate, dependency audit, reproducible pack
├── tools/Dexpace.Tools.sln # repository tools (knowledge CLI, housekeeping skill) and their tests
└── docs/
dotnet restore Dexpace.Sdk.sln --locked-mode
dotnet build Dexpace.Sdk.sln --configuration Release # the build IS the lint gate (warnings-as-errors)
dotnet format Dexpace.Sdk.sln --verify-no-changes
dotnet test --solution Dexpace.Sdk.sln --configuration Release
dotnet pack Dexpace.Sdk.sln --configuration Release --output artifacts/packagesRequires the .NET SDK pinned in global.json (10.0.401, rolling forward to the latest patch). Every project
targets net10.0. Tests are xUnit v3 on Microsoft.Testing.Platform, so dotnet test takes --solution /
--project and the platform's options. CONTRIBUTING.md lists every CI gate and how to run it
locally.
using Dexpace.Sdk.Core.Configuration;
using Dexpace.Sdk.Core.Http.Request;
using Dexpace.Sdk.Core.Pipeline;
using Dexpace.Sdk.Http.SystemNet;
await using var transport = new SystemNetHttpClient();
var pipeline = DexpacePipeline.CreateDefault(transport); // redirect, retry, instrumentation, …
using var response = await pipeline.SendAsync(Request.Get("https://api.example.com/health"), new DexpaceClientOptions());
if (response.IsSuccess)
{
Console.WriteLine(await response.Body.ReadAsStringAsync());
}These are enforced by the build (TreatWarningsAsErrors, AnalysisLevel=latest-recommended,
EnforceCodeStyleInBuild), .editorconfig and CI; CLAUDE.md has the full list.
net10.0, C#latest, nullable on, implicit usings off (a committedGlobalUsings.csper project).- Immutable models.
record/readonly record struct; mutate viawithexpressions or theWith*helpers. No builders-as-objects — C# object initializers andwithcover it. - Interfaces for SPIs.
IHttpClient,IAsyncHttpClientandISerdeare the seams; core ships no transport and no concrete codec of its own. - Async-first, deterministic cleanup. Bodies, responses, and transports implement
IDisposable/IAsyncDisposable; single-use bodies throw on a second read. Library code awaits withConfigureAwait(false). - Core's one runtime dependency is
Microsoft.Extensions.Logging.Abstractions. Otherwise it builds against the shared framework only; adapters add at most one third-party library each. - Narrow public API, fully documented, and locked. Every public member carries a
///doc comment, and every change to the surface is a reviewedPublicAPI.Unshipped.txtdiff. - MIT license header on every source file.
MIT — see LICENSE.