All Projects → Pathoschild → Fluenthttpclient

Pathoschild / Fluenthttpclient

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

Projects that are alternatives of or similar to Fluenthttpclient

Fuel
The easiest HTTP networking library for Kotlin/Android
Stars: ✭ 4,057 (+1850.48%)
Mutual labels:  rest, http-client
Ky
🌳 Tiny & elegant JavaScript HTTP client based on the browser Fetch API
Stars: ✭ 7,047 (+3287.98%)
Mutual labels:  rest, http-client
Restclient
🦄 Simple HTTP and REST client for Unity based on Promises, also supports Callbacks! 🎮
Stars: ✭ 675 (+224.52%)
Mutual labels:  rest, http-client
Vscode Restclient
REST Client Extension for Visual Studio Code
Stars: ✭ 3,289 (+1481.25%)
Mutual labels:  rest, http-client
Fluentlyhttpclient
Http Client for .NET Standard with fluent APIs which are intuitive, easy to use and also highly extensible.
Stars: ✭ 73 (-64.9%)
Mutual labels:  rest, http-client
Restc Cpp
Modern C++ REST Client library
Stars: ✭ 371 (+78.37%)
Mutual labels:  rest, http-client
Parallec
Fast Parallel Async HTTP/SSH/TCP/UDP/Ping Client Java Library. Aggregate 100,000 APIs & send anywhere in 20 lines of code. Ping/HTTP Calls 8000 servers in 12 seconds. (Akka) www.parallec.io
Stars: ✭ 777 (+273.56%)
Mutual labels:  rest, http-client
Requester
Powerful, modern HTTP/REST client built on top of the Requests library
Stars: ✭ 273 (+31.25%)
Mutual labels:  rest, http-client
Flogo Contrib
Flogo Contribution repo. Contains activities, triggers, models and actions.
Stars: ✭ 60 (-71.15%)
Mutual labels:  rest, http-client
Json Api Dart
JSON:API client for Dart/Flutter
Stars: ✭ 53 (-74.52%)
Mutual labels:  rest, http-client
Httpie
As easy as /aitch-tee-tee-pie/ 🥧 Modern, user-friendly command-line HTTP client for the API era. JSON support, colors, sessions, downloads, plugins & more. https://twitter.com/httpie
Stars: ✭ 53,052 (+25405.77%)
Mutual labels:  rest, http-client
Snug
Write reusable web API interactions
Stars: ✭ 108 (-48.08%)
Mutual labels:  rest, http-client
Gun
HTTP/1.1, HTTP/2 and Websocket client for Erlang/OTP.
Stars: ✭ 710 (+241.35%)
Mutual labels:  rest, http-client
Restful.js
A pure JS client for interacting with server-side RESTful resources. Think Restangular without Angular.
Stars: ✭ 977 (+369.71%)
Mutual labels:  rest, http-client
Restclient Cpp
C++ client for making HTTP/REST requests
Stars: ✭ 1,206 (+479.81%)
Mutual labels:  rest, http-client
Fshttp
A lightweight F# HTTP library.
Stars: ✭ 181 (-12.98%)
Mutual labels:  rest, http-client
Django Rest Auth
This app makes it extremely easy to build Django powered SPA's (Single Page App) or Mobile apps exposing all registration and authentication related functionality as CBV's (Class Base View) and REST (JSON)
Stars: ✭ 2,289 (+1000.48%)
Mutual labels:  rest
Blockchain
A simple implementation of blockchain in java
Stars: ✭ 201 (-3.37%)
Mutual labels:  rest
Swagger Js
Javascript library to connect to swagger-enabled APIs via browser or nodejs
Stars: ✭ 2,319 (+1014.9%)
Mutual labels:  rest
Cocoa Rest Client
A free, native Apple macOS app for testing HTTP/REST endpoints
Stars: ✭ 2,257 (+985.1%)
Mutual labels:  rest

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

Get started

Install

Install it from NuGet:

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
.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());

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

Response metadata

The previous examples parse the response directly, but sometimes you want to peek at the HTTP metadata:

IResponse response = await client.GetAsync("messages/latest");
if (response.IsSuccessStatusCode || response.Status == HttpStatusCode.Found)
   return response.AsArray<T>();

Simple retry policy

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

client
   .SetRequestCoordinator(
      maxRetries: 3,
      shouldRetry: request => request.StatusCode != HttpStatusCode.OK,
      getDelay: (attempt, response) => TimeSpan.FromSeconds(attempt * 5) // wait 5, 10, and 15 seconds
   );

Chained retry policies

You can also wrap retry logic into IRetryConfig implementations:

/// <summary>A retry policy which retries with incremental backoff.</summary>
public class RetryWithBackoffConfig : IRetryConfig
{
    /// <summary>The maximum number of times to retry a request before failing.</summary>
    public int MaxRetries => 3;

    /// <summary>Get whether a request should be retried.</summary>
    /// <param name="response">The last HTTP response received.</param>
    public bool ShouldRetry(HttpResponseMessage response)
    {
        return request.StatusCode != HttpStatusCode.OK;
    }

    /// <summary>Get the time to wait until the next retry.</summary>
    /// <param name="attempt">The retry index (starting at 1).</param>
    /// <param name="response">The last HTTP response received.</param>
    public TimeSpan GetDelay(int attempt, HttpResponseMessage response)
    {
        return TimeSpan.FromSeconds(attempt * 5); // wait 5, 10, and 15 seconds
    }
}

Then you can add one or more retry policies, and they'll each be given the opportunity to retry a request:

client
   .SetRequestCoordinator(new[]
   {
      new TokenExpiredRetryConfig(),
      new DatabaseTimeoutRetryConfig(),
      new RetryWithBackoffConfig()
   });

Note that there's one retry count across all retry policies. For example, if TokenExpiredRetryConfig retries once before falling back to RetryWithBackoffConfig, the latter will receive 2 as its first retry count. If you need more granular control, see custom retry/coordination policy.

Cancellation tokens

The client fully supports .NET cancellation tokens if you need to abort requests:

var tokenSource = new CancellationTokenSource();
await client
   .PostAsync()
   .WithCancellationToken(tokenSource.Token);
tokenSource.Cancel();

Custom requests

You can make changes directly to the HTTP request before it's sent:

client
   .GetAsync("items")
   .WithCustom(request =>
   {
      request.Method = HttpMethod.Post;
      request.Headers.CacheControl = new CacheControlHeaderValue { MaxAge = TimeSpan.FromMinutes(30) };
   });

Synchronous use

The client is built around the async and await keywords, but you can use the client synchronously. That's not recommended — it complicates error-handling (e.g. errors get wrapped into AggregateException), and it's very easy to cause thread deadlocks when you do this (see Parallel Programming with .NET: Await, and UI, and deadlocks! Oh my! and Don't Block on Async Code).

If you really need to use it synchronously, you can just call the Result property:

Item item = client.GetAsync("items/14").Result;

Or if you don't need the response:

client.PostAsync("items", new Item()).AsResponse().Wait();

Extensibility

Custom formats

The client supports JSON and XML out of the box. If you need more, you can...

  • Add any of the existing media type formatters:

    client.Formatters.Add(new YamlFormatter());
    
  • Create your own by subclassing MediaTypeFormatter (optionally using the included MediaTypeFormatterBase class).

Custom filters

You can read and change the underlying HTTP requests and responses by creating IHttpFilter implementations. They can be useful for automating custom authentication or error-handling.

For example, the default error-handling is just a filter:

/// <summary>Method invoked just after the HTTP response is received. This method can modify the incoming HTTP response.</summary>
/// <param name="response">The HTTP response.</param>
/// <param name="httpErrorAsException">Whether HTTP error responses (e.g. HTTP 404) should be raised as exceptions.</param>
public void OnResponse(IResponse response, bool httpErrorAsException)
{
   if (httpErrorAsException && !response.Message.IsSuccessStatusCode)
      throw new ApiException(response, $"The API query failed with status code {response.Message.StatusCode}: {response.Message.ReasonPhrase}");
}

...which you can replace with your own:

client.Filters.Remove<DefaultErrorFilter>();
client.Filters.Add(new YourErrorFilter());

You can do much more with HTTP filters by editing the requests before they're sent or the responses before they're parsed:

/// <summary>Method invoked just before the HTTP request is submitted. This method can modify the outgoing HTTP request.</summary>
/// <param name="request">The HTTP request.</param>
public void OnRequest(IRequest request)
{
   // example only — you'd normally use a method like client.SetAuthentication(…) instead.
   request.Message.Headers.Authorization = new AuthenticationHeaderValue("token", "…");
}

Custom retry/coordination policy

You can implement IRequestCoordinator to control how requests are dispatched. For example, here's a retry coordinator using Polly:

/// <summary>A request coordinator which retries failed requests with a delay between each attempt.</summary>
public class RetryCoordinator : IRequestCoordinator
{
   /// <summary>Dispatch an HTTP request.</summary>
   /// <param name="request">The response message to validate.</param>
   /// <param name="send">Dispatcher that executes the request.</param>
   /// <returns>The final HTTP response.</returns>
   public Task<HttpResponseMessage> ExecuteAsync(IRequest request, Func<IRequest, Task<HttpResponseMessage>> send)
   {
      HttpStatusCode[] retryCodes = { HttpStatusCode.GatewayTimeout, HttpStatusCode.RequestTimeout };
      return Policy
         .HandleResult<HttpResponseMessage>(request => retryCodes.Contains(request.StatusCode)) // should we retry?
         .WaitAndRetryAsync(3, attempt => TimeSpan.FromSeconds(attempt)) // up to 3 retries with increasing delay
         .ExecuteAsync(() => send(request)); // begin handling request
   }
}

...and here's how you'd set it:

client.SetRequestCoordinator(new RetryCoordinator());

(You can only have one request coordinator on the client; you should use HTTP filters instead for most overrides.)

Custom HTTP

For advanced scenarios, you can customise the underlying HttpClient and HttpClientHandler. See the next section for an example.

Mocks for unit testing

Here's how to create mock requests for unit testing using RichardSzalay.MockHttp:

// create mock
var mockHandler = new MockHttpMessageHandler();
mockHandler.When(HttpMethod.Get, "https://example.org/api/items").Respond(HttpStatusCode.OK, testRequest => new StringContent("[]"));

// create client
var client = new FluentClient("https://example.org/api", new HttpClient(mockHandler));
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].