Skip to content

svmukhin/sergeim-http

Repository files navigation

SergeiM.Http

GitHub Actions Workflow Status NuGet Version Hits-of-Code GitHub License

A fluent HTTP client library for .NET that provides a pure OOP approach to building and executing HTTP requests with method chaining.

Installation

dotnet add package SergeiM.Http

Quick Start

Simple GET Request

var response = new Request("https://api.example.com")
    .Uri().Path("/users").QueryParam("id", 123).Back()
    .Method(Request.GET)
    .Fetch();

JSON Response

var response = new Request("https://api.example.com")
    .Uri().Path("/users").QueryParam("id", 123).Back()
    .Header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)
    .Fetch()
    .As<JsonResponse>()
    .AssertStatus(200);

var user = response.AsObject();
string userName = user.GetString("name");
int age = user.GetInt("age");
bool isActive = user.GetBoolean("is_active", false);

XML Response with XPath

string href = new Request("https://api.example.com")
    .Uri().Path("/data").Back()
    .Header(HttpHeaders.ACCEPT, MediaType.TEXT_XML)
    .Fetch()
    .As<XmlResponse>()
    .AssertStatus(200)
    .AssertXPath("/page/links/link[@rel='see']")
    .EvaluateXPath("/page/links/link[@rel='see']/@href");

Complex Chaining Example

string name = new Request("https://www.example.com:8080")
    .Uri().Path("/users").QueryParam("id", 333).Back()
    .Method(Request.GET)
    .Header(HttpHeaders.ACCEPT, MediaType.TEXT_XML)
    .Fetch()
    .As<RestResponse>()
    .AssertStatus(200)
    .As<XmlResponse>()
    .AssertXPath("/page/links/link[@rel='see']")
    .Rel("/page/links/link[@rel='see']/@href")
    .Header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)
    .Fetch()

Wire System

SergeiM.Http uses a wire system for sending HTTP requests, allowing you to customize and extend request handling through decorators.

Basic Wire Usage

By default, requests use HttpWire:

var response = new Request("https://api.example.com").Fetch();

Custom Wire

You can specify a custom wire implementation:

var response = new Request("https://api.example.com", new HttpWire()).Fetch();

Changing Wire with Through()

Change the wire at any point using the Through() method:

var response = new Request("https://api.example.com")
    .Through(new HttpWire())
    .Fetch();

Authorization Wire

Add authorization headers to requests:

var response = new Request("https://api.example.com")
    .Through(new BasicAuthWire(new HttpWire(), "Bearer your-token"))
    .Fetch();

Retry Wire

Add automatic retry logic for failed requests:

var response = new Request("https://api.example.com", new RetryWire(
    new HttpWire(),
    maxRetries: 5,
    delayBetweenRetries: TimeSpan.FromSeconds(2)
)).Fetch();

Chaining Wire Decorators

Combine multiple decorators for advanced functionality:

var wire = new RetryWire(
    new BasicAuthWire(
        new HttpWire(),
        "Bearer token"
    ),
    maxRetries: 3,
    delayBetweenRetries: TimeSpan.FromSeconds(1)
);
var response = new Request("https://api.example.com", wire)
    .Uri().Path("/data").Back()
    .Fetch();

Or fluently with Through():

var response = new Request("https://api.example.com")
    .Through(new BasicAuthWire(new HttpWire(), "Bearer token"))
    .Uri().Path("/protected-resource").Back()
    .Through(new RetryWire(
        new BasicAuthWire(new HttpWire(), "Bearer token"),
        3,
        TimeSpan.FromSeconds(2)
    ))
    .Fetch();

Custom Wire Implementation

Create your own wire by implementing IWire:

public class LoggingWire : IWire
{
    private readonly IWire _origin;
    private readonly ILogger _logger;

    public LoggingWire(IWire origin, ILogger logger)
    {
        _origin_ = origin;
        _logger = logger;
    }

    public async Task<HttpResponseMessage> SendAsync(
        string method,
        string uri,
        Dictionary<string, string> headers,
        string? body = null)
    {
        _logger.Log($"Sending {method} request to {uri}");
        var response = await _innerWire.SendAsync(method, uri, headers, body);
        _logger.Log($"Received {response.StatusCode} from {uri}");
        return response;
    }

    public HttpResponseMessage Send(
        string method,
        string uri,
        Dictionary<string, string> headers,
        string? body = null)
    {
        return SendAsync(method, uri, headers, body).GetAwaiter().GetResult();
    }
}

Building

dotnet build

Testing

dotnet test

Conventional Commits

This project follows Conventional Commits to automate versioning and changelog generation via release-please.

Type Purpose Bump
feat New feature minor
fix Bug fix patch
docs Documentation only changes
style Code style (formatting, whitespace)
refactor Code refactoring
test Adding or updating tests
chore Maintenance (CI, deps, etc.)

Breaking changes are signaled with ! after the type (feat!:) or a BREAKING CHANGE: footer — triggers a major bump.

License

See LICENSE.txt for details.

About

No description, website, or topics provided.

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages