Welcome to HttpClient

Kampute.HttpClient is a .NET library designed to simplify HTTP communication with RESTful APIs by enhancing the native HttpClient capabilities. Tailored for developers seeking a potent yet flexible HTTP client for API integration within .NET applications, it combines ease of use with a wide array of functionalities to address the complexities of web service consumption.

Explore the API documentation for detailed insights.

Key Features

Serialization Support

By default, Kampute.HttpClient does not include any content deserializer. To accommodate popular content types, the following extension packages are available:

For scenarios where the provided serialization packages do not meet specific requirements, Kampute.HttpClient allows the implementation of custom deserializers. Developers can create their own serialization modules by implementing interfaces for deserialization, thus enabling support for custom content types or proprietary data formats.

Installation

Install Kampute.HttpClient via NuGet:

dotnet add package Kampute.HttpClient

Usage Examples

The examples below demonstrate how to use the library for common tasks.

Basic Usage

To get started with HttpRestClient, simply instantiate it and use it to perform HTTP requests.

using Kampute.HttpClient;
using Kampute.HttpClient.Json;

// Create a new instance of the HttpRestClient
using var client = new HttpRestClient();

// Configure the client to accept JSON responses, using System.Text.Json library.
// This is an extension method provided by the Kampute.HttpClient.Json package.
client.AcceptJson();

// Perform a GET request.
// The GetAsync<TResponse> method will automatically deserialize the JSON response
// into the specified MyModel type.
var data = await client.GetAsync<MyModel>("https://api.example.com/resource");

Scoped Request Headers

In addition to setting default request headers that apply to all requests, you can define headers for a specific set of requests using a scoped approach. This feature allows for temporary modifications to headers that override default settings within a defined context. This is particularly useful for handling varying endpoint requirements or for testing scenarios.

Below is an example that demonstrates how to temporarily override the Accept header for a series of requests, ensuring that all requests within the scope explicitly request a specific media type.

using Kampute.HttpClient;

// Create a new instance of the HttpRestClient.
using var client = new HttpRestClient();

string csv;

// Begin a scoped block where the 'Accept' header is set to 'text/csv'.
// All HTTP requests within this using block will include this 'Accept' header.
using (client.BeginHeaderScope(new Dictionary<string, string> { ["Accept"] = MediaTypeNames.Text.Csv }))
{
    // Perform a GET request to retrieve data as CSV. The 'Accept' header for this request
    // will be 'text/csv', as specified by the scoped header.
    csv = await client.GetAsStringAsync("https://api.example.com/resource/csv");
}

Alternatively, you can use the WithScope extension method to simplify the code as follows:

using Kampute.HttpClient;

using var client = new HttpRestClient();

var csv = await client
    .WithScope()
    .SetHeader("Accept", MediaTypeNames.Text.Csv)
    .PerformAsync(scopedClient => scopedClient.GetAsStringAsync("https://api.example.com/resource/csv"));

Scoped Request Properties

Similar to headers, you can also scope request properties. This capability is invaluable in scenarios where you need to maintain state or context-specific information temporarily during a series of HTTP operations. Scoped properties work similarly to scoped headers, allowing developers to define temporary data attached to requests that are automatically cleared once the scope is exited. This feature enhances the adaptability of your HTTP interactions, especially in complex or state-dependent communication scenarios.

Custom Retry Strategies

The library offers various retry strategies to manage transient failures, ensuring your application remains resilient during network instability or temporary service unavailability. The example below demonstrates how to apply a Fibonacci backoff strategy, which gradually increases the delay between retries, balancing the need to retry soon against the need to wait longer as the number of attempts increases.

using Kampute.HttpClient;

// Create a new instance of the HttpRestClient
using var client = new HttpRestClient();

