SkillAgentSearch skills...

FluentHttpClient

A modern async HTTP client for REST APIs. Its fluent interface lets you send an HTTP request and parse the response in one go.

Install / Use

/learn @Pathoschild/FluentHttpClient
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

FluentHttpClient is a modern async HTTP client for REST APIs. Its fluent interface lets you send an HTTP request and parse the response in one go — hiding away the gritty details like deserialisation, content negotiation, optional retry logic, and URL encoding:

Blog result = await new FluentClient("https://example.org/api")
   .GetAsync("blogs")
   .WithArgument("id", 15)
   .WithBearerAuthentication(token)
   .As<Blog>();

Designed with discoverability and extensibility as core principles, just autocomplete to see which methods are available at each step.

Contents

Why does this exist?

.NET has a built-in HttpClient, and there are various higher-level HTTP clients. However:

  • Using HttpClient can be tedious, with lots of repeated boilerplate code and low discoverability. Usage also varies depending on your .NET version (and it isn't available at all in some older versions), which makes multi-targeting harder.
  • Higher-level client libraries often reinvent the wheel. That means you can lose access to the features, performance optimizations, best practices & patterns, and ecosystem of libraries which come with using the built-in HttpClient.

FluentHttpClient offers a middle ground: it's a thin wrapper around the .NET HttpClient which greatly simplifies its API and makes it consistent across .NET versions, but you're still using the official HttpClient with all the benefits that provides.

For example, this code using FluentHttpClient:

using var client = using new FluentClient("https://example.org/api");
Blog result = await client
   .GetAsync("blogs")
   .WithArgument("search", searchText)
   .WithBearerAuthentication(token)
   .As<Blog>();

Is equivalent to this code when using HttpClient directly:

using var client = new HttpClient
{
    BaseAddress = new Uri("https://example.org/api/")
};

string url = "blogs";
if (searchText != null)
    url += $"?search={Uri.EscapeDataString(searchText)}";

using var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.UserAgent.Add(new ProductInfoHeaderValue("ExampleClient", "1.0.0")); // often required; a configurable user agent is added automatically by FluentHttpClient
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();

string json = await response.Content.ReadAsStringAsync();
Blog result = JsonConvert.DeserializeObject<Blog>(json);

And that's just a simple example; the HttpClient code can become much more complex if you want any of the other features built-in to FluentHttpClient like content negotiation, retry logic, error-handling, filters, etc.

Get started

Install

Install it [from NuGet][Pathoschild.Http.FluentClient]:

Install-Package Pathoschild.Http.FluentClient

The client works on most platforms (including Linux, Mac, and Windows):

| platform | min version | | :-------------------------- | :---------- | | .NET | 5.0 | | .NET Core | 1.0 | | .NET Framework | 4.5.2 | | [.NET Standard][] | 1.3 | | Mono | 4.6 | | Unity | 2018.1 | | Universal Windows Platform | 10.0 | | Xamarin.Android | 7.0 | | Xamarin.iOS | 10.0 | | Xamarin.Mac | 3.0 |

Basic usage

Just create the client and chain methods to set up the request/response. For example, this sends a GET request and deserializes the response into a custom Item class based on content negotiation:

Item item = await new FluentClient()
   .GetAsync("https://example.org/api/items/14")
   .As<Item>();

You can also reuse the client for many requests (which improves performance using the built-in connection pool), and set a base URL in the constructor:

using var client = new FluentClient("https://example.org/api");

Item item = await client
   .GetAsync("items/14")
   .As<Item>();

The client provides methods for DELETE, GET, POST, PUT, and PATCH out of the box. You can also use SendAsync to craft a custom HTTP request.

URL arguments

You can add any number of arguments to the request URL with an anonymous object:

await client
   .PostAsync("items/14")
   .WithArguments(new { page = 1, search = "some search text" });

Or with a dictionary:

await client
   .PostAsync("items/14")
   .WithArguments(new Dictionary<string, object> { … });

Or individually:

await client
   .PostAsync("items/14")
   .WithArgument("page", 1)
   .WithArgument("search", "some search text");

Body

You can add a model body directly in a POST or PUT:

await client.PostAsync("search", new SearchOptions(…));

Or add it to any request:

await client
   .GetAsync("search")
   .WithBody(new SearchOptions(…));

Or provide it in various formats:

format | example :--------------- | :------ serialized model | WithBody(new ExampleModel()) form URL encoded | WithBody(p => p.FormUrlEncoded(values)) file upload | WithBody(p => p.FileUpload(files)) HttpContent | WithBody(httpContent)

Headers

You can add any number of headers:

await client
   .PostAsync("items/14")
   .WithHeader("User-Agent", "Some Bot/1.0.0")
   .WithHeader("Content-Type", "application/json");

Or use methods for common headers like WithAuthentication, WithBasicAuthentication, WithBearerAuthentication, and SetUserAgent.

(Basic headers like Content-Type and User-Agent will be added automatically if you omit them.)

Read the response

You can parse the response by awaiting an As* method:

await client
   .GetAsync("items")
   .AsArray<Item>();

Here are the available formats:

type | method --------- | ------ Item | As<Item>() Item[] | AsArray<Item>() byte[] | AsByteArray() string | AsString() Stream | AsStream() JToken | AsRawJson() JObject | AsRawJsonObject() JArray | AsRawJsonArray()

The AsRawJson method can also return dynamic to avoid needing a model class:

dynamic item = await client
   .GetAsync("items/14")
   .AsRawJsonObject();

string author = item.Author.Name;

If you don't need the content, you can just await the request:

await client.PostAsync("items", new Item(…));

Read the response info

You can also read the HTTP response info before parsing the body:

IResponse response = await client.GetAsync("items");
if (response.IsSuccessStatusCode || response.Status == HttpStatusCode.Found)
   return response.AsArray<Item>();

Handle errors

By default the client will throw ApiException if the server returns an error code:

try
{
   await client.Get("items");
}
catch(ApiException ex)
{
   string responseText = await ex.Response.AsString();
   throw new Exception($"The API responded with HTTP {ex.Response.Status}: {responseText}");
}

If you don't want that, you can...

  • disable it for one request:

    IResponse response = await client
       .GetAsync("items")
       .WithOptions(ignoreHttpErrors: true);
    
  • disable it for all requests:

    client.SetOptions(ignoreHttpErrors: true);
    
  • use your own error filter.

Advanced features

Request options

You can customize the request/response flow using a few built-in options.

You can set an option for one request:

IResponse response = await client
   .GetAsync("items")
   .WithOptions(ignoreHttpErrors: true);

Or for all requests:

client.SetOptions(ignoreHttpErrors: true);

The available options are:

option | default | effect --------------------- | ------- | ------ ignoreHttpErrors | false | Whether HTTP error responses like HTTP 404 should be ignored (true) or raised as exceptions (false). ignoreNullArguments | true | Whether null arguments in the request body and URL query string should be ignored (true) or sent as-is (false). completeWhen | ResponseContentRead | When we should stop waiting for the response. For example, setting this to ResponseHeadersRead will let you handle the response as soon as the headers are received, before the full response body has been fetched. This only affects getting the IResponse; reading the response body (e.g. using a method like IResponse.As<T>()) will still wait for the request body to be fetched as usual.

Simple retry policy

The client won't retry failed requests by default, but that's easy to configure:

client
   .SetRequestCoordinator(
  
View on GitHub
GitHub Stars359
CategoryDevelopment
Updated1mo ago
Forks54

Languages

C#

Security Score

100/100

Audited on Mar 4, 2026

No findings