RestSharp
Simple REST and HTTP API client for .NET. Supports rapid development with intuitive API design. Built-in automatic serialization, authentication (OAuth, JWT, Basic), request/response transformation, and error handling. Based on HttpClient from version 107+.
GitHub Overview
restsharp/RestSharp
Simple REST and HTTP API Client for .NET
Topics
Star History
Library
RestSharp
Overview
RestSharp is a "Simple REST and HTTP API Client for .NET" that has been trusted and relied upon in the .NET ecosystem for many years as a high-level HTTP client library. Designed with emphasis on "ease of use and simplicity," it operates as a thin wrapper around HttpClient, providing comprehensive features necessary for RESTful API consumption including automatic JSON/XML serialization, diverse authentication methods, asynchronous processing, and file upload/download. It has been widely adopted from enterprise-level to personal projects as an intuitive and efficient HTTP communication library for .NET developers.
Details
RestSharp 2025 edition continues to evolve as an established solution for .NET HTTP clients. Currently built on HttpClient-based design, it fully benefits from .NET Core advantages and adopts System.Text.Json as the default serializer. With rich authentication options (Basic, OAuth 1/2, JWT, custom authentication), automatic JSON/XML serialization, support for diverse request body formats, middleware-like request/response processing, and comprehensive asynchronous support, it meets modern web API development requirements. Compared to HttpClient, RestSharp provides improved development efficiency through high-level APIs and rich feature integration.
Key Features
- Intuitive and Simple API: Easy to learn with minimal boilerplate code
- Automatic Serialization: JSON, XML, and custom format support
- Comprehensive Authentication Support: Complete support for OAuth, Basic, JWT, and custom authentication
- Flexible Request Construction: Diverse configuration of URL parameters, headers, and body
- File Operation Support: Multipart forms and file upload/download
- Configurable Defaults: Client-level timeout and header configuration
Pros and Cons
Pros
- Long-term track record in .NET ecosystem with rich community support
- Significantly more concise code writing and higher readability compared to HttpClient
- Massive improvement in development efficiency through automatic JSON/XML serialization
- Enterprise-level support through built-in support for diverse authentication methods
- Easy learning through comprehensive documentation and abundant implementation examples
- Customizable request/response processing through middleware patterns
Cons
- Low-level control may be limited as it's a thin wrapper around HttpClient
- Direct HttpClient usage may be preferable for large-scale high-performance applications
- Potential unexpected behavior in some edge cases
- May be insufficient for complex HTTP client requirements
- Slight increase in application size due to additional dependencies
- Additional packages required when using serializers other than System.Text.Json
Reference Pages
Code Examples
Installation and Basic Setup
<!-- Project file (.csproj) - Basic package -->
<PackageReference Include="RestSharp" Version="112.0.0" />
<!-- Enhanced JSON processing (when using Newtonsoft.Json) -->
<PackageReference Include="RestSharp" Version="112.0.0" />
<PackageReference Include="RestSharp.Serializers.NewtonsoftJson" Version="112.0.0" />
<!-- Enhanced OAuth authentication -->
<PackageReference Include="RestSharp" Version="112.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
// Basic namespace references
using RestSharp;
using RestSharp.Authenticators;
using System.Text.Json;
// Basic client initialization
var client = new RestClient("https://api.example.com");
// Advanced option configuration
var options = new RestClientOptions("https://api.example.com") {
ThrowOnAnyError = true, // Throw exception on errors
Timeout = TimeSpan.FromSeconds(30), // Timeout configuration
UserAgent = "MyApp/1.0", // User-Agent header
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true
};
var client = new RestClient(options);
// Default header configuration
client.AddDefaultHeader("Accept", "application/json");
client.AddDefaultHeader("Accept-Language", "en-US,ja-JP");
Basic Requests (GET/POST/PUT/DELETE)
using RestSharp;
// User data classes
public class User {
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
public class UserCreateRequest {
public string Name { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
public class ApiResponse<T> {
public bool Success { get; set; }
public T Data { get; set; }
public string Message { get; set; }
}
public async Task<List<User>> GetUsersExample() {
var client = new RestClient("https://api.example.com");
// Basic GET request (simple)
var response = await client.GetAsync<List<User>>("users");
return response;
// GET request with parameters (using RestRequest)
var request = new RestRequest("users")
.AddQueryParameter("page", "1")
.AddQueryParameter("limit", "10")
.AddQueryParameter("sort", "created_at");
var users = await client.GetAsync<List<User>>(request);
return users;
}
public async Task<User> CreateUserExample() {
var client = new RestClient("https://api.example.com");
var newUser = new UserCreateRequest {
Name = "John Doe",
Email = "[email protected]",
Age = 30
};
// POST request (JSON sending) - Simple version
var createdUser = await client.PostJsonAsync<UserCreateRequest, User>(
"users", newUser, CancellationToken.None
);
// POST request (detailed version)
var request = new RestRequest("users", Method.Post)
.AddJsonBody(newUser)
.AddHeader("Authorization", "Bearer your-token-here");
var response = await client.PostAsync<ApiResponse<User>>(request);
if (response.Success) {
Console.WriteLine($"User created successfully: ID={response.Data.Id}");
return response.Data;
} else {
throw new Exception($"User creation failed: {response.Message}");
}
}
public async Task<User> UpdateUserExample(int userId) {
var client = new RestClient("https://api.example.com");
var updateData = new {
Name = "Jane Doe",
Email = "[email protected]",
Age = 31
};
// PUT request
var request = new RestRequest($"users/{userId}", Method.Put)
.AddJsonBody(updateData)
.AddHeader("Authorization", "Bearer your-token-here");
var updatedUser = await client.PutAsync<User>(request);
return updatedUser;
}
public async Task<bool> DeleteUserExample(int userId) {
var client = new RestClient("https://api.example.com");
// DELETE request (simple version)
var response = await client.DeleteAsync($"users/{userId}");
// DELETE request (detailed version)
var request = new RestRequest($"users/{userId}", Method.Delete)
.AddHeader("Authorization", "Bearer your-token-here");
var deleteResponse = await client.DeleteAsync(request);
return deleteResponse.IsSuccessStatusCode;
}
// Response details verification
public async Task ResponseDetailsExample() {
var client = new RestClient("https://api.example.com");
var request = new RestRequest("users/1");
// Detailed response information retrieval with RestResponse<T>
var response = await client.ExecuteAsync<User>(request);
Console.WriteLine($"Status code: {response.StatusCode}");
Console.WriteLine($"Success: {response.IsSuccessful}");
Console.WriteLine($"Response time: {response.ResponseTime}");
Console.WriteLine($"Content type: {response.ContentType}");
Console.WriteLine($"Headers count: {response.Headers?.Count}");
if (response.Data != null) {
Console.WriteLine($"User: {response.Data.Name}");
}
if (response.ErrorException != null) {
Console.WriteLine($"Error: {response.ErrorException.Message}");
}
}
Authentication Configuration and Security
using RestSharp;
using RestSharp.Authenticators;
// Basic authentication (client-wide)
public RestClient CreateBasicAuthClient() {
var options = new RestClientOptions("https://api.example.com") {
Authenticator = new HttpBasicAuthenticator("username", "password")
};
return new RestClient(options);
}
// Bearer Token authentication (JWT)
public RestClient CreateBearerTokenClient(string token) {
var options = new RestClientOptions("https://api.example.com") {
Authenticator = new JwtAuthenticator(token)
};
return new RestClient(options);
}
// Per-request Basic authentication
public async Task<string> PerRequestBasicAuth() {
var client = new RestClient("https://api.example.com");
var request = new RestRequest("protected-resource") {
Authenticator = new HttpBasicAuthenticator("username", "password")
};
var response = await client.GetAsync(request);
return response.Content;
}
// OAuth1 authentication (Twitter API etc.)
public RestClient CreateOAuth1Client() {
var authenticator = OAuth1Authenticator.ForAccessToken(
consumerKey: "your-consumer-key",
consumerSecret: "your-consumer-secret",
accessToken: "your-access-token",
accessTokenSecret: "your-access-token-secret"
);
var options = new RestClientOptions("https://api.twitter.com/1.1") {
Authenticator = authenticator
};
return new RestClient(options);
}
// Custom authentication implementation
public class ApiKeyAuthenticator : AuthenticatorBase {
private readonly string _apiKey;
public ApiKeyAuthenticator(string apiKey) : base("") {
_apiKey = apiKey;
}
protected override ValueTask<Parameter> GetAuthenticationParameter(string accessToken) {
return new ValueTask<Parameter>(
new HeaderParameter("X-API-Key", _apiKey)
);
}
}
// OAuth2 flow implementation example
public class OAuth2Authenticator : AuthenticatorBase {
private readonly string _baseUrl;
private readonly string _clientId;
private readonly string _clientSecret;
public OAuth2Authenticator(string baseUrl, string clientId, string clientSecret)
: base("") {
_baseUrl = baseUrl;
_clientId = clientId;
_clientSecret = clientSecret;
}
protected override async ValueTask<Parameter> GetAuthenticationParameter(string accessToken) {
if (string.IsNullOrEmpty(Token)) {
Token = await GetAccessToken();
}
return new HeaderParameter("Authorization", $"Bearer {Token}");
}
private async Task<string> GetAccessToken() {
var options = new RestClientOptions(_baseUrl) {
Authenticator = new HttpBasicAuthenticator(_clientId, _clientSecret)
};
using var client = new RestClient(options);
var request = new RestRequest("oauth2/token")
.AddParameter("grant_type", "client_credentials");
var response = await client.PostAsync<TokenResponse>(request);
return $"{response.TokenType} {response.AccessToken}";
}
}
public class TokenResponse {
public string AccessToken { get; set; }
public string TokenType { get; set; }
public int ExpiresIn { get; set; }
}
// SSL/TLS configuration
public RestClient CreateSecureClient() {
var options = new RestClientOptions("https://api.example.com") {
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => {
// Implement proper certificate validation in production
return true;
},
ClientCertificates = new X509CertificateCollection {
// Add client certificates
new X509Certificate2("client-cert.p12", "password")
}
};
return new RestClient(options);
}
JSON/XML Serialization and Customization
using RestSharp;
using RestSharp.Serializers.NewtonsoftJson;
using Newtonsoft.Json;
using System.Text.Json;
// Default System.Text.Json configuration
public RestClient CreateJsonClient() {
var options = new JsonSerializerOptions {
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true,
PropertyNameCaseInsensitive = true
};
var clientOptions = new RestClientOptions("https://api.example.com");
var client = new RestClient(
clientOptions,
configureSerialization: s => s.UseSystemTextJson(options)
);
return client;
}
// Using Newtonsoft.Json
public RestClient CreateNewtonsoftJsonClient() {
var clientOptions = new RestClientOptions("https://api.example.com");
var client = new RestClient(
clientOptions,
configureSerialization: s => s.UseNewtonsoftJson(new JsonSerializerSettings {
NullValueHandling = NullValueHandling.Ignore,
DateFormatHandling = DateFormatHandling.IsoDateFormat,
ContractResolver = new CamelCasePropertyNamesContractResolver()
})
);
return client;
}
// Custom serializer implementation
public class CustomJsonSerializer : IRestSerializer {
private readonly JsonSerializerOptions _options;
public CustomJsonSerializer() {
_options = new JsonSerializerOptions {
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
}
public string Serialize(object obj) =>
obj == null ? null : JsonSerializer.Serialize(obj, _options);
public string Serialize(Parameter bodyParameter) =>
Serialize(bodyParameter.Value);
public T Deserialize<T>(RestResponse response) =>
JsonSerializer.Deserialize<T>(response.Content, _options);
public ContentType ContentType { get; set; } = ContentType.Json;
public ISerializer Serializer => this;
public IDeserializer Deserializer => this;
public DataFormat DataFormat => DataFormat.Json;
public string[] AcceptedContentTypes => ContentType.JsonAccept;
public SupportsContentType SupportsContentType =>
contentType => contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase);
}
// XML operation examples
public async Task XmlExamples() {
var client = new RestClient("https://api.example.com");
// Sending XML body
var xmlData = @"<?xml version='1.0' encoding='UTF-8'?>
<user>
<name>John Doe</name>
<email>[email protected]</email>
<age>30</age>
</user>";
var request = new RestRequest("users", Method.Post)
.AddXmlBody(xmlData)
.AddHeader("Content-Type", "application/xml");
var response = await client.PostAsync(request);
// Receiving XML response
var xmlRequest = new RestRequest("users/1");
var xmlResponse = await client.GetAsync(xmlRequest);
// Automatic XML to object conversion (requires custom deserializer)
var user = await client.GetAsync<User>(xmlRequest);
}
// Complex data format processing
public async Task ComplexDataHandling() {
var client = new RestClient("https://api.example.com");
// JSON body with custom content type
var request = new RestRequest("api/data", Method.Post);
request.AddJsonBody(new { message = "Hello", timestamp = DateTime.Now }, "application/vnd.api+json");
// Force serialization of strings
const string jsonString = @"{""key"": ""value""}";
request.AddJsonBody(jsonString, forceSerialize: true);
// Multiple parameter additions
request.AddQueryParameter("format", "json")
.AddQueryParameter("locale", "en-US")
.AddHeader("Accept-Encoding", "gzip, deflate")
.AddHeader("Cache-Control", "no-cache");
var response = await client.PostAsync(request);
}
Error Handling and Retry Functionality
using RestSharp;
using System.Net;
// Comprehensive error handling
public async Task<T> SafeApiCall<T>(RestClient client, RestRequest request) where T : class {
try {
var response = await client.ExecuteAsync<T>(request);
if (response.IsSuccessful && response.Data != null) {
return response.Data;
}
// Detailed processing by status code
switch (response.StatusCode) {
case HttpStatusCode.Unauthorized:
throw new UnauthorizedAccessException("Authentication is invalid. Please check your token.");
case HttpStatusCode.Forbidden:
throw new InvalidOperationException("Access denied.");
case HttpStatusCode.NotFound:
throw new ArgumentException("Specified resource not found.");
case HttpStatusCode.TooManyRequests:
throw new InvalidOperationException("API limit reached. Please wait before retrying.");
case HttpStatusCode.InternalServerError:
throw new Exception("Internal server error occurred.");
default:
if (response.ErrorException != null) {
throw new Exception($"API call error: {response.ErrorException.Message}", response.ErrorException);
}
throw new Exception($"Unexpected status: {response.StatusCode} - {response.Content}");
}
}
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException) {
throw new TimeoutException("Request timed out.", ex);
}
catch (TaskCanceledException ex) {
throw new OperationCanceledException("Request was cancelled.", ex);
}
catch (Exception ex) when (!(ex is UnauthorizedAccessException || ex is InvalidOperationException || ex is ArgumentException)) {
throw new Exception($"Network error or unexpected error: {ex.Message}", ex);
}
}
// Request with retry functionality
public async Task<T> RetryApiCall<T>(
RestClient client,
RestRequest request,
int maxRetries = 3,
TimeSpan? delay = null) where T : class {
var retryDelay = delay ?? TimeSpan.FromSeconds(1);
for (int attempt = 0; attempt <= maxRetries; attempt++) {
try {
var response = await client.ExecuteAsync<T>(request);
if (response.IsSuccessful) {
return response.Data;
}
// Don't retry for permanent errors
if (response.StatusCode == HttpStatusCode.Unauthorized ||
response.StatusCode == HttpStatusCode.Forbidden ||
response.StatusCode == HttpStatusCode.NotFound) {
throw new Exception($"Permanent error: {response.StatusCode}");
}
if (attempt == maxRetries) {
throw new Exception($"Maximum attempts reached: {response.StatusCode}");
}
Console.WriteLine($"Attempt {attempt + 1} failed ({response.StatusCode}). Retrying in {retryDelay.TotalSeconds} seconds...");
await Task.Delay(retryDelay);
retryDelay = TimeSpan.FromMilliseconds(retryDelay.TotalMilliseconds * 2); // Exponential backoff
}
catch (Exception ex) when (attempt < maxRetries) {
Console.WriteLine($"Attempt {attempt + 1} error: {ex.Message}. Retrying in {retryDelay.TotalSeconds} seconds...");
await Task.Delay(retryDelay);
retryDelay = TimeSpan.FromMilliseconds(retryDelay.TotalMilliseconds * 2);
}
}
throw new Exception("Unexpected error occurred in retry process.");
}
// Circuit breaker pattern implementation
public class CircuitBreakerApiClient {
private readonly RestClient _client;
private int _failureCount;
private DateTime _lastFailureTime;
private readonly int _failureThreshold;
private readonly TimeSpan _timeout;
private bool _isOpen;
public CircuitBreakerApiClient(RestClient client, int failureThreshold = 5, TimeSpan? timeout = null) {
_client = client;
_failureThreshold = failureThreshold;
_timeout = timeout ?? TimeSpan.FromMinutes(1);
}
public async Task<T> ExecuteAsync<T>(RestRequest request) where T : class {
if (_isOpen) {
if (DateTime.Now - _lastFailureTime > _timeout) {
_isOpen = false;
_failureCount = 0;
Console.WriteLine("Circuit breaker reset to half-open state.");
} else {
throw new Exception("Circuit breaker is open. Please wait before retrying.");
}
}
try {
var response = await _client.ExecuteAsync<T>(request);
if (response.IsSuccessful) {
_failureCount = 0; // Reset count on success
return response.Data;
} else {
await HandleFailure();
throw new Exception($"API request failed: {response.StatusCode}");
}
}
catch (Exception ex) {
await HandleFailure();
throw;
}
}
private async Task HandleFailure() {
_failureCount++;
_lastFailureTime = DateTime.Now;
if (_failureCount >= _failureThreshold) {
_isOpen = true;
Console.WriteLine($"Circuit breaker opened. Failure count: {_failureCount}");
}
}
}
// Usage example
public async Task ErrorHandlingExample() {
var client = new RestClient("https://api.example.com");
var request = new RestRequest("users/1");
// Basic safe call
try {
var user = await SafeApiCall<User>(client, request);
Console.WriteLine($"User retrieved successfully: {user.Name}");
}
catch (Exception ex) {
Console.WriteLine($"Error: {ex.Message}");
}
// Call with retry
try {
var userWithRetry = await RetryApiCall<User>(client, request, maxRetries: 3, TimeSpan.FromSeconds(2));
Console.WriteLine($"Retry successful: {userWithRetry.Name}");
}
catch (Exception ex) {
Console.WriteLine($"Retry failed: {ex.Message}");
}
// Using circuit breaker
var circuitBreaker = new CircuitBreakerApiClient(client);
try {
var userWithCircuitBreaker = await circuitBreaker.ExecuteAsync<User>(request);
Console.WriteLine($"Circuit breaker successful: {userWithCircuitBreaker.Name}");
}
catch (Exception ex) {
Console.WriteLine($"Circuit breaker error: {ex.Message}");
}
}
File Operations and Multipart
using RestSharp;
// File upload
public async Task<string> UploadFileExample() {
var client = new RestClient("https://api.example.com");
// Single file upload
var request = new RestRequest("upload", Method.Post)
.AddFile("file", @"C:\path\to\document.pdf", "application/pdf")
.AddParameter("description", "Important document")
.AddParameter("category", "documents")
.AddHeader("Authorization", "Bearer your-token");
var response = await client.PostAsync<UploadResponse>(request);
return response.FileId;
}
// Multipart form data
public async Task<string> MultipartFormExample() {
var client = new RestClient("https://api.example.com");
var request = new RestRequest("upload/multipart", Method.Post);
// Multiple files and additional data
request.AddFile("document", @"C:\files\document.pdf", "application/pdf");
request.AddFile("image", @"C:\files\image.jpg", "image/jpeg");
request.AddParameter("title", "Multiple file post");
request.AddParameter("tags", "document,image,upload");
request.AddParameter("public", "false");
// Custom file parameter
var fileBytes = await File.ReadAllBytesAsync(@"C:\files\data.json");
request.AddFile("data", fileBytes, "data.json", "application/json");
var response = await client.PostAsync<MultipartUploadResponse>(request);
return response.BatchId;
}
// File download
public async Task<byte[]> DownloadFileExample(string fileId) {
var client = new RestClient("https://api.example.com");
var request = new RestRequest($"files/{fileId}/download")
.AddHeader("Authorization", "Bearer your-token");
var response = await client.ExecuteAsync(request);
if (response.IsSuccessful) {
return response.RawBytes;
}
throw new Exception($"File download failed: {response.StatusCode}");
}
// Streaming download (large files)
public async Task DownloadLargeFileExample(string fileId, string localPath) {
var client = new RestClient("https://api.example.com");
var request = new RestRequest($"files/{fileId}/download")
.AddHeader("Authorization", "Bearer your-token");
// Download stream
var response = await client.DownloadDataAsync(request);
if (response != null) {
await File.WriteAllBytesAsync(localPath, response);
Console.WriteLine($"File saved to {localPath}.");
} else {
throw new Exception("File download failed.");
}
}
// Download with progress
public async Task DownloadWithProgressExample(string fileId, string localPath, IProgress<DownloadProgress> progress) {
var client = new RestClient("https://api.example.com");
var request = new RestRequest($"files/{fileId}/download")
.AddHeader("Authorization", "Bearer your-token");
// Direct HttpClient usage for progress tracking
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("Authorization", "Bearer your-token");
using var response = await httpClient.GetAsync($"https://api.example.com/files/{fileId}/download", HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
var totalBytes = response.Content.Headers.ContentLength ?? -1L;
using var contentStream = await response.Content.ReadAsStreamAsync();
using var fileStream = new FileStream(localPath, FileMode.Create, FileAccess.Write, FileShare.None);
var buffer = new byte[8192];
var totalBytesRead = 0L;
int bytesRead;
while ((bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length)) > 0) {
await fileStream.WriteAsync(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
if (totalBytes > 0) {
var progressPercentage = (double)totalBytesRead / totalBytes * 100;
progress?.Report(new DownloadProgress(totalBytesRead, totalBytes, progressPercentage));
}
}
}
public class UploadResponse {
public string FileId { get; set; }
public string FileName { get; set; }
public long FileSize { get; set; }
public string Url { get; set; }
}
public class MultipartUploadResponse {
public string BatchId { get; set; }
public List<UploadResponse> Files { get; set; }
}
public class DownloadProgress {
public long BytesReceived { get; }
public long TotalBytes { get; }
public double ProgressPercentage { get; }
public DownloadProgress(long bytesReceived, long totalBytes, double progressPercentage) {
BytesReceived = bytesReceived;
TotalBytes = totalBytes;
ProgressPercentage = progressPercentage;
}
}
Asynchronous Processing and Performance Optimization
using RestSharp;
using System.Collections.Concurrent;
// Concurrent request processing
public async Task<List<User>> GetUsersInParallel(List<int> userIds) {
var client = new RestClient("https://api.example.com");
var semaphore = new SemaphoreSlim(5, 5); // Limit concurrent execution to 5
var tasks = userIds.Select(async userId => {
await semaphore.WaitAsync();
try {
var request = new RestRequest($"users/{userId}")
.AddHeader("Authorization", "Bearer your-token");
return await client.GetAsync<User>(request);
}
finally {
semaphore.Release();
}
});
var users = await Task.WhenAll(tasks);
return users.Where(u => u != null).ToList();
}
// Batch processing
public async Task<List<T>> ProcessInBatches<T>(
List<int> ids,
Func<int, Task<T>> processor,
int batchSize = 10,
TimeSpan? delay = null) {
var results = new ConcurrentBag<T>();
var delayTime = delay ?? TimeSpan.FromMilliseconds(100);
for (int i = 0; i < ids.Count; i += batchSize) {
var batch = ids.Skip(i).Take(batchSize);
var batchTasks = batch.Select(processor);
var batchResults = await Task.WhenAll(batchTasks);
foreach (var result in batchResults.Where(r => r != null)) {
results.Add(result);
}
// Wait to reduce API load
if (i + batchSize < ids.Count) {
await Task.Delay(delayTime);
}
Console.WriteLine($"Batch {i / batchSize + 1} completed: {batchResults.Length} items processed");
}
return results.ToList();
}
// Pagination-aware full data retrieval
public async Task<List<T>> GetAllPaginatedData<T>(
RestClient client,
string baseEndpoint,
int pageSize = 100,
CancellationToken cancellationToken = default) {
var allData = new List<T>();
var page = 1;
bool hasMoreData;
do {
var request = new RestRequest(baseEndpoint)
.AddQueryParameter("page", page.ToString())
.AddQueryParameter("per_page", pageSize.ToString())
.AddHeader("Authorization", "Bearer your-token");
var response = await client.ExecuteAsync<PagedResponse<T>>(request, cancellationToken);
if (response.IsSuccessful && response.Data?.Items != null) {
allData.AddRange(response.Data.Items);
hasMoreData = response.Data.HasMore;
page++;
Console.WriteLine($"Page {page - 1} completed: {response.Data.Items.Count} items (Total: {allData.Count})");
// API load reduction
await Task.Delay(50, cancellationToken);
} else {
hasMoreData = false;
}
} while (hasMoreData && !cancellationToken.IsCancellationRequested);
Console.WriteLine($"All data retrieval completed: {allData.Count} items");
return allData;
}
// API client with caching functionality
public class CachedApiClient {
private readonly RestClient _client;
private readonly MemoryCache _cache;
private readonly TimeSpan _defaultCacheDuration;
public CachedApiClient(RestClient client, TimeSpan? cacheDuration = null) {
_client = client;
_cache = new MemoryCache(new MemoryCacheOptions {
SizeLimit = 1000 // Maximum number of cache entries
});
_defaultCacheDuration = cacheDuration ?? TimeSpan.FromMinutes(5);
}
public async Task<T> GetWithCacheAsync<T>(
string cacheKey,
RestRequest request,
TimeSpan? cacheDuration = null) where T : class {
if (_cache.TryGetValue(cacheKey, out T cachedResult)) {
Console.WriteLine($"Cache hit: {cacheKey}");
return cachedResult;
}
var response = await _client.ExecuteAsync<T>(request);
if (response.IsSuccessful && response.Data != null) {
var duration = cacheDuration ?? _defaultCacheDuration;
_cache.Set(cacheKey, response.Data, duration);
Console.WriteLine($"Cache saved: {cacheKey} (Expires: {duration})");
return response.Data;
}
throw new Exception($"Data retrieval failed: {response.StatusCode}");
}
public void ClearCache() {
_cache.Dispose();
Console.WriteLine("Cache cleared.");
}
}
// Usage example
public async Task PerformanceOptimizationExample() {
var client = new RestClient("https://api.example.com");
// Concurrent processing example
var userIds = Enumerable.Range(1, 20).ToList();
var users = await GetUsersInParallel(userIds);
Console.WriteLine($"Concurrent retrieval completed: {users.Count} users");
// Batch processing example
var batchResults = await ProcessInBatches(
userIds,
async id => await client.GetAsync<User>($"users/{id}"),
batchSize: 5,
delay: TimeSpan.FromMilliseconds(200)
);
Console.WriteLine($"Batch processing completed: {batchResults.Count} items");
// Pagination example
var allUsers = await GetAllPaginatedData<User>(client, "users", pageSize: 50);
Console.WriteLine($"All users retrieval completed: {allUsers.Count} items");
// Caching functionality example
var cachedClient = new CachedApiClient(client, TimeSpan.FromMinutes(10));
var request = new RestRequest("users/1");
var user1 = await cachedClient.GetWithCacheAsync<User>("user_1", request);
var user2 = await cachedClient.GetWithCacheAsync<User>("user_1", request); // Retrieved from cache
}
public class PagedResponse<T> {
public List<T> Items { get; set; }
public int CurrentPage { get; set; }
public int TotalPages { get; set; }
public int TotalItems { get; set; }
public bool HasMore { get; set; }
}