PlainHttp 1.0.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package PlainHttp --version 1.0.1
NuGet\Install-Package PlainHttp -Version 1.0.1
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PlainHttp" Version="1.0.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add PlainHttp --version 1.0.1
#r "nuget: PlainHttp, 1.0.1"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install PlainHttp as a Cake Addin
#addin nuget:?package=PlainHttp&version=1.0.1

// Install PlainHttp as a Cake Tool
#tool nuget:?package=PlainHttp&version=1.0.1

PlainHttp NuGet License

An easy HTTP client for .NET Core 2.2 with support for serialization, proxies, testing and more

Features

  • Wraps HttpClient and provides a cleaner and easier interface
  • Supports any HTTP method
  • Per-request timeout with a custom HttpRequestTimeoutException
  • Per-request proxy with transparent pooling
  • Automatic serialization of objects to JSON/XML/URL encoded
  • (no deserialization support)
  • Download a file to disk
  • Set a response encoding
  • Automatically enabled decompression of GZIP and DEFLATE responses
  • Allows to mock requests for unit testing
  • Heavily used in production by @botfactoryit to send 1 million requests per day

Why .NET Core 2.2

This library targets .NET Core 2.2 because it requires the PooledConnectionLifetime property on HttpMessageHandler, introduced in .NET Core 2.2. This makes sure that reusing the same HttpClient for a long time doesn't have unintended consequences affecting DNS resolution. This library in fact keeps a pool of HttpClient instances that are never disposed.

In particular, the library keeps:

  • One HttpClient per request host
  • One HttpClient per proxy

There is currently no mechanism that disposes HttpClient instances that are unused, so if you use a lot of random proxies or many different hostnames, you might get into trouble. This can be easily improved by creating a custom IHttpClientFactory, and then passing the factory to each request through the HttpClientFactory property.

Usage

Basic GET request:

string url = "http://random.org";
IHttpRequest request = new HttpRequest(url);
IHttpResponse response = await request.SendAsync();
string body = response.Body;

Also with Uri:

Uri uri = new Uri("http://random.org");
IHttpRequest request = new HttpRequest(uri);

Checking if the HTTP status code is in the 2xx range:

IHttpResponse response = await request.SendAsync();

if (!response.Succeeded)
{
    Console.WriteLine($"Response status code is {response.Message.StatusCode}");
}
else
{
    Console.WriteLine($"Response length is {response.Body.Length}");
}

Every exception is wrapped in an HttpRequestException, from which HttpRequestTimeoutException is derived:

try
{
    IHttpResponse response = await request.SendAsync();
}
catch (HttpRequestException ex)
{
    if (ex is HttpRequestTimeoutException)
    {
        Console.WriteLine("Request timed out");
    }
    else
    {
        Console.WriteLine("Something bad happened: {0}", ex);
        // PlainHttp.HttpRequestException: Failed request: [GET https://yyyy.org/] [No such host is known] ---> System.Net.Http.HttpRequestException: No such host is known ---> System.Net.Sockets.SocketException: No such host is known
        // etc.
    }
}

Setting custom headers:

IHttpRequest request = new HttpRequest(url)
{
    Headers = new Dictionary<string, string>
    {
        // note that no user agent is set by default
        { "User-Agent", "PlainHttp/1.0" }
    }
};

Custom response decoding:

IHttpRequest request = new HttpRequest(url)
{
    ResponseEncoding = Encoding.GetEncoding("ISO-8859-1")
};

Custom timeout (note that by default there is no timeout):

IHttpRequest request = new HttpRequest(url)
{
    Timeout = TimeSpan.FromSeconds(10)
};

POST request with URL-encoded payload:

IHttpRequest request = new HttpRequest(url)
{
    Method = HttpMethod.Post,
    ContentType = ContentType.UrlEncoded,
    Payload = new
    {
        something = "hello",
        buuu = true
    }
};

POST request with JSON payload:

IHttpRequest request = new HttpRequest(url)
{
    Method = HttpMethod.Post,
    ContentType = ContentType.Json,
    // you can pass any object, it will be passed to JSON.NET
    Payload = new
    {
        something = "web"
    }
};

POST request with XML payload:

IHttpRequest request = new HttpRequest(url)
{
    Method = HttpMethod.Post,
    ContentType = ContentType.Json,
    // the object will be passed to System.Xml.Serialization.XmlSerializer
    Payload = new
    {
        something = "web"
    }
};

You can also choose a content type and then pass an already serialized string:

IHttpRequest request = new HttpRequest(url)
{
    Method = HttpMethod.Post,
    ContentType = ContentType.Json,
    Payload = "{ \"key\": true }"
};

There is currently no response deserialization. The body will be automatically converted to string with the encoding specified by the ResponseEncoding property.

If you specify the DownloadFileName property, the response will be saved to file and the response Body property will be null.

IHttpRequest request = new HttpRequest(url)
{
    Method = HttpMethod.Get,
    DownloadFileName = "page.html"
};

Set a custom proxy per request (make sure you're aware of the pitfalls mentioned above):

IHttpRequest request = new HttpRequest(url)
{
    Proxy = new Uri("http://yyyy.org:3128")
};

This library wraps the Flurl URL builder that provides some utilities that are used internally. You can anyway use Flurl to build URLs in an easier way:

string url = "http://random.org"
    .SetQueryParam("locale", "it")
    .SetQueryParam("timestamp", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());

Unit testing HTTP requests is easy with PlainHttp. You can enqueue HTTP responses that will be dequeued in sequence. This mechanism is "async safe": the TestingMode property is static but wrapped in to an AsyncLocal instance, so that you can run your tests in parallel.

// Run this once
TestingMode http = new TestingMode();
HttpRequest.SetTestingMode(http);

// Then enqueue HTTP responses
HttpResponseMessage msg = new HttpResponseMessage()
{
    StatusCode = (HttpStatusCode)200,
    Content = new StringContent("oh hello")
};

http.RequestsQueue.Enqueue(msg);

// Then send your requests normally, in the same async context
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.2 is compatible.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.0.0 641 1/24/2024
2.0.0-pre3 70 1/24/2024
2.0.0-pre2 296 11/21/2023
2.0.0-pre1 215 10/28/2023
1.3.0 1,524 8/1/2022
1.2.0 1,309 11/27/2020
1.1.2 760 6/24/2020
1.1.1 407 6/19/2020
1.1.0 420 6/18/2020
1.0.3 892 12/12/2019
1.0.2 520 12/4/2019
1.0.1 750 10/3/2019
1.0.0 901 8/16/2019