RestSharp
Production guide to RestSharp: The mature REST and HTTP API client library for .NET
6 minute read
is one of the most mature and widely-used REST client libraries in the .NET ecosystem, with 9.8k+ GitHub stars and 268 contributors.
RestSharp 113.x - Major Rewrite
**RestSharp 107+** was completely rewritten to use `HttpClient` internally. It now serves as a lightweight wrapper around `HttpClient`, adding convenience features like default parameters, built-in serialization, and authentication support.| Info | Link |
|---|---|
| License | |
| Downloads | |
| Latest Version | |
| Issues | |
| Contributors |
What RestSharp Adds to HttpClient
RestSharp is a wrapper around HttpClient that provides:
- Default parameters - Add headers, query parameters, or body data at the client level
- Multiple serialization formats - JSON (System.Text.Json), XML, CSV with pluggable serializers
- Rich parameter handling - URL segments, query strings, headers, cookies, body content
- Authentication support - OAuth 1/2, Basic, JWT, API keys
- Request/response interceptors - Customize the HTTP pipeline
- Simpler API - Less boilerplate than raw
HttpClient
Available Packages
| Package | Description |
|---|---|
RestSharp | Core library with System.Text.Json and basic XML serializer |
RestSharp.Serializers.NewtonsoftJson | Newtonsoft.Json serializer |
RestSharp.Serializers.Xml | Custom RestSharp XML serializer |
RestSharp.Serializers.CsvHelper | CSV serializer |
Quick Start
Installation
dotnet add package RestSharp
Basic Usage
using RestSharp;
// Create client with options
RestClientOptions options = new("https://api.example.com")
{
ThrowOnAnyError = false, // Don't throw on non-2xx status codes
Timeout = TimeSpan.FromSeconds(30),
UserAgent = "MyApp/1.0"
};
RestClient client = new(options);
// GET - Retrieve a single resource
RestRequest getRequest = new("api/v1/tasks/{taskId}");
getRequest.AddUrlSegment("taskId", 123);
RestResponse<TaskDto> getResponse = await client.ExecuteGetAsync<TaskDto>(getRequest);
if (getResponse.IsSuccessful && getResponse.Data is not null)
{
TaskDto task = getResponse.Data;
Console.WriteLine($"Task: {task.Title}");
}
// GET - Retrieve collection
RestRequest listRequest = new("api/v1/tasks");
listRequest.AddQueryParameter("status", "pending");
listRequest.AddQueryParameter("page", 1);
TaskDto[]? tasks = await client.GetAsync<TaskDto[]>(listRequest);
// POST - Create resource
CreateTaskRequest createPayload = new("Buy groceries", "Weekly shopping", TaskPriority.Medium);
RestRequest postRequest = new RestRequest("api/v1/tasks")
.AddJsonBody(createPayload);
TaskDto? createdTask = await client.PostAsync<TaskDto>(postRequest);
Console.WriteLine($"Created task #{createdTask?.Id}");
// PUT - Update resource
UpdateTaskRequest updatePayload = new("Buy organic groceries", "Updated description", TaskPriority.High);
RestRequest putRequest = new RestRequest("api/v1/tasks/{taskId}")
.AddUrlSegment("taskId", createdTask!.Id)
.AddJsonBody(updatePayload);
TaskDto? updatedTask = await client.PutAsync<TaskDto>(putRequest);
// DELETE - Remove resource
RestRequest deleteRequest = new RestRequest("api/v1/tasks/{taskId}")
.AddUrlSegment("taskId", createdTask.Id);
RestResponse deleteResponse = await client.ExecuteDeleteAsync(deleteRequest);
Console.WriteLine($"Deleted: {deleteResponse.IsSuccessful}");
DTOs (Data Transfer Objects)
/// <summary>Represents a task in the system.</summary>
public sealed record TaskDto(
int Id,
string Title,
string? Description,
bool IsCompleted,
DateTime CreatedAt,
TaskPriority Priority);
/// <summary>Request model for creating a task.</summary>
public sealed record CreateTaskRequest(
string Title,
string? Description = null,
TaskPriority Priority = TaskPriority.Medium);
/// <summary>Request model for updating a task.</summary>
public sealed record UpdateTaskRequest(
string Title,
string? Description,
TaskPriority Priority);
/// <summary>Task priority levels.</summary>
public enum TaskPriority { Low, Medium, High, Critical }
Typed API Client Pattern
Encapsulate RestSharp calls in a dedicated client class for better maintainability:
using RestSharp;
/// <summary>
/// Typed client for the Tasks API.
/// Encapsulates all REST operations for task management.
/// </summary>
public sealed class TasksApiClient : IDisposable
{
private readonly RestClient _client;
private bool _disposed;
public TasksApiClient(string baseUrl)
{
RestClientOptions options = new(baseUrl)
{
ThrowOnAnyError = false,
Timeout = TimeSpan.FromSeconds(30)
};
_client = new RestClient(options);
}
public TasksApiClient(RestClient client)
{
_client = client;
}
/// <summary>Retrieves a task by its identifier.</summary>
public async Task<TaskDto?> GetTaskByIdAsync(
int taskId,
CancellationToken cancellationToken = default)
{
RestRequest request = new RestRequest("api/v1/tasks/{taskId}")
.AddUrlSegment("taskId", taskId);
return await _client.GetAsync<TaskDto>(request, cancellationToken);
}
/// <summary>Retrieves all tasks with optional filtering.</summary>
public async Task<IReadOnlyList<TaskDto>> GetTasksAsync(
string? status = null,
int page = 1,
int pageSize = 20,
CancellationToken cancellationToken = default)
{
RestRequest request = new RestRequest("api/v1/tasks")
.AddQueryParameter("page", page)
.AddQueryParameter("pageSize", pageSize);
if (status is not null)
{
request.AddQueryParameter("status", status);
}
TaskDto[]? tasks = await _client.GetAsync<TaskDto[]>(request, cancellationToken);
return tasks ?? Array.Empty<TaskDto>();
}
/// <summary>Creates a new task.</summary>
public async Task<TaskDto?> CreateTaskAsync(
CreateTaskRequest createRequest,
CancellationToken cancellationToken = default)
{
RestRequest request = new RestRequest("api/v1/tasks")
.AddJsonBody(createRequest);
return await _client.PostAsync<TaskDto>(request, cancellationToken);
}
/// <summary>Updates an existing task.</summary>
public async Task<TaskDto?> UpdateTaskAsync(
int taskId,
UpdateTaskRequest updateRequest,
CancellationToken cancellationToken = default)
{
RestRequest request = new RestRequest("api/v1/tasks/{taskId}")
.AddUrlSegment("taskId", taskId)
.AddJsonBody(updateRequest);
return await _client.PutAsync<TaskDto>(request, cancellationToken);
}
/// <summary>Deletes a task by its identifier.</summary>
public async Task<bool> DeleteTaskAsync(
int taskId,
CancellationToken cancellationToken = default)
{
RestRequest request = new RestRequest("api/v1/tasks/{taskId}")
.AddUrlSegment("taskId", taskId);
RestResponse response = await _client.ExecuteDeleteAsync(request, cancellationToken);
return response.IsSuccessful;
}
public void Dispose()
{
if (!_disposed)
{
_client.Dispose();
_disposed = true;
}
}
}
// Usage
using TasksApiClient client = new("https://api.example.com");
IReadOnlyList<TaskDto> tasks = await client.GetTasksAsync(status: "pending");
TaskDto? newTask = await client.CreateTaskAsync(new("New task"));
Advanced Features
Authentication
using RestSharp;
using RestSharp.Authenticators;
// Basic authentication
RestClientOptions basicOptions = new("https://api.example.com")
{
Authenticator = new HttpBasicAuthenticator("username", "password")
};
RestClient basicClient = new(basicOptions);
// JWT Bearer token
RestClientOptions jwtOptions = new("https://api.example.com")
{
Authenticator = new JwtAuthenticator("your-jwt-token")
};
RestClient jwtClient = new(jwtOptions);
// OAuth2 with token refresh
RestClientOptions oauth2Options = new("https://api.example.com")
{
Authenticator = new OAuth2AuthorizationRequestHeaderAuthenticator("access-token", "Bearer")
};
RestClient oauth2Client = new(oauth2Options);
// API Key in header
RestClient apiKeyClient = new("https://api.example.com");
apiKeyClient.AddDefaultHeader("X-API-Key", "your-api-key");
Request Configuration
using RestSharp;
RestRequest request = new RestRequest("api/v1/products")
// URL segments
.AddUrlSegment("version", "v1")
// Query parameters
.AddQueryParameter("category", "electronics")
.AddQueryParameter("inStock", true)
.AddQueryParameter("minPrice", 100.00m)
// Headers
.AddHeader("Accept-Language", "en-US")
.AddHeader("X-Request-Id", Guid.NewGuid().ToString())
// Cookies
.AddCookie("session", "abc123", "/", "api.example.com")
// JSON body
.AddJsonBody(new { name = "Product", price = 99.99 });
// Or XML body
request.AddXmlBody(new ProductXmlModel { Name = "Product" });
File Uploads
using RestSharp;
RestRequest uploadRequest = new RestRequest("api/v1/files/upload", Method.Post);
// Upload from file path
uploadRequest.AddFile("document", "path/to/document.pdf", "application/pdf");
// Upload from byte array
byte[] imageBytes = await File.ReadAllBytesAsync("image.png");
uploadRequest.AddFile("image", imageBytes, "image.png", "image/png");
// Upload from stream
await using FileStream stream = File.OpenRead("data.csv");
uploadRequest.AddFile("data", () => stream, "data.csv", "text/csv");
// Add form fields with file
uploadRequest.AddParameter("description", "My uploaded files");
uploadRequest.AddParameter("category", "documents");
RestResponse response = await client.ExecutePostAsync(uploadRequest);
Error Handling
using RestSharp;
RestRequest request = new("api/v1/tasks/999");
RestResponse<TaskDto> response = await client.ExecuteGetAsync<TaskDto>(request);
if (response.IsSuccessful)
{
TaskDto task = response.Data!;
Console.WriteLine($"Task: {task.Title}");
}
else if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
{
Console.WriteLine("Task not found");
}
else if (response.ErrorException is not null)
{
// Network or deserialization error
Console.WriteLine($"Error: {response.ErrorMessage}");
Console.WriteLine($"Exception: {response.ErrorException.Message}");
}
else
{
// HTTP error with response body
Console.WriteLine($"HTTP {(int)response.StatusCode}: {response.StatusDescription}");
Console.WriteLine($"Content: {response.Content}");
}
Dependency Injection with IHttpClientFactory
RestSharp 113+ supports Microsoft.Extensions.DependencyInjection:
using Microsoft.Extensions.DependencyInjection;
using RestSharp;
IServiceCollection services = new ServiceCollection();
// Register RestClient with IHttpClientFactory
services.AddRestClient("tasks-api", (IServiceProvider sp, RestClientOptions options) =>
{
options.BaseUrl = new Uri("https://api.example.com");
options.Timeout = TimeSpan.FromSeconds(30);
});
// Resolve and use
IServiceProvider provider = services.BuildServiceProvider();
RestClient client = provider.GetRequiredService<RestClient>();
✅ Pros
- Mature & stable - 13+ years of development, .NET Foundation project
- Intuitive API - Easy to learn and use
- Flexible serialization - JSON, XML, CSV with pluggable serializers
- Rich authentication - OAuth 1/2, Basic, JWT, custom
- No code generation - Quick prototyping without build-time steps
- Active community - Discord server, StackOverflow support
⚠️ Considerations
- Memory overhead - Higher allocations than raw HttpClient
- No type-safe interface - Manual request building required
- No compile-time validation - API contracts not verified at build time
- v107+ breaking changes - Significant API changes from earlier versions
Full Sample
See the full sample on GitHub: https://github.com/BenjaminAbt/dotnet.rest-samples