Skip to main content

Axios.Net

An HTTP client for .NET 8 and .NET 10 with an axios-like syntax: Axios.Get<T>(url), Axios.Create(config), interceptors, config merging and typed errors. It has no dependency other than the .NET runtime (JSON is handled by System.Text.Json).

Features​

  • axios API: Get, Post, Put, Patch, Delete, Head, Options, Request, PostForm, PutForm, PatchForm, GetUri, Create, All, MergeConfig and ToFormData.
  • Typed responses: AxiosResponse<T> exposes Status, StatusText, Headers, Config, Request, Duration and Data.
  • Config merging: library defaults, then client defaults, then the request config. Default headers per method (common, post, ...).
  • Query parameters: nested objects and arrays, with a configurable array format and serializer.
  • Bodies: JSON, application/x-www-form-urlencoded, multipart/form-data (with AxiosFile), binary, streams and any HttpContent.
  • Interceptors: request and response chains with Eject, Clear, RunWhen and Synchronous, able to recover from errors.
  • Errors: a single AxiosException carrying the axios error codes, plus ValidateStatus and Redact.
  • Transfer control: timeout, cancellation, upload and download progress, size limits, redirects, decompression and proxy.
  • Transforms: TransformRequest and TransformResponse.

Installation​

Install-Package Axios.Net

or with the .NET CLI:

dotnet add package Axios.Net
info

The package is named Axios.Net but the root namespace is AxiosNet, so that it does not clash with the static Axios class.

Usage​

Direct requests​

Program.cs
using AxiosNet.Client;
using AxiosNet.Models;

AxiosResponse<User> res = await Axios.Get<User>("https://api.test/users/1");

Console.WriteLine($"{res.Status} {res.Data!.Name} in {res.Duration.TotalMilliseconds}ms");

await Axios.Post<User>("https://api.test/users", new { Name = "Ada" });

The body type T decides how the response is read: string, byte[] and Stream return the raw body, anything else is deserialized from JSON. You can override it with ResponseType.

Configured instance​

Axios.Create is the equivalent of axios.create: the client keeps its own defaults and interceptors.

Program.cs
using AxiosNet.Client;
using AxiosNet.Config;

AxiosClient api = Axios.Create(new AxiosRequestConfig
{
BaseUrl = "https://api.test/v1",
Timeout = TimeSpan.FromSeconds(10),
Headers = { ["X-App"] = "demo" }
});

api.Defaults.CommonHeaders["Authorization"] = "Bearer token"; // axios defaults.headers.common
api.Defaults.MethodHeaders["post"] = new() { { "X-Post", "1" } }; // axios defaults.headers.post

var users = await api.Get<List<User>>("/users", new() { Params = { ["page"] = 2 } });
tip

Config precedence is: library defaults, then client defaults, then the request config. Url, Method and Data are never inherited from the defaults, and headers and params are merged key by key.

Query parameters​

Arrays follow axios: a[]=1&a[]=2 by default. Use Indexes to change the format, or Serialize to take over completely.

Program.cs
await api.Get<string>("/search", new()
{
Params = { ["ids"] = new[] { 1, 2 }, ["filter"] = new Dictionary<string, object?> { ["name"] = "a b" } },
ParamsSerializer = new() { Indexes = null } // ids=1&ids=2 (false: ids[]=1, true: ids[0]=1)
});

GetUri returns the final URL without sending anything.

Request bodies​

DataSent as
stringAs is, application/x-www-form-urlencoded unless a Content-Type header is set (like axios)
byte[], StreamBinary, application/octet-stream
HttpContentAs is
Any other objectJSON

Set the Content-Type header to application/x-www-form-urlencoded or multipart/form-data (or use PostForm) to serialize an object that way. Use AxiosFile for file parts:

Program.cs
await api.PostForm<string>("/upload", new Dictionary<string, object?>
{
["name"] = "Ada",
["file"] = new AxiosFile(File.ReadAllBytes("avatar.png"), "avatar.png", "image/png")
});

Interceptors​

Request interceptors run last registered first, response interceptors first registered first.

Program.cs
int id = api.Interceptors.Request.Use(
config => { config.Headers["X-Trace"] = Guid.NewGuid().ToString(); return config; },
options: new() { RunWhen = c => c.Method == HttpMethod.Get, Synchronous = true });

api.Interceptors.Response.Use(
response => response,
error => error is AxiosException { Response: { } r } ? r : throw error); // recover or rethrow

api.Interceptors.Request.Eject(id);
api.Interceptors.Response.Clear();

Errors​

Every failure throws an AxiosException (Code, Config, Request, Response, Status, ToJSON()). Use ValidateStatus to resolve selected status codes, and Redact to mask keys in ToJSON().

