Logo

Notorious Client is meant to simplify the sending of HTTP requests through a fluent builder and an infinitely extensible client system.

NuGet

Summary

Support

  • Net6/7

Features

  • Easy building of HttpRequestMessage
  • Easy building of multipart/form-data requests
  • Body serialisation as JSON, powered by Newtonsoft, but customisable.
  • Easy handling of request's authentication
  • Infinitely extensible system.
  • Allow you to build maintanable and testable API Client.

Motivation

The goal is to provide a way to create API clients that is clear, fast, and above all, maintainable.

Getting Started

First, install NuGet. Then, install NotoriousClient from the package manager console:

PM> Install-Package NotoriousClient

Or from the .NET CLI as:

dotnet add package NotoriousClient

Then create a client, and inherit from BaseClient :

using NotoriousClient.Clients;

public class UserClient : BaseClient, IUserClient
{
    public UserClient(IRequestSender sender, string url) : base(sender, url)
    {
    }
}

Add method within that client that uses RequestBuilder to build your request.

public class UserClient : BaseClient, IUserClient
{
    // Define your endpoint
    private Endpoint GET_USERS_ENDPOINT = new Endpoint("/api/users", Method.Get);

    public UserClient(IRequestSender sender, string url) : base(sender, url)
    {
    }

    // Add call method.
    public async Task<IEnumerable<User>> GetUsers()
    {
        HttpRequestMessage request = GetBuilder(GET_USERS_ENDPOINT)
            .WithAuthentication("username", "password")
            .AddQueryParameter("limit", "100")
            .Build();

        HttpResponseMessage response = await Sender.SendAsync(request);

        return response.ReadAs<IEnumerable<User>>();
    }
}

Other example of API call.

private Endpoint GET_USER_ENDPOINT = new Endpoint("/api/users/{id}", Method.Get);

public async Task<User> GetUser(int id)
{
    HttpRequestMessage request = GetBuilder(GET_USERS_ENDPOINT)
        .WithAuthentication("username", "password")
        .AddEndpointParameter("id", id.ToString())
        .Build();

    HttpResponseMessage response = await Sender.SendAsync(request);

    return response.ReadAs<User>();
}
private Endpoint CREATE_USER_ENDPOINT = new Endpoint("/api/users", Method.Post);

public async Task<User> CreateUser(User user)
{
    HttpRequestMessage request = GetBuilder(CREATE_USER_ENDPOINT)
        .WithAuthentication("username", "password")
        .WithJsonBody(user)
        .Build();

    HttpResponseMessage response = await Sender.SendAsync(request);

    return response.ReadAs<User>();
}

Last but not least, add everything to your dependency injection.

services.AddHttpClient();
services.AddScoped<IRequestSender>((serviceProvider) => new RequestSender(serviceProvider.GetRequiredService<IHttpClientFactory>()));
services.AddScoped((serviceProvider) => new UserClient(serviceProvider.GetRequiredService<IRequestSender>(), "http://my.api.com/"));

How could i use RequestBuilder ?

Lets dive into the possibity of the RequestBuilder !

Configure URN, URL, and http verb.

new RequestBuilder("https://toto.com", "/users", Method.Get);

Configure URI parameters

Add URL parameters

new RequestBuilder("https://toto.com", "/users/{id}", Method.Get).AddEndpointParameter("id", "myfakeid");
IDictionary<string, string> endpointsParams = ...;
new RequestBuilder("https://toto.com", "/users/{id}/{name}", Method.Get).AddEndpointParameters(endpointsParams);

Add Query Parameters

new RequestBuilder("https://toto.com", "/users", Method.Get).AddQueryParameter("id", "myfakeid");
IDictionary<string, string> queryParams = ...;
new RequestBuilder("https://toto.com", "/users", Method.Get).AddQueryParameters(queryParams);

Configure request's headers

Add custom headers

new RequestBuilder("https://toto.com", "/users", Method.Get).AddCustomHeader("id", "myfakeid");
IDictionary<string, string> headers = ...;
new RequestBuilder("https://toto.com", "/users", Method.Get).AddCustomHeaders(headers);

Add accept header

new RequestBuilder("https://toto.com", "/users", Method.Get).WithCustomAcceptMediaType("application/json");

Authentication

Add basic authentication

new RequestBuilder("https://toto.com", "/users", Method.Get).WithAuthentication("login", "password");

Add bearer authentication

new RequestBuilder("https://toto.com", "/users", Method.Get).WithAuthentication("token");

Add custom scheme authentication

