Loading repository data…
Loading repository data…
jchristn / repository
RestWrapper is a simple C# class library to help simplify sending REST API requests and retrieving responses (RESTful HTTP and HTTPS)
RestWrapper is a small C# library for sending HTTP requests without rebuilding the same request, header, authorization, streaming, and response-handling code in every project.
It wraps HttpClient with a compact API for:
HttpClient instancesIf you want something lighter than a full API client framework but more structured than hand-rolled HttpClient calls, RestWrapper sits in the middle:
HttpClient for DI, proxies, mTLS, custom handlers, and connection reuseRestWrapper currently targets:
netstandard2.0netstandard2.1net462net48net6.0net8.0net10.0dotnet add package RestWrapper
Simple GET:
using RestWrapper;
using RestRequest request = new RestRequest("https://api.example.com/status");
using RestResponse response = await request.SendAsync();
Console.WriteLine(response.StatusCode);
Console.WriteLine(response.DataAsString);
Simple POST:
using RestWrapper;
using System.Net.Http;
using RestRequest request = new RestRequest("https://api.example.com/messages", HttpMethod.Post);
request.ContentType = "text/plain";
using RestResponse response = await request.SendAsync("Hello, world!");
Console.WriteLine(response.StatusCode);
Console.WriteLine(response.DataAsString);
JSON response handling:
using RestWrapper;
using RestRequest request = new RestRequest("https://api.example.com/items/123");
using RestResponse response = await request.SendAsync();
MyDto dto = response.DataFromJson<MyDto>();
Console.WriteLine(dto.Name);
Form data:
using RestWrapper;
using System.Net.Http;
using RestRequest request = new RestRequest("https://api.example.com/login", HttpMethod.Post);
Dictionary<string, string> form = new Dictionary<string, string>
{
{ "username", "alice" },
{ "password", "secret" }
};
using RestResponse response = await request.SendAsync(form);
Console.WriteLine(response.StatusCode);
using RestWrapper;
using System.Net.Http;
using RestRequest request = new RestRequest("https://api.example.com/items", HttpMethod.Post);
request.ContentType = "application/json; charset=utf-8";
request.Headers.Add("X-Correlation-Id", Guid.NewGuid().ToString());
request.Headers.Add("X-Tenant", "east");
using RestResponse response = await request.SendAsync("{\"name\":\"demo\"}");
Basic auth:
using RestWrapper;
using RestRequest request = new RestRequest("https://api.example.com/secure");
request.Authorization.User = "alice";
request.Authorization.Password = "secret";
using RestResponse response = await request.SendAsync();
Bearer auth:
using RestWrapper;
using RestRequest request = new RestRequest("https://api.example.com/secure");
request.Authorization.BearerToken = "your-token";
using RestResponse response = await request.SendAsync();
Raw authorization header:
using RestWrapper;
using RestRequest request = new RestRequest("https://api.example.com/secure");
request.Authorization.Raw = "Custom scheme-value";
using RestResponse response = await request.SendAsync();
using RestWrapper;
using System.Net.Http;
using System.Text;
byte[] payload = Encoding.UTF8.GetBytes("streamed payload");
using MemoryStream stream = new MemoryStream(payload);
using RestRequest request = new RestRequest("https://api.example.com/upload", HttpMethod.Put);
request.ContentType = "text/plain";
using RestResponse response = await request.SendAsync(payload.Length, stream);
Console.WriteLine(response.StatusCode);
For regular responses, RestWrapper gives you multiple access patterns:
response.Data for the raw streamresponse.DataAsBytes for the buffered byte arrayresponse.DataAsString for UTF-8 string contentresponse.DataFromJson<T>() for JSON payloadsExample:
using RestWrapper;
using RestRequest request = new RestRequest("https://api.example.com/items/123");
using RestResponse response = await request.SendAsync();
Console.WriteLine(response.IsSuccessStatusCode);
Console.WriteLine(response.ContentType);
Console.WriteLine(response.ContentLength);
Console.WriteLine(response.DataAsString);
Each RestResponse includes a Time property:
using RestWrapper;
using RestRequest request = new RestRequest("https://api.example.com/status");
using RestResponse response = await request.SendAsync();
Console.WriteLine("Start : " + response.Time.Start);
Console.WriteLine("End : " + response.Time.End);
Console.WriteLine("Total ms : " + response.Time.TotalMs);
When the server returns text/event-stream, use ReadEventAsync() instead of Data, DataAsString, or DataFromJson<T>().
using RestWrapper;
using RestRequest request = new RestRequest("https://api.example.com/events");
using RestResponse response = await request.SendAsync();
while (true)
{
ServerSentEvent evt = await response.ReadEventAsync();
if (evt == null) break;
Console.WriteLine($"[{evt.Event}] {evt.Data}");
}
ServerSentEvent exposes:
IdEventDataRetryRestWrapper supports chunked request sending and chunked response reading.
using RestWrapper;
using System.Net.Http;
using RestRequest request = new RestRequest("https://api.example.com/chunked", HttpMethod.Post);
request.ChunkedTransfer = true;
await request.SendChunkAsync("chunk-1", false);
await request.SendChunkAsync("chunk-2", false);
using RestResponse response = await request.SendChunkAsync(Array.Empty<byte>(), true);
while (true)
{
ChunkData chunk = await response.ReadChunkAsync();
if (chunk == null) break;
Console.WriteLine(System.Text.Encoding.UTF8.GetString(chunk.Data));
if (chunk.IsFinal) break;
}
As with SSE, chunked responses are a specialized mode. Use ReadChunkAsync() instead of Data, DataAsString, DataAsBytes, or DataFromJson<T>().
If you already manage HttpClient instances through dependency injection or need custom handler behavior, you can provide your own HttpClient to RestRequest.
using RestWrapper;
using System.Net.Http;
using HttpClient client = new HttpClient();
using RestRequest request = new RestRequest(
"https://api.example.com/resource",
HttpMethod.Get,
client);
using RestResponse response = await request.SendAsync();
Console.WriteLine(response.StatusCode);
This is useful for:
HttpClientFactory usageImportant behavior:
RestRequest will not dispose an external HttpClientWhen you supply your own HttpClient, transport-level settings also become caller-owned. In that mode, do not use these RestRequest properties:
TimeoutMillisecondsAllowAutoRedirectIgnoreCertificateErrorsCertificateFilenameCertificatePasswordConfigure those on your HttpClient / handler instead.
RestResponse.DataFromJson<T>() uses System.Text.Json by default, but you can replace the serializer:
using RestWrapper;
using RestRequest request = new RestRequest("https://api.example.com/items/123");
using RestResponse response = await request.SendAsync();
response.SerializationHelper = new MySerializer();
MyDto dto = response.DataFromJson<MyDto>();
MySerializer just needs to implement ISerializationHelper.
The repository includes both interactive and automated test hosts:
src/Test is the interactive console appsrc/Test.Shared contains the shared Touchstone suitessrc/Test.Automated is the console/CLI automation runnersrc/Test.Xunit exposes the shared suite through dotnet testsrc/Test.Nunit exposes the same shared suite through dotnet testThe current automated surface contains 120 shared cases covering:
HttpClient flowsRestWrapper uses the platform HttpClient stack. When targeting localhost, some environments will try IPv6 loopback first. If your local service is only listening on IPv4, that can introduce a noticeable delay.
If you see this behavior, prefer 127.0.0.1 instead of localhost.
See CHANGELOG.md for version history.