Program.cs
using AxiosNet.Errors;

try
{
await api.Get<User>("/missing");
}
catch (AxiosException e)
{
Console.WriteLine($"{e.Code} {e.Status} {e.Response?.Data}");
}
CodeMeaning
ERR_BAD_REQUEST4xx response
ERR_BAD_RESPONSEOther rejected status, size limit exceeded or unreadable body
ERR_NETWORKThe request could not reach the server
ERR_CANCELEDCanceled (CanceledError)
ERR_INVALID_URLInvalid or non absolute URL
ERR_FORM_DATA_DEPTH_EXCEEDEDParams or form object nested too deeply
ECONNABORTED / ETIMEDOUTTimeout (ETIMEDOUT with Transitional.ClarifyTimeoutError)

Cancellation and progress​

Program.cs
using CancellationTokenSource cts = new();

AxiosResponse<byte[]> file = await api.Get<byte[]>("/big-file", new()
{
CancellationToken = cts.Token,
OnDownloadProgress = e => Console.WriteLine($"{e.Progress:P0} ({e.Loaded}/{e.Total})")
});

Request config​

GroupOptions
URLUrl, Method, BaseUrl, AllowAbsoluteUrls
HeadersHeaders, MethodHeaders / CommonHeaders, Auth
QueryParams, ParamsSerializer (Encode, Serialize, Indexes, MaxDepth)
BodyData, TransformRequest, FormSerializer (Dots, MetaTokens, Indexes, MaxDepth)
ResponseResponseType, ResponseEncoding, TransformResponse, ValidateStatus, Transitional
LimitsTimeout, MaxContentLength, MaxBodyLength, MaxRedirects
NetworkDecompress, Proxy
EventsOnUploadProgress, OnDownloadProgress, CancellationToken (axios' signal)
DiagnosticsRedact
note

MaxRedirects, Decompress and Proxy need the built-in handler: they are ignored when you pass your own HttpMessageHandler to Axios.Create.

Namespaces​

NamespaceTypes
AxiosNet.ClientAxios, AxiosClient
AxiosNet.ConfigAxiosRequestConfig, AxiosParamsSerializer, AxiosFormSerializer, AxiosTransitional, AxiosProxy, AxiosResponseType
AxiosNet.ErrorsAxiosException, CanceledError, AxiosErrorCodes
AxiosNet.ModelsAxiosResponse, AxiosResponse<T>, AxiosHeaders, AxiosFile, AxiosProgressEvent
AxiosNet.InterceptorsAxiosInterceptors, InterceptorManager<T>, InterceptorOptions

Differences with axios​

These axios options are tied to browsers or Node.js and have no equivalent in .NET: adapter (pass an HttpMessageHandler to Axios.Create instead), xsrfCookieName, xsrfHeaderName, withXSRFToken, withCredentials, httpAgent, httpsAgent, socketPath, allowedSocketPaths, transport, insecureHTTPParser, formDataHeaderPolicy, env, maxRate, beforeRedirect, sensitiveHeaders, parseReviver (configure AxiosClient.JsonOptions) and the deprecated CancelToken (use CancellationToken).

Coming from FrApp42.Web​

Axios.Net follows the same goal as the Request class of FrApp42.Web, with a different usage: one call instead of a fluent builder, and exceptions instead of a Result<T>.

Request (FrApp42.Web)Axios.Net
new Request(url, method) + Run<T>()Axios.Get<T>(url), Axios.Post<T>(url, data), ...
AddHeader / AddHeadersHeaders = { ["Name"] = "value" }
SetAuthorization(value)Headers = { ["Authorization"] = value }, or Auth for Basic
AddQueryParam / AddQueryParamsParams = { ["key"] = value }
AddJsonBody(obj)Data = obj, or the data argument of Post / Put / Patch
AddTextBody(text)Data = text with Headers = { ["Content-Type"] = "text/plain" }
AddByteBody(bytes, name) + RunDocument<T>()PostForm<T>(url, new Dictionary<string, object?> { ["file"] = new AxiosFile(bytes, name) })
RunBinaryRaw<T>()Post<T>(url, bytes)
RunGetBytes()Get<byte[]>(url)
TimeoutTimeout
Result<T>: StatusCode, Value, ErrorAxiosResponse<T>: Status, Data. Failures throw AxiosException (Status, Response?.Data)
caution

Request also deserialized XML responses. Axios.Net does not: read the body with Get<string> and parse it yourself. JSON is handled by System.Text.Json, so Newtonsoft.Json attributes such as [JsonProperty] are ignored (use [JsonPropertyName]).

License​

Axios.Net is released under the AGPL-3.0-or-later license.