// Configure the client's retry mechanism.
// The Fibonacci strategy will retry up to 5 times
// with an initial delay of 1 second between retries
// and delay increases following the Fibonacci sequence for subsequent retries.
client.BackoffStrategy = BackoffStrategies.Fibonacci(maxAttempts: 5, initialDelay: TimeSpan.FromSeconds(1));

Handling HTTP Errors

The library includes built-in handlers for managing common HTTP errors, streamlining the implementation of custom logic for error responses. Here's how to utilize the built-in '401 Unauthorized' error handler:

using Kampute.HttpClient;
using Kampute.HttpClient.ErrorHandlers;

// Create an instance of the built-in '401 Unauthorized' error handler.
// This handler defines the logic to handle unauthorized responses.
using var unauthorizedErrorHandler = new HttpError401Handler(async (client, challenges, cancellationToken) =>
{
    // In this example, we're handling the unauthorized error by making a POST request to an
    // authentication endpoint to obtain a new authentication token.
    var auth = await client.PostAsFormAsync<AuthToken>("https://api.example.com/auth",
    [
        KeyValuePair.Create("client_id", MY_APP_ID),
        KeyValuePair.Create("client_secret", MY_APP_SECRET)
    ]);

    // Return a new AuthenticationHeaderValue with the obtained token.
    // This will be used to include the authentication header in subsequent requests.
    return new AuthenticationHeaderValue(AuthSchemes.Bearer, auth.Token);
});

// Create a new instance of the HttpRestClient
using var client = new HttpRestClient();

// Register the unauthorized error handler with the client.
// This allows the client to handle '401 Unauthorized' responses automatically.
client.ErrorHandlers.Add(unauthorizedErrorHandler);

Additionally, handling '503 Service Unavailable' and '429 Too Many Requests' errors is simplified with the built-in handler, ensuring your application can gracefully retry requests during service outages and rate limit encounters.

Handling Content Types

For handling specific content types like JSON or XML, consider using the available extension packages.

In the example below, we assume that both the Kampute.HttpClient.NewtonsoftJson package, which facilitates JSON content handling through the Newtonsoft.Json library, and the Kampute.HttpClient.DataContract package, enabling XML content management via DataContractSerializer, have been installed.

using Kampute.HttpClient;
using Kampute.HttpClient.NewtonsoftJson;
using Kampute.HttpClient.DataContract;

// Create a new instance of the HttpRestClient.
using var client = new HttpRestClient();

// Configure the client to accept JSON responses, using the Newtonsoft.Json library.
// This is an extension method provided by the Kampute.HttpClient.NewtonsoftJson package
client.AcceptJson();

// Configure the client to accept XML responses, using DataContractSerializer.
// This is an extension method provided by the Kampute.HttpClient.DataContract package
client.AcceptXml();

// Execute a GET request. The server may respond in either JSON or XML format.
// The GetAsync<TResponse> method will automatically deserialize the response
// into the specified MyResource type, based on the response content type (JSON or XML).
var result = await client.GetAsync<MyResource>("https://api.example.com/resource");

// Send a PATCH request with a payload in JSON format.
// The PatchAsJsonAsync method is provided by the Kampute.HttpClient.NewtonsoftJson package.
await client.PatchAsJsonAsync("https://api.example.com/resource", new { name = "new name" });

// Send a POST request with a payload in XML format.
// The PostAsXmlAsync method is provided by the Kampute.HttpClient.DataContract package.
var newResource = new MyResource();
await client.PostAsXmlAsync("https://api.example.com/resource", newResource);

Documentation

Explore the Kampute.HttpClient library's API Documentation for an in-depth understanding of its functionalities. You'll find detailed class references, method signatures, and descriptions of properties to guide your implementation and leverage the library's full potential.

Contributing

Contributions welcome! Please follow the existing coding and documentation conventions to maintain consistency across the codebase.

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature-name
  3. Commit changes: git commit -m 'Add feature'
  4. Push branch: git push origin feature-name
  5. Open a pull request

License

Licensed under the MIT License.