Resty
Go向けのシンプルなHTTPおよびRESTクライアント。net/httpの内部的な使用によりパフォーマンスを維持しながら、より使いやすいAPIを提供。自動JSON/XML解析、OAuth/Bearer認証、リトライ機能、デバッグ、ミドルウェアサポートを内蔵。
GitHub概要
トピックス
スター履歴
ライブラリ
Resty
概要
Restyは「Go言語向けのシンプルで機能豊富なHTTP/RESTクライアント」として開発された、GoエコシステムでネイティブのHTTPクライアントとして広く採用されている高レベルHTTPライブラリです。「シンプルさと豊富な機能の両立」を重視して設計され、net/httpパッケージ上に構築された強力な抽象化により、チェイン可能なAPI、自動リトライ機能、ミドルウェアシステム、多様な認証オプション、JSON/XML自動処理を提供。Go開発者にとって直感的で効率的なHTTP通信を実現し、REST API消費からWebサービス開発まで幅広いシナリオで活用されています。
詳細
Resty 2025年版(v3系)は、Go HTTPクライアントの成熟したソリューションとして継続的な進化を遂げています。Client、Request、Responseの3つの主要コンポーネントを中心とした設計により、HTTPリクエストのライフサイクル全体を包括的に管理。スレッドセーフな実装(sync.RWMutex使用)により複数ゴルーチンからの安全な利用を保証し、豊富なミドルウェアシステム、自動リトライ機能、OAuth/Basic/JWT認証、サーバーサイドイベント(SSE)対応、トレース機能、カスタムDNSリゾルバー等、エンタープライズグレードのHTTP通信要件を満たします。net/httpとの比較において、Restyは高レベルAPIによる開発効率向上と豊富な組み込み機能を提供します。
主な特徴
- チェイン可能なAPI: 直感的で流暢なインターフェースによる簡潔なコード記述
- 強力なミドルウェアシステム: リクエスト/レスポンス処理のカスタマイズ
- 自動リトライ機能: 設定可能なリトライ条件とバックオフ戦略
- 包括的認証サポート: Basic、Bearer、Digest、OAuth認証の組み込み対応
- 自動JSON/XML処理: 構造体の自動シリアライゼーション/デシリアライゼーション
- スレッドセーフ設計: 並行処理環境での安全な利用
メリット・デメリット
メリット
- net/httpと比較して大幅に簡潔で読みやすいコード記述
- 豊富な組み込み機能による高い開発効率と生産性向上
- 強力なミドルウェアシステムによる高度なカスタマイズ性
- 自動リトライとエラーハンドリングによる堅牢性
- 包括的な認証オプションによるエンタープライズレベル対応
- スレッドセーフ設計による並行処理での安全性
デメリット
- net/httpと比較してやや高い学習コストと抽象化レベル
- 外部依存関係の追加によるバイナリサイズの若干増加
- 非常に特殊なHTTP要件では低レベル制御が制限される場合
- 高度な機能を使わない場合はnet/httpで十分な場合もある
- バージョンアップ時のAPI変更による移行コスト
- デバッグ時に内部実装の理解が必要になる場合
参考ページ
書き方の例
インストールと基本セットアップ
// go.mod に追加
require resty.dev/v3
// またはGo 1.17+でのインストール
go get resty.dev/v3
package main
import (
"fmt"
"resty.dev/v3"
)
// 基本的なクライアント初期化
func main() {
// クライアント作成(リソース管理のため必ずdeferでClose)
client := resty.New()
defer client.Close()
// 基本設定
client.SetBaseURL("https://api.example.com").
SetTimeout(30 * time.Second).
SetRetryCount(3).
SetRetryWaitTime(1 * time.Second).
SetHeader("User-Agent", "MyApp/1.0").
SetHeader("Accept", "application/json")
}
// 高度なクライアント設定
func createAdvancedClient() *resty.Client {
client := resty.New()
// デバッグモード有効化
client.SetDebug(true)
// プロキシ設定
client.SetProxy("http://proxy.example.com:8080")
// SSL/TLS設定
client.SetRootCertificate("/path/to/root/cert.pem").
SetClientCertificates("/path/to/client.crt", "/path/to/client.key")
// リダイレクト設定
client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(5))
return client
}
// データ構造定義例
type User struct {
ID int `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
Name string `json:"name"`
}
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type LoginResponse struct {
Token string `json:"token"`
User User `json:"user"`
ExpiresIn int `json:"expires_in"`
}
type ErrorResponse struct {
Code int `json:"code"`
Message string `json:"message"`
Details string `json:"details"`
}
基本的なリクエスト(GET/POST/PUT/DELETE)
package main
import (
"fmt"
"strconv"
"time"
"resty.dev/v3"
)
func basicRequestsExample() {
client := resty.New()
defer client.Close()
// 基本的なGETリクエスト
resp, err := client.R().
Get("https://httpbin.org/get")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Status Code: %d\n", resp.StatusCode())
fmt.Printf("Status: %s\n", resp.Status())
fmt.Printf("Time: %v\n", resp.Time())
fmt.Printf("Body: %s\n", resp.String())
// クエリパラメータ付きGETリクエスト
resp, err = client.R().
SetQueryParams(map[string]string{
"page": "1",
"limit": "10",
"sort": "name",
"order": "asc",
"filter": "active",
}).
SetHeader("Accept", "application/json").
SetAuthToken("your-bearer-token").
Get("https://api.example.com/users")
fmt.Printf("GET with params: %s\n", resp.String())
// POSTリクエスト(JSON)
loginReq := LoginRequest{
Username: "testuser",
Password: "testpass",
}
var loginResp LoginResponse
var errResp ErrorResponse
resp, err = client.R().
SetBody(loginReq). // 自動的にJSONエンコード
SetResult(&loginResp). // 成功時のレスポンス先
SetError(&errResp). // エラー時のレスポンス先
Post("https://api.example.com/login")
if err != nil {
fmt.Printf("Request Error: %v\n", err)
return
}
if resp.IsSuccess() {
fmt.Printf("Login Success: Token=%s, User=%s\n",
loginResp.Token, loginResp.User.Name)
} else {
fmt.Printf("Login Failed: %s - %s\n",
errResp.Message, errResp.Details)
}
// PUTリクエスト(ユーザー更新)
userUpdate := User{
ID: 123,
Username: "updateduser",
Email: "[email protected]",
Name: "Updated Name",
}
resp, err = client.R().
SetBody(userUpdate).
SetAuthToken(loginResp.Token).
SetError(&errResp).
Put("https://api.example.com/users/123")
if resp.IsSuccess() {
fmt.Println("User updated successfully")
} else {
fmt.Printf("Update failed: %s\n", errResp.Message)
}
// DELETEリクエスト
resp, err = client.R().
SetAuthToken(loginResp.Token).
SetError(&errResp).
Delete("https://api.example.com/users/123")
if resp.IsSuccess() {
fmt.Println("User deleted successfully")
} else {
fmt.Printf("Delete failed: %s\n", errResp.Message)
}
// PATCHリクエスト(部分更新)
partialUpdate := map[string]interface{}{
"email": "[email protected]",
"name": "New Name",
}
resp, err = client.R().
SetBody(partialUpdate).
SetAuthToken(loginResp.Token).
SetError(&errResp).
Patch("https://api.example.com/users/123")
if resp.IsSuccess() {
fmt.Println("User partially updated")
}
}
// 様々なボディタイプの送信例
func requestBodyTypesExample() {
client := resty.New()
defer client.Close()
// 構造体をボディとして送信
user := User{Username: "test", Email: "[email protected]"}
client.R().SetBody(user).Post("https://api.example.com/users")
// Mapをボディとして送信
data := map[string]interface{}{
"name": "Test",
"value": 123,
}
client.R().SetBody(data).Post("https://api.example.com/data")
// 文字列をボディとして送信
jsonStr := `{"username":"test", "password":"pass"}`
client.R().
SetContentType("application/json").
SetBody(jsonStr).
Post("https://api.example.com/login")
// バイト配列をボディとして送信
jsonBytes := []byte(`{"key":"value"}`)
client.R().
SetContentType("application/json").
SetBody(jsonBytes).
Post("https://api.example.com/data")
// io.Readerをボディとして送信(v3ではストリーム化)
reader := strings.NewReader(`{"stream":"data"}`)
client.R().
SetContentType("application/json").
SetBody(reader).
Post("https://api.example.com/stream")
}
認証とセキュリティ
package main
import (
"context"
"golang.org/x/oauth2/clientcredentials"
"resty.dev/v3"
)
// Basic認証
func basicAuthExample() {
client := resty.New()
defer client.Close()
// クライアント全体でBasic認証を設定
client.SetBasicAuth("username", "password")
// 特定リクエストのみBasic認証
resp, err := client.R().
SetBasicAuth("username", "password").
Get("https://api.example.com/protected")
fmt.Println(err, resp)
}
// Bearer Token認証
func bearerTokenExample() {
client := resty.New()
defer client.Close()
// クライアント全体でBearer Token設定
client.SetAuthToken("your-jwt-token-here")
// 特定リクエストのみBearer Token
resp, err := client.R().
SetAuthToken("specific-token").
Get("https://api.example.com/users")
// カスタム認証スキーム
client.SetAuthScheme("Custom")
// 結果: Authorization: Custom your-token
// カスタムヘッダー名
client.SetHeaderAuthorizationKey("X-API-Key")
// 結果: X-API-Key: Bearer your-token
fmt.Println(err, resp)
}
// Digest認証
func digestAuthExample() {
client := resty.New()
defer client.Close()
// Digest認証設定(MD5, SHA-256, SHA-512対応)
client.SetDigestAuth("username", "password")
resp, err := client.R().
Get("https://api.example.com/digest-protected")
fmt.Println(err, resp)
}
// OAuth2 Client Credentials
func oauth2Example() {
// OAuth2 Client Credentials設定
clientCredConfig := &clientcredentials.Config{
ClientID: "your-client-id",
ClientSecret: "your-client-secret",
TokenURL: "https://auth.example.com/oauth2/token",
Scopes: []string{"read", "write"},
}
// 方法1: OAuth2クライアントを直接使用
credClient := clientCredConfig.Client(context.Background())
client := resty.NewWithClient(credClient)
defer client.Close()
// 方法2: カスタムミドルウェアでトークン管理
client2 := resty.New()
defer client2.Close()
client2.AddRequestMiddleware(func(c *resty.Client, req *resty.Request) error {
token, err := clientCredConfig.Token(req.Context())
if err != nil {
return err
}
req.SetAuthScheme(token.Type()).
SetAuthToken(token.AccessToken)
return nil
})
// 使用例
resp, err := client2.R().Get("https://api.example.com/protected")
fmt.Println(err, resp)
}
// カスタム認証ミドルウェア
func customAuthExample() {
client := resty.New()
defer client.Close()
client.AddRequestMiddleware(func(c *resty.Client, req *resty.Request) error {
// カスタム認証ロジック
apiKey := "your-api-key"
timestamp := time.Now().Unix()
signature := generateSignature(apiKey, timestamp) // 独自実装
req.SetHeader("X-API-Key", apiKey).
SetHeader("X-Timestamp", strconv.FormatInt(timestamp, 10)).
SetHeader("X-Signature", signature)
return nil
})
resp, err := client.R().Get("https://api.example.com/secure")
fmt.Println(err, resp)
}
func generateSignature(apiKey string, timestamp int64) string {
// 署名生成ロジック(例:HMAC-SHA256など)
return "generated-signature"
}
// SSL/TLS設定
func sslTlsExample() {
client := resty.New()
defer client.Close()
// ルート証明書設定
client.SetRootCertificate("/path/to/root/cert.pem")
// クライアント証明書設定
client.SetCertificates("/path/to/client.crt", "/path/to/client.key")
// 文字列から証明書設定
certStr := `-----BEGIN CERTIFICATE-----
... cert content ...
-----END CERTIFICATE-----`
client.SetClientRootCertificateFromString(certStr)
// 証明書ウォッチャー(証明書の自動更新)
certWatcherOpts := &resty.CertWatcherOptions{
PoolInterval: 12 * time.Hour,
}
client.SetClientRootCertificatesWatcher(
certWatcherOpts,
"/path/to/cert1.pem",
"/path/to/cert2.pem",
)
// プロキシ設定
client.SetProxy("http://proxy.example.com:8080")
// SOCKS5プロキシ
client.SetProxy("socks5://127.0.0.1:1080")
resp, err := client.R().Get("https://secure-api.example.com/data")
fmt.Println(err, resp)
}
ミドルウェアとカスタマイズ
package main
import (
"log"
"time"
"resty.dev/v3"
)
// リクエストミドルウェア
func requestMiddlewareExample() {
client := resty.New()
defer client.Close()
// ログミドルウェア
client.AddRequestMiddleware(func(c *resty.Client, req *resty.Request) error {
log.Printf("Request: %s %s", req.Method, req.URL)
return nil
})
// レート制限ミドルウェア
lastRequest := time.Now()
minInterval := 100 * time.Millisecond
client.AddRequestMiddleware(func(c *resty.Client, req *resty.Request) error {
elapsed := time.Since(lastRequest)
if elapsed < minInterval {
time.Sleep(minInterval - elapsed)
}
lastRequest = time.Now()
return nil
})
// リクエストID追加ミドルウェア
client.AddRequestMiddleware(func(c *resty.Client, req *resty.Request) error {
requestID := generateRequestID() // 独自実装
req.SetHeader("X-Request-ID", requestID)
return nil
})
// 複数ミドルウェアの順序指定
client.SetRequestMiddlewares(
customAuth1Middleware,
customAuth2Middleware,
resty.PrepareRequestMiddleware, // この後でRawRequestが利用可能
customLoggingMiddleware,
customMetricsMiddleware,
)
}
// レスポンスミドルウェア
func responseMiddlewareExample() {
client := resty.New()
defer client.Close()
// レスポンスログミドルウェア
client.AddResponseMiddleware(func(c *resty.Client, res *resty.Response) error {
log.Printf("Response: %d %s (%v)",
res.StatusCode(), res.Status(), res.Time())
return nil
})
// メトリクス収集ミドルウェア
client.AddResponseMiddleware(func(c *resty.Client, res *resty.Response) error {
// メトリクス送信
sendMetrics(res.Request.Method, res.StatusCode(), res.Time())
return nil
})
// エラー処理ミドルウェア
client.AddResponseMiddleware(func(c *resty.Client, res *resty.Response) error {
if res.StatusCode() >= 400 {
log.Printf("HTTP Error: %d - %s", res.StatusCode(), res.String())
// 必要に応じてエラーを下流に伝播
// return errors.New("HTTP error occurred")
}
return nil
})
// 複数レスポンスミドルウェアの順序指定
client.SetResponseMiddlewares(
customMetricsMiddleware,
customLoggingMiddleware,
resty.AutoParseResponseMiddleware, // この前ではボディは読み込まれない
customPostProcessMiddleware,
resty.SaveToFileResponseMiddleware, // Request.SetOutputFileName使用時
customCleanupMiddleware,
)
}
// カスタムコンテンツエンコーダー/デコーダー
func customEncodersExample() {
client := resty.New()
defer client.Close()
// カスタムエンコーダー追加
client.AddContentTypeEncoder("application/x-custom", func(w io.Writer, v any) error {
// カスタムエンコーディングロジック
data := fmt.Sprintf("CUSTOM:%v", v)
_, err := w.Write([]byte(data))
return err
})
// カスタムデコーダー追加
client.AddContentTypeDecoder("application/x-custom", func(r io.Reader, v any) error {
// カスタムデコーディングロジック
data, err := io.ReadAll(r)
if err != nil {
return err
}
// vに結果を設定(型アサーションが必要)
if ptr, ok := v.(*string); ok {
*ptr = string(data)
}
return nil
})
// カスタム圧縮解除器
client.AddContentDecompresser("custom-compress", func(r io.ReadCloser) (io.ReadCloser, error) {
// カスタム圧縮解除ロジック
return r, nil // 例:何もしない
})
}
// ヘルパー関数
func generateRequestID() string {
return fmt.Sprintf("req-%d", time.Now().UnixNano())
}
func sendMetrics(method string, statusCode int, duration time.Duration) {
// メトリクス送信ロジック
log.Printf("Metrics: %s %d %v", method, statusCode, duration)
}
func customAuth1Middleware(c *resty.Client, req *resty.Request) error {
// カスタム認証1
return nil
}
func customAuth2Middleware(c *resty.Client, req *resty.Request) error {
// カスタム認証2
return nil
}
func customLoggingMiddleware(c *resty.Client, res *resty.Response) error {
log.Printf("Custom log: %s", res.Status())
return nil
}
func customMetricsMiddleware(c *resty.Client, res *resty.Response) error {
// メトリクス処理
return nil
}
func customPostProcessMiddleware(c *resty.Client, res *resty.Response) error {
// レスポンス後処理
return nil
}
func customCleanupMiddleware(c *resty.Client, res *resty.Response) error {
// クリーンアップ処理
return nil
}
エラーハンドリングとリトライ機能
package main
import (
"errors"
"time"
"resty.dev/v3"
)
// 基本的なエラーハンドリング
func basicErrorHandlingExample() {
client := resty.New()
defer client.Close()
var result map[string]interface{}
var errorResp ErrorResponse
resp, err := client.R().
SetResult(&result).
SetError(&errorResp).
Get("https://api.example.com/data")
if err != nil {
fmt.Printf("Request Error: %v\n", err)
return
}
if resp.IsSuccess() {
fmt.Printf("Success: %+v\n", result)
} else if resp.IsError() {
fmt.Printf("API Error: %d - %s\n",
resp.StatusCode(), errorResp.Message)
} else {
fmt.Printf("Unexpected Status: %d\n", resp.StatusCode())
}
}
// 自動リトライ設定
func retryConfigurationExample() {
client := resty.New()
defer client.Close()
// クライアントレベルのリトライ設定
client.SetRetryCount(3).
SetRetryWaitTime(1 * time.Second).
SetRetryMaxWaitTime(10 * time.Second)
// カスタムリトライ条件追加
client.AddRetryConditions(
func(res *resty.Response, err error) bool {
// ネットワークエラーの場合はリトライ
if err != nil {
return true
}
// 5xxエラーの場合はリトライ
if res.StatusCode() >= 500 {
return true
}
// レート制限の場合はリトライ
if res.StatusCode() == 429 {
return true
}
return false
},
func(res *resty.Response, err error) bool {
// タイムアウトの場合はリトライ
if err != nil && strings.Contains(err.Error(), "timeout") {
return true
}
return false
},
)
// リトライフック(各リトライ間で実行)
client.AddRetryHooks(
func(res *resty.Response, err error) {
fmt.Printf("Retry attempt for: %s\n", res.Request.URL)
},
func(res *resty.Response, err error) {
// メトリクス送信など
sendRetryMetrics(res, err)
},
)
}
// リクエストレベルのリトライ設定
func requestLevelRetryExample() {
client := resty.New()
defer client.Close()
resp, err := client.R().
AddRetryConditions(
func(res *resty.Response, err error) bool {
// このリクエスト固有のリトライ条件
return res != nil && res.StatusCode() == 503
},
).
AddRetryHooks(
func(res *resty.Response, err error) {
// このリクエスト固有のリトライフック
fmt.Println("Request-specific retry hook")
},
).
SetRetryCount(5). // このリクエストのみ5回リトライ
Get("https://api.example.com/unstable")
fmt.Println(err, resp)
}
// 包括的なエラーハンドリング関数
func comprehensiveErrorHandling() {
client := resty.New()
defer client.Close()
result, err := safeAPICall(client, "https://api.example.com/data")
if err != nil {
fmt.Printf("API Call Failed: %v\n", err)
return
}
fmt.Printf("API Call Success: %+v\n", result)
}
func safeAPICall(client *resty.Client, url string) (map[string]interface{}, error) {
var result map[string]interface{}
var errorResp ErrorResponse
resp, err := client.R().
SetResult(&result).
SetError(&errorResp).
SetRetryCount(3).
AddRetryConditions(
func(res *resty.Response, err error) bool {
if err != nil {
return true // ネットワークエラーはリトライ
}
return res.StatusCode() >= 500 || res.StatusCode() == 429
},
).
Get(url)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
switch {
case resp.IsSuccess():
return result, nil
case resp.StatusCode() == 401:
return nil, errors.New("authentication failed")
case resp.StatusCode() == 403:
return nil, errors.New("access denied")
case resp.StatusCode() == 404:
return nil, errors.New("resource not found")
case resp.StatusCode() == 429:
return nil, errors.New("rate limit exceeded")
case resp.IsError():
return nil, fmt.Errorf("API error: %d - %s", resp.StatusCode(), errorResp.Message)
default:
return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode())
}
}
// 回路ブレーカーパターン実装
type CircuitBreaker struct {
failures int
lastFailure time.Time
threshold int
timeout time.Duration
isOpen bool
}
func NewCircuitBreaker(threshold int, timeout time.Duration) *CircuitBreaker {
return &CircuitBreaker{
threshold: threshold,
timeout: timeout,
}
}
func (cb *CircuitBreaker) Call(client *resty.Client, url string) (*resty.Response, error) {
if cb.isOpen {
if time.Since(cb.lastFailure) > cb.timeout {
cb.isOpen = false
cb.failures = 0
} else {
return nil, errors.New("circuit breaker is open")
}
}
resp, err := client.R().Get(url)
if err != nil || resp.StatusCode() >= 500 {
cb.failures++
cb.lastFailure = time.Now()
if cb.failures >= cb.threshold {
cb.isOpen = true
}
if err != nil {
return nil, err
}
return resp, fmt.Errorf("server error: %d", resp.StatusCode())
}
cb.failures = 0
return resp, nil
}
func sendRetryMetrics(res *resty.Response, err error) {
// リトライメトリクスの送信
fmt.Printf("Retry metrics: %v, %v\n", res, err)
}
高度な機能とパフォーマンス最適化
package main
import (
"context"
"net"
"time"
"resty.dev/v3"
)
// フォームデータとファイルアップロード
func formsAndFilesExample() {
client := resty.New()
defer client.Close()
// フォームデータ送信
resp, err := client.R().
SetFormData(map[string]string{
"username": "myuser",
"password": "mypass",
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]",
}).
Post("https://api.example.com/register")
fmt.Println(err, resp)
// ファイルアップロード
resp, err = client.R().
SetFile("document", "/path/to/file.pdf").
SetFormData(map[string]string{
"title": "Important Document",
"description": "This is a test upload",
}).
Post("https://api.example.com/upload")
fmt.Println(err, resp)
// マルチパートファイルアップロード
resp, err = client.R().
SetFiles(map[string]string{
"file1": "/path/to/file1.txt",
"file2": "/path/to/file2.jpg",
"file3": "/path/to/file3.pdf",
}).
SetFormData(map[string]string{
"batch_name": "document_batch_1",
"category": "legal_documents",
}).
Post("https://api.example.com/batch-upload")
fmt.Println(err, resp)
}
// カスタムDNSリゾルバー
func customDNSExample() {
// カスタムDNSリゾルバー設定
dialer := &net.Dialer{
Resolver: &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{
Timeout: 20 * time.Second,
}
return d.DialContext(ctx, "udp", "8.8.8.8:53") // Google DNS
},
},
}
client := resty.NewWithDialer(dialer)
defer client.Close()
resp, err := client.R().Get("https://api.example.com/data")
fmt.Println(err, resp)
}
// サーバーサイドイベント(SSE)
func serverSentEventsExample() {
// SSEクライアント作成
es := resty.NewEventSource().
SetURL("https://api.example.com/events").
SetHeader("Authorization", "Bearer your-token").
OnMessage(func(e interface{}) {
event := e.(*resty.Event)
fmt.Printf("Event: %s, Data: %s\n", event.Event, event.Data)
}, nil).
OnError(func(err error) {
fmt.Printf("SSE Error: %v\n", err)
}).
OnOpen(func() {
fmt.Println("SSE Connection opened")
}).
OnClose(func() {
fmt.Println("SSE Connection closed")
})
// SSE接続開始
err := es.Get()
if err != nil {
fmt.Printf("SSE Failed: %v\n", err)
}
}
// デバッグとトレース
func debugAndTraceExample() {
client := resty.New()
defer client.Close()
// デバッグモード有効化
client.SetDebug(true)
// トレース有効化でリクエスト実行
resp, err := client.R().
EnableTrace().
Get("https://httpbin.org/get")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
// トレース情報取得
traceInfo := resp.Request.TraceInfo()
fmt.Printf("DNS Lookup: %v\n", traceInfo.DNSLookup)
fmt.Printf("Connect Time: %v\n", traceInfo.ConnTime)
fmt.Printf("TLS Handshake: %v\n", traceInfo.TLSHandshake)
fmt.Printf("Server Time: %v\n", traceInfo.ServerTime)
fmt.Printf("Response Time: %v\n", traceInfo.ResponseTime)
fmt.Printf("Total Time: %v\n", traceInfo.TotalTime)
fmt.Printf("Is Connection Reused: %v\n", traceInfo.IsConnReused)
fmt.Printf("Remote Address: %v\n", traceInfo.RemoteAddr)
// cURLコマンド生成
curlCmd := resp.Request.CurlCmd()
fmt.Printf("Equivalent cURL command:\n%s\n", curlCmd)
}
// 生のHTTPレスポンス処理
func rawResponseExample() {
client := resty.New()
defer client.Close()
// 自動パースを無効化して生レスポンスを取得
resp, err := client.R().
SetDoNotParseResponse(true).
Get("https://httpbin.org/json")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer resp.Body.Close() // 必ずクローズ
// 生レスポンスボディを読み込み
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Read Error: %v\n", err)
return
}
fmt.Printf("Raw Response: %s\n", string(bodyBytes))
}
// レスポンス内容の強制的な型指定
func forceContentTypeExample() {
client := resty.New()
defer client.Close()
var result map[string]interface{}
// Content-Typeに関係なくJSONとして解析を強制
resp, err := client.R().
SetResult(&result).
SetForceResponseContentType("application/json").
Get("https://api.example.com/data-with-wrong-content-type")
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
if resp.IsSuccess() {
fmt.Printf("Forced JSON parsing result: %+v\n", result)
}
}
// 並行リクエスト処理
func concurrentRequestsExample() {
client := resty.New()
defer client.Close()
urls := []string{
"https://httpbin.org/get?id=1",
"https://httpbin.org/get?id=2",
"https://httpbin.org/get?id=3",
"https://httpbin.org/get?id=4",
"https://httpbin.org/get?id=5",
}
type Result struct {
URL string
StatusCode int
Body string
Error error
}
results := make(chan Result, len(urls))
// 並行リクエスト実行
for _, url := range urls {
go func(u string) {
resp, err := client.R().Get(u)
results <- Result{
URL: u,
StatusCode: resp.StatusCode(),
Body: resp.String(),
Error: err,
}
}(url)
}
// 結果収集
for i := 0; i < len(urls); i++ {
result := <-results
if result.Error != nil {
fmt.Printf("Error for %s: %v\n", result.URL, result.Error)
} else {
fmt.Printf("Success for %s: %d\n", result.URL, result.StatusCode)
}
}
}