public class NotoriousAuthentication : IAuthenticationInformation
{
    private readonly string _crownId;
    private readonly string _crownName;
    public NotoriousAuthentication(string crownId, string crownName)
    {
        _crownId = crownId;
        _crownName = crownName;
    }

    public string Token => _crownName + _crownId;

    public string Scheme => "Notorious";
}

new RequestBuilder("https://toto.com", "/users", Method.Get).WithAuthentication(new NotoriousAuthentication("1997", "BIG"));

Add body to classic request

Add body as JSON

User user = GetUsersFromDb()

new RequestBuilder("https://toto.com", "/users", Method.Get).WithJsonBody(user);

Add body as JSON with a custom serializer

public class CustomSerializer : IJsonSerializer
{
    public string ConvertToJson(object obj)
    {
        // Your serilization logic here...
    }
}

User user = GetUsersFromDb()

new RequestBuilder("https://toto.com", "/users", Method.Get).WithJsonBody(user, new CustomSerializer());

Add body as Stream

Stream stream = GetFileStream("C:/Crown/BIG.png")

new RequestBuilder("https://toto.com", "/users", Method.Get).WithStreamBody(stream);

Add body as HTTP Content

new RequestBuilder("https://toto.com", "/users", Method.Get).WithContentBody(new StringContent("MyCustomContent"));

⚠️ Note that you could use any type of content handled by .NET, such as StringContent, StreamContent, HttpContent, etc...

Add body to multipart request

⚠️ Note that you CAN'T use multipart bodies if you already added a classic body to the request

Add body as JSON

User user = GetUsersFromDb()

new RequestBuilder("https://toto.com", "/users", Method.Get).WithJsonMultipartBody(user, "USER_SECTION");

Add body as JSON with a custom serializer

public class CustomSerializer : IJsonSerializer
{
    public string ConvertToJson(object obj)
    {
        // Your serilization logic here...
    }
}

User user = GetUsersFromDb()

new RequestBuilder("https://toto.com", "/users", Method.Get).WithJsonMultipartBody(user, "USER_SECTION", new CustomSerializer());

Add body as Stream

Stream stream = GetFileStream("C:/Crown/BIG.png")

new RequestBuilder("https://toto.com", "/users", Method.Get).WithStreamMultipartBody(stream, "STREAM_SECTION");

Add body as HTTP Content

new RequestBuilder("https://toto.com", "/users", Method.Get).WithContentMultipartBody(new StringContent("MyCustomContent"), "CUSTOM_CONTENT_SECTION");

How could i implement a custom BaseClient ?

NotoriousClient is entirely designed to be infinitely extensible.

Let's say you need to get a token from an API before every request.

public class BearerAuthClient : BaseClient
{
    private readonly ITokenClient _tokenClient;

    public BearerAuthClient(IRequestSender sender, string url, ITokenClient tokenClient) : base(sender, url)
    {
        ArgumentNullException.ThrowIfNull(tokenClient, nameof(tokenClient));
        _tokenClient = tokenClient;
    }

    protected override async Task<IRequestBuilder> GetBuilderAsync(string route, Method method = Method.Get)
    {
        // Get your token every time you create a request. 
        string token = await GetToken();
        
        // Return a preconfigured builder with your token !
        return (await base.GetBuilderAsync(route, method)).WithAuthentication(token);
    }

    public async Task<string> GetToken()
    {
        // Handle token logic here.
        return await _tokenClient.GetToken();
    }
}

public class UserClient : BearerAuthClient
{
    private Endpoint CREATE_USER_ENDPOINT = new Endpoint("/api/users", Method.Post);

    public UserClient(IRequestSender sender, string url) : base(sender, url)
    {
    }

    public async Task<IEnumerable<User>> CreateUser(User user)
    {
        // Every builded request will be configured with bearer authentication !
        HttpRequestMessage request = (await GetBuilderAsync(CREATE_USER_ENDPOINT))
            .WithJsonBody(user)
            .Build();

        HttpResponseMessage response = await Sender.SendAsync(request);

        return response.ReadAs<User>();
    }
}

This is your turn to play with it, you could image everything you want, adding custom authentication, custom company headers, logging !

How can i use the request's builder in standalone ?

You can create a standalone builder by instantiating RequestBuilder.

// Dont forget to use IRequestBuilder to have access to extensions method !
IRequestBuilder requestBuilder = new RequestBuilder("http://my.api.com/", "api/v1.0/users", Method.GET);

Then, you will have access to all same method that you were using in Client !