RestSharp

.NET向けのシンプルなRESTおよびHTTP APIクライアント。直感的なAPIデザインで迅速な開発を支援。自動シリアライゼーション、認証(OAuth、JWT、Basic)、リクエスト/レスポンス変換、エラーハンドリング機能を内蔵。バージョン107以降HttpClientベース。

HTTPクライアント.NETREST認証JSONXML

GitHub概要

restsharp/RestSharp

Simple REST and HTTP API Client for .NET

スター9,788
ウォッチ418
フォーク2,337
作成日:2009年11月16日
言語:C#
ライセンス:Apache License 2.0

トピックス

なし

スター履歴

restsharp/RestSharp Star History
データ取得日時: 2025/10/22 09:55

ライブラリ

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; }
}