RestSharp
.NET向けのシンプルなRESTおよびHTTP APIクライアント。直感的なAPIデザインで迅速な開発を支援。自動シリアライゼーション、認証(OAuth、JWT、Basic)、リクエスト/レスポンス変換、エラーハンドリング機能を内蔵。バージョン107以降HttpClientベース。
GitHub概要
restsharp/RestSharp
Simple REST and HTTP API Client for .NET
トピックス
スター履歴
ライブラリ
RestSharp
概要
RestSharpは「.NET向けのシンプルなRESTおよびHTTP APIクライアント」として開発された、.NETエコシステムで長年にわたって信頼され続けている高レベルHTTPクライアントライブラリです。「使いやすさとシンプルさ」を重視して設計され、HttpClientの薄いラッパーとして動作し、JSON/XML自動シリアライゼーション、多様な認証方式、非同期処理、ファイルアップロード/ダウンロードなど、RESTful API消費に必要な包括的機能を提供。.NET開発者にとって直感的で効率的なHTTP通信ライブラリとして、企業レベルから個人プロジェクトまで幅広く採用されています。
詳細
RestSharp 2025年版は、.NET HTTPクライアントの確立されたソリューションとして進化を続けています。現在はHttpClientをベースとした設計により、.NET Coreの恩恵を完全に享受し、System.Text.Jsonをデフォルトシリアライザーとして採用。豊富な認証オプション(Basic、OAuth 1/2、JWT、カスタム認証)、自動JSON/XMLシリアライゼーション、多様なリクエストボディ形式対応、ミドルウェアライクなリクエスト/レスポンス処理、包括的な非同期サポートにより、現代的なWeb API開発要件を満たします。HttpClientとの比較において、RestSharpは高レベルAPIによる開発効率向上と豊富な機能統合を提供します。
主な特徴
- 直感的でシンプルなAPI: 最小限のボイラープレートコードで学習容易
- 自動シリアライゼーション: JSON、XML、カスタムフォーマット対応
- 包括的認証サポート: OAuth、Basic、JWT、カスタム認証の完全対応
- 柔軟なリクエスト構築: URLパラメータ、ヘッダー、ボディの多様な設定
- ファイル操作対応: マルチパートフォームとファイルアップロード/ダウンロード
- 設定可能なデフォルト: クライアントレベルでのタイムアウト、ヘッダー設定
メリット・デメリット
メリット
- .NETエコシステムでの長期実績と豊富なコミュニティサポート
- HttpClientと比較して大幅に簡潔なコード記述と高い可読性
- 自動JSON/XMLシリアライゼーションによる開発効率の大幅向上
- 多様な認証方式の組み込みサポートによる企業レベル対応
- 充実したドキュメントと豊富な実装例による学習容易性
- ミドルウェアパターンによるリクエスト/レスポンス処理のカスタマイズ性
デメリット
- HttpClientの薄いラッパーのため、低レベル制御が制限される場合がある
- 大規模な高性能アプリケーションではHttpClient直接利用が望ましい場合
- 一部のエッジケースで期待と異なる動作をする可能性
- 複雑なHTTPクライアント要件には機能不足の場合がある
- 依存関係追加によるアプリケーションサイズの若干増加
- System.Text.Json以外のシリアライザー使用時は追加パッケージ必要
参考ページ
書き方の例
インストールと基本セットアップ
<!-- プロジェクトファイル (.csproj) - 基本パッケージ -->
<PackageReference Include="RestSharp" Version="112.0.0" />
<!-- JSON処理強化(Newtonsoft.Json使用の場合)-->
<PackageReference Include="RestSharp" Version="112.0.0" />
<PackageReference Include="RestSharp.Serializers.NewtonsoftJson" Version="112.0.0" />
<!-- OAuth認証強化 -->
<PackageReference Include="RestSharp" Version="112.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
// 基本的なnamespace参照
using RestSharp;
using RestSharp.Authenticators;
using System.Text.Json;
// 基本的なクライアント初期化
var client = new RestClient("https://api.example.com");
// 高度なオプション設定
var options = new RestClientOptions("https://api.example.com") {
ThrowOnAnyError = true, // エラー時に例外をスロー
Timeout = TimeSpan.FromSeconds(30), // タイムアウト設定
UserAgent = "MyApp/1.0", // User-Agentヘッダー
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true
};
var client = new RestClient(options);
// デフォルトヘッダーの設定
client.AddDefaultHeader("Accept", "application/json");
client.AddDefaultHeader("Accept-Language", "ja-JP,en-US");
基本的なリクエスト(GET/POST/PUT/DELETE)
using RestSharp;
// ユーザー用のデータクラス
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");
// 基本的なGETリクエスト(シンプル)
var response = await client.GetAsync<List<User>>("users");
return response;
// パラメータ付きGETリクエスト(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 = "田中太郎",
Email = "[email protected]",
Age = 30
};
// POSTリクエスト(JSON送信)- シンプル版
var createdUser = await client.PostJsonAsync<UserCreateRequest, User>(
"users", newUser, CancellationToken.None
);
// POSTリクエスト(詳細版)
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($"ユーザー作成成功: ID={response.Data.Id}");
return response.Data;
} else {
throw new Exception($"ユーザー作成失敗: {response.Message}");
}
}
public async Task<User> UpdateUserExample(int userId) {
var client = new RestClient("https://api.example.com");
var updateData = new {
Name = "田中次郎",
Email = "[email protected]",
Age = 31
};
// PUTリクエスト
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リクエスト(シンプル版)
var response = await client.DeleteAsync($"users/{userId}");
// DELETEリクエスト(詳細版)
var request = new RestRequest($"users/{userId}", Method.Delete)
.AddHeader("Authorization", "Bearer your-token-here");
var deleteResponse = await client.DeleteAsync(request);
return deleteResponse.IsSuccessStatusCode;
}
// レスポンス詳細の確認
public async Task ResponseDetailsExample() {
var client = new RestClient("https://api.example.com");
var request = new RestRequest("users/1");
// RestResponse<T>での詳細な応答情報取得
var response = await client.ExecuteAsync<User>(request);
Console.WriteLine($"ステータスコード: {response.StatusCode}");
Console.WriteLine($"成功: {response.IsSuccessful}");
Console.WriteLine($"レスポンス時間: {response.ResponseTime}");
Console.WriteLine($"コンテンツタイプ: {response.ContentType}");
Console.WriteLine($"ヘッダー数: {response.Headers?.Count}");
if (response.Data != null) {
Console.WriteLine($"ユーザー: {response.Data.Name}");
}
if (response.ErrorException != null) {
Console.WriteLine($"エラー: {response.ErrorException.Message}");
}
}
認証設定とセキュリティ
using RestSharp;
using RestSharp.Authenticators;
// Basic認証(クライアント全体)
public RestClient CreateBasicAuthClient() {
var options = new RestClientOptions("https://api.example.com") {
Authenticator = new HttpBasicAuthenticator("username", "password")
};
return new RestClient(options);
}
// Bearer Token認証(JWT)
public RestClient CreateBearerTokenClient(string token) {
var options = new RestClientOptions("https://api.example.com") {
Authenticator = new JwtAuthenticator(token)
};
return new RestClient(options);
}
// リクエスト毎のBasic認証
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認証(Twitter API等)
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);
}
// カスタム認証実装
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フロー実装例
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設定
public RestClient CreateSecureClient() {
var options = new RestClientOptions("https://api.example.com") {
RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => {
// 本番環境では適切な証明書検証を実装
return true;
},
ClientCertificates = new X509CertificateCollection {
// クライアント証明書の追加
new X509Certificate2("client-cert.p12", "password")
}
};
return new RestClient(options);
}
JSON/XMLシリアライゼーションとカスタマイズ
using RestSharp;
using RestSharp.Serializers.NewtonsoftJson;
using Newtonsoft.Json;
using System.Text.Json;
// デフォルトのSystem.Text.Json設定
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;
}
// 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;
}
// カスタムシリアライザー実装
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操作の例
public async Task XmlExamples() {
var client = new RestClient("https://api.example.com");
// XMLボディの送信
var xmlData = @"<?xml version='1.0' encoding='UTF-8'?>
<user>
<name>田中太郎</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);
// XMLレスポンスの受信
var xmlRequest = new RestRequest("users/1");
var xmlResponse = await client.GetAsync(xmlRequest);
// XMLをオブジェクトに自動変換(カスタムデシリアライザーが必要)
var user = await client.GetAsync<User>(xmlRequest);
}
// 複合データ形式の処理
public async Task ComplexDataHandling() {
var client = new RestClient("https://api.example.com");
// JSONボディにカスタムコンテンツタイプ
var request = new RestRequest("api/data", Method.Post);
request.AddJsonBody(new { message = "Hello", timestamp = DateTime.Now }, "application/vnd.api+json");
// 文字列の強制シリアライゼーション
const string jsonString = @"{""key"": ""value""}";
request.AddJsonBody(jsonString, forceSerialize: true);
// パラメータの複数追加
request.AddQueryParameter("format", "json")
.AddQueryParameter("locale", "ja-JP")
.AddHeader("Accept-Encoding", "gzip, deflate")
.AddHeader("Cache-Control", "no-cache");
var response = await client.PostAsync(request);
}
エラーハンドリングとリトライ機能
using RestSharp;
using System.Net;
// 包括的なエラーハンドリング
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;
}
// ステータスコード別の詳細処理
switch (response.StatusCode) {
case HttpStatusCode.Unauthorized:
throw new UnauthorizedAccessException("認証が無効です。トークンを確認してください。");
case HttpStatusCode.Forbidden:
throw new InvalidOperationException("アクセス権限がありません。");
case HttpStatusCode.NotFound:
throw new ArgumentException("指定されたリソースが見つかりません。");
case HttpStatusCode.TooManyRequests:
throw new InvalidOperationException("API制限に達しました。しばらく待ってから再試行してください。");
case HttpStatusCode.InternalServerError:
throw new Exception("サーバー内部エラーが発生しました。");
default:
if (response.ErrorException != null) {
throw new Exception($"API呼び出しエラー: {response.ErrorException.Message}", response.ErrorException);
}
throw new Exception($"予期しないステータス: {response.StatusCode} - {response.Content}");
}
}
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException) {
throw new TimeoutException("リクエストがタイムアウトしました。", ex);
}
catch (TaskCanceledException ex) {
throw new OperationCanceledException("リクエストがキャンセルされました。", ex);
}
catch (Exception ex) when (!(ex is UnauthorizedAccessException || ex is InvalidOperationException || ex is ArgumentException)) {
throw new Exception($"ネットワークエラーまたは予期しないエラー: {ex.Message}", ex);
}
}
// リトライ機能付きリクエスト
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;
}
// 永続的なエラーの場合はリトライしない
if (response.StatusCode == HttpStatusCode.Unauthorized ||
response.StatusCode == HttpStatusCode.Forbidden ||
response.StatusCode == HttpStatusCode.NotFound) {
throw new Exception($"永続的エラー: {response.StatusCode}");
}
if (attempt == maxRetries) {
throw new Exception($"最大試行回数に達しました: {response.StatusCode}");
}
Console.WriteLine($"試行 {attempt + 1} 失敗 ({response.StatusCode}). {retryDelay.TotalSeconds}秒後に再試行...");
await Task.Delay(retryDelay);
retryDelay = TimeSpan.FromMilliseconds(retryDelay.TotalMilliseconds * 2); // 指数バックオフ
}
catch (Exception ex) when (attempt < maxRetries) {
Console.WriteLine($"試行 {attempt + 1} でエラー: {ex.Message}. {retryDelay.TotalSeconds}秒後に再試行...");
await Task.Delay(retryDelay);
retryDelay = TimeSpan.FromMilliseconds(retryDelay.TotalMilliseconds * 2);
}
}
throw new Exception("リトライ処理で予期しないエラーが発生しました。");
}
// 回路ブレーカーパターン実装
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("回路ブレーカーを半開状態にリセットしました。");
} else {
throw new Exception("回路ブレーカーが開いています。しばらく待ってから再試行してください。");
}
}
try {
var response = await _client.ExecuteAsync<T>(request);
if (response.IsSuccessful) {
_failureCount = 0; // 成功時はカウントリセット
return response.Data;
} else {
await HandleFailure();
throw new Exception($"APIリクエスト失敗: {response.StatusCode}");
}
}
catch (Exception ex) {
await HandleFailure();
throw;
}
}
private async Task HandleFailure() {
_failureCount++;
_lastFailureTime = DateTime.Now;
if (_failureCount >= _failureThreshold) {
_isOpen = true;
Console.WriteLine($"回路ブレーカーが開きました。失敗回数: {_failureCount}");
}
}
}
// 使用例
public async Task ErrorHandlingExample() {
var client = new RestClient("https://api.example.com");
var request = new RestRequest("users/1");
// 基本的な安全な呼び出し
try {
var user = await SafeApiCall<User>(client, request);
Console.WriteLine($"ユーザー取得成功: {user.Name}");
}
catch (Exception ex) {
Console.WriteLine($"エラー: {ex.Message}");
}
// リトライ付き呼び出し
try {
var userWithRetry = await RetryApiCall<User>(client, request, maxRetries: 3, TimeSpan.FromSeconds(2));
Console.WriteLine($"リトライ成功: {userWithRetry.Name}");
}
catch (Exception ex) {
Console.WriteLine($"リトライ失敗: {ex.Message}");
}
// 回路ブレーカー使用
var circuitBreaker = new CircuitBreakerApiClient(client);
try {
var userWithCircuitBreaker = await circuitBreaker.ExecuteAsync<User>(request);
Console.WriteLine($"回路ブレーカー成功: {userWithCircuitBreaker.Name}");
}
catch (Exception ex) {
Console.WriteLine($"回路ブレーカーエラー: {ex.Message}");
}
}
ファイル操作とマルチパート
using RestSharp;
// ファイルアップロード
public async Task<string> UploadFileExample() {
var client = new RestClient("https://api.example.com");
// 単一ファイルアップロード
var request = new RestRequest("upload", Method.Post)
.AddFile("file", @"C:\path\to\document.pdf", "application/pdf")
.AddParameter("description", "重要な文書")
.AddParameter("category", "documents")
.AddHeader("Authorization", "Bearer your-token");
var response = await client.PostAsync<UploadResponse>(request);
return response.FileId;
}
// マルチパートフォームデータ
public async Task<string> MultipartFormExample() {
var client = new RestClient("https://api.example.com");
var request = new RestRequest("upload/multipart", Method.Post);
// 複数ファイルと追加データ
request.AddFile("document", @"C:\files\document.pdf", "application/pdf");
request.AddFile("image", @"C:\files\image.jpg", "image/jpeg");
request.AddParameter("title", "複数ファイル投稿");
request.AddParameter("tags", "document,image,upload");
request.AddParameter("public", "false");
// カスタムファイルパラメータ
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;
}
// ファイルダウンロード
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($"ファイルダウンロード失敗: {response.StatusCode}");
}
// ストリーミングダウンロード(大容量ファイル)
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");
// ダウンロード用のストリーム
var response = await client.DownloadDataAsync(request);
if (response != null) {
await File.WriteAllBytesAsync(localPath, response);
Console.WriteLine($"ファイルを {localPath} に保存しました。");
} else {
throw new Exception("ファイルダウンロードに失敗しました。");
}
}
// 進行状況付きダウンロード
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");
// HttpClientを直接使用した進行状況付きダウンロード
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;
}
}
非同期処理とパフォーマンス最適化
using RestSharp;
using System.Collections.Concurrent;
// 並行リクエスト処理
public async Task<List<User>> GetUsersInParallel(List<int> userIds) {
var client = new RestClient("https://api.example.com");
var semaphore = new SemaphoreSlim(5, 5); // 同時実行数を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();
}
// バッチ処理
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);
}
// API負荷軽減のための待機
if (i + batchSize < ids.Count) {
await Task.Delay(delayTime);
}
Console.WriteLine($"バッチ {i / batchSize + 1} 完了: {batchResults.Length} 件処理");
}
return results.ToList();
}
// ページネーション対応の全データ取得
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 - 1} 取得完了: {response.Data.Items.Count} 件 (総計: {allData.Count} 件)");
// API負荷軽減
await Task.Delay(50, cancellationToken);
} else {
hasMoreData = false;
}
} while (hasMoreData && !cancellationToken.IsCancellationRequested);
Console.WriteLine($"全データ取得完了: {allData.Count} 件");
return allData;
}
// キャッシュ機能付きAPIクライアント
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 // キャッシュエントリの最大数
});
_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($"キャッシュヒット: {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($"キャッシュ保存: {cacheKey} (有効期限: {duration})");
return response.Data;
}
throw new Exception($"データ取得失敗: {response.StatusCode}");
}
public void ClearCache() {
_cache.Dispose();
Console.WriteLine("キャッシュをクリアしました。");
}
}
// 使用例
public async Task PerformanceOptimizationExample() {
var client = new RestClient("https://api.example.com");
// 並行処理例
var userIds = Enumerable.Range(1, 20).ToList();
var users = await GetUsersInParallel(userIds);
Console.WriteLine($"並行取得完了: {users.Count} ユーザー");
// バッチ処理例
var batchResults = await ProcessInBatches(
userIds,
async id => await client.GetAsync<User>($"users/{id}"),
batchSize: 5,
delay: TimeSpan.FromMilliseconds(200)
);
Console.WriteLine($"バッチ処理完了: {batchResults.Count} 件");
// ページネーション例
var allUsers = await GetAllPaginatedData<User>(client, "users", pageSize: 50);
Console.WriteLine($"全ユーザー取得完了: {allUsers.Count} 件");
// キャッシュ機能例
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); // キャッシュから取得
}
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; }
}