HTTParty

Ruby向けの使いやすいHTTPクライアントgem。「HTTPを楽しく」をコンセプトに、直感的で分かりやすい構文を提供。Net::HTTPベースで構築され、自動JSON/XML解析、認証、Cookie処理機能を内蔵。Ruby初心者やサイドプロジェクトで人気。

HTTPクライアントRubyシンプルAPIJSON解析XML解析

GitHub概要

jnunemaker/httparty

:tada: Makes http fun again!

スター5,878
ウォッチ62
フォーク966
作成日:2008年7月28日
言語:Ruby
ライセンス:MIT License

トピックス

httphttpartyruby

スター履歴

jnunemaker/httparty Star History
データ取得日時: 2025/10/22 09:54

ライブラリ

HTTParty

概要

HTTPartyは「HTTPを楽しくする」をコンセプトとしたRuby用のHTTPクライアントライブラリです。RESTful Webサービスの利用を極めて簡単にし、直感的なAPIでHTTPリクエストの送信とレスポンス処理を提供します。自動的なJSON/XML解析、認証サポート、設定の柔軟性を特徴とし、Ruby開発者にとって最も使いやすいHTTPライブラリの一つとして広く採用されています。シンプルな一行のコードから本格的なAPIクライアント構築まで、幅広い用途に対応します。

詳細

HTTParty 2025年版(v0.23.1)は、15年以上の開発実績を持つ成熟したHTTPクライアントライブラリとして確固たる地位を維持しています。Ruby 2.7.0以降をサポートし、Ruby 3.4との互換性も確保されています。モジュール式のアーキテクチャによりクラスメソッドとしての直接使用と、カスタムAPIクライアントクラスへのインクルードの両方に対応。レスポンスの自動フォーマット検出、包括的な認証機能、SSL設定、広範なリクエストカスタマイズオプションにより、個人プロジェクトから企業レベルのAPI統合まで対応可能です。

主な特徴

  • シンプルなAPI: 直感的で読みやすいHTTPリクエスト記述
  • 自動レスポンス解析: JSON、XML、CSV、YAMLの自動パース機能
  • 包括的認証サポート: Basic認証、Digest認証、カスタムヘッダー対応
  • 柔軟な設定: ベースURI、デフォルトパラメータ、タイムアウト設定
  • コマンドラインツール: httpartyコマンドによるターミナルでの直接利用
  • モジュール式設計: 直接使用とクラスインクルードの両方に対応

メリット・デメリット

メリット

  • Rubyエコシステムでの高い普及率と豊富なコミュニティサポート
  • 極めて分かりやすいAPIによる高い学習効率と開発生産性
  • 自動JSON/XML解析により手動パース処理が不要
  • Rails、Sinatraなどのフレームワークとの優れた統合性
  • コマンドラインツールによるAPI探索と動作確認の容易さ
  • 15年以上の開発実績による高い安定性と信頼性

デメリット

  • パフォーマンス重視の用途では他のライブラリより速度が劣る
  • 大量の並列リクエスト処理には向かない
  • 非同期処理機能が限定的(単純な同期処理ベース)
  • HTTP/2対応が不完全(基本的にHTTP/1.1ベース)
  • 機能の豊富さゆえに軽量性では劣る部分がある
  • 高度なHTTP機能(ストリーミング等)のサポートは限定的

参考ページ

書き方の例

インストールと基本セットアップ

# HTTPartyのインストール
gem install httparty

# Gemfileに追加
echo 'gem "httparty"' >> Gemfile
bundle install

# バージョン確認
ruby -e "require 'httparty'; puts HTTParty::VERSION"

# コマンドラインツールのヘルプ表示
httparty --help

基本的なリクエスト(GET/POST/PUT/DELETE)

require 'httparty'

# 基本的なGETリクエスト(クラスメソッド使用)
response = HTTParty.get('https://api.example.com/users')
puts response.code         # 200
puts response.message      # OK
puts response.headers      # ヘッダー情報
puts response.body         # レスポンスボディ
puts response.parsed_response  # 解析済みレスポンス(JSON→Hash)

# クエリパラメータ付きGETリクエスト
response = HTTParty.get('https://api.example.com/users', {
  query: {
    page: 1,
    limit: 10,
    sort: 'created_at'
  }
})
puts response.request.uri  # https://api.example.com/users?page=1&limit=10&sort=created_at

# ヘッダー付きGETリクエスト
response = HTTParty.get('https://api.example.com/protected', {
  headers: {
    'Authorization' => 'Bearer your-token',
    'Content-Type' => 'application/json',
    'User-Agent' => 'MyApp/1.0'
  }
})

# POSTリクエスト(JSON送信)
user_data = {
  name: '田中太郎',
  email: '[email protected]',
  age: 30
}

response = HTTParty.post('https://api.example.com/users', {
  body: user_data.to_json,
  headers: {
    'Content-Type' => 'application/json',
    'Authorization' => 'Bearer your-token'
  }
})

if response.success?
  created_user = response.parsed_response
  puts "ユーザー作成完了: ID=#{created_user['id']}"
else
  puts "エラー: #{response.code} - #{response.message}"
  puts response.body
end

# POSTリクエスト(フォームデータ送信)
response = HTTParty.post('https://api.example.com/login', {
  body: {
    username: 'testuser',
    password: 'secret123'
  }
})

# PUTリクエスト(データ更新)
updated_data = {
  name: '田中次郎',
  email: '[email protected]'
}

response = HTTParty.put('https://api.example.com/users/123', {
  body: updated_data.to_json,
  headers: { 'Content-Type' => 'application/json' }
})

# DELETEリクエスト
response = HTTParty.delete('https://api.example.com/users/123', {
  headers: { 'Authorization' => 'Bearer your-token' }
})

puts "削除成功" if response.success?

# レスポンス詳細情報の確認
puts "ステータス: #{response.code}"
puts "メッセージ: #{response.message}"
puts "ヘッダー: #{response.headers.inspect}"
puts "本文: #{response.body}"
puts "解析結果: #{response.parsed_response}"

APIクライアントクラスの構築(インクルードパターン)

require 'httparty'

# HTTPartyモジュールをインクルードしたAPIクライアント
class StackExchangeClient
  include HTTParty
  
  # ベースURIとデフォルト設定
  base_uri 'api.stackexchange.com'
  default_timeout 30
  headers 'User-Agent' => 'MyApp/1.0'
  
  def initialize(site = 'stackoverflow')
    @options = { query: { site: site } }
  end
  
  def questions(page: 1, pagesize: 30)
    self.class.get('/2.2/questions', @options.merge(
      query: @options[:query].merge(page: page, pagesize: pagesize)
    ))
  end
  
  def users(page: 1, pagesize: 30)
    self.class.get('/2.2/users', @options.merge(
      query: @options[:query].merge(page: page, pagesize: pagesize)
    ))
  end
  
  def question_by_id(question_id)
    self.class.get("/2.2/questions/#{question_id}", @options)
  end
end

# APIクライアントの使用
client = StackExchangeClient.new('stackoverflow')
questions = client.questions(page: 1, pagesize: 10)

if questions.success?
  questions.parsed_response['items'].each do |question|
    puts "タイトル: #{question['title']}"
    puts "作成日: #{Time.at(question['creation_date'])}"
    puts "タグ: #{question['tags'].join(', ')}"
    puts "---"
  end
else
  puts "エラー: #{questions.code} - #{questions.message}"
end

# より高度なAPIクライアント例
class GitHubAPIClient
  include HTTParty
  
  base_uri 'https://api.github.com'
  headers 'Accept' => 'application/vnd.github.v3+json'
  default_timeout 30
  
  def initialize(token = nil)
    if token
      self.class.headers 'Authorization' => "token #{token}"
    end
  end
  
  def user(username)
    self.class.get("/users/#{username}")
  end
  
  def repositories(username, type: 'all')
    self.class.get("/users/#{username}/repos", {
      query: { type: type, sort: 'updated', direction: 'desc' }
    })
  end
  
  def repository(owner, repo)
    self.class.get("/repos/#{owner}/#{repo}")
  end
  
  def create_issue(owner, repo, title, body = nil, labels = [])
    self.class.post("/repos/#{owner}/#{repo}/issues", {
      body: {
        title: title,
        body: body,
        labels: labels
      }.to_json,
      headers: { 'Content-Type' => 'application/json' }
    })
  end
end

# GitHub APIクライアントの使用
github = GitHubAPIClient.new('your-github-token')
user_info = github.user('octocat')

if user_info.success?
  user = user_info.parsed_response
  puts "ユーザー: #{user['name']} (@#{user['login']})"
  puts "公開リポジトリ: #{user['public_repos']}個"
  puts "フォロワー: #{user['followers']}人"
end

認証とセキュリティ設定

require 'httparty'

# Basic認証の設定
class APIClientWithBasicAuth
  include HTTParty
  base_uri 'https://api.example.com'
  basic_auth 'username', 'password'
end

# または個別リクエストでBasic認証
response = HTTParty.get('https://api.example.com/private', {
  basic_auth: {
    username: 'your_username',
    password: 'your_password'
  }
})

# Digest認証の設定
class APIClientWithDigestAuth
  include HTTParty
  base_uri 'https://api.example.com'
  digest_auth 'username', 'password'
end

# カスタム認証ヘッダー
class APIClientWithTokenAuth
  include HTTParty
  base_uri 'https://api.example.com'
  
  def initialize(api_key)
    self.class.headers 'X-API-Key' => api_key
  end
end

# SSL証明書設定
class SecureAPIClient
  include HTTParty
  base_uri 'https://secure-api.example.com'
  
  # SSL証明書ファイルの指定
  ssl_ca_file '/path/to/certificate.pem'
  ssl_ca_path '/path/to/certificates/'
  
  # クライアント証明書の設定
  pem File.read('/path/to/client.pem'), 'password'
  
  # SSL検証を無効化(開発環境のみ)
  # ssl_verify false
end

# プロキシ設定
class ProxyAPIClient
  include HTTParty
  base_uri 'https://api.example.com'
  
  http_proxy 'proxy.example.com', 8080, 'proxy_user', 'proxy_pass'
end

# タイムアウト設定
class TimeoutAPIClient
  include HTTParty
  base_uri 'https://api.example.com'
  
  default_timeout 30        # 全体のタイムアウト
  open_timeout 10          # 接続タイムアウト
  read_timeout 15          # 読み込みタイムアウト
  write_timeout 10         # 書き込みタイムアウト
end

# ヘッダーとCookie管理
class StatefulAPIClient
  include HTTParty
  base_uri 'https://api.example.com'
  
  def initialize
    @cookies = {}
  end
  
  def login(username, password)
    response = self.class.post('/login', {
      body: { username: username, password: password }.to_json,
      headers: { 'Content-Type' => 'application/json' }
    })
    
    if response.success?
      # レスポンスからCookieを取得
      @cookies = response.headers['set-cookie']
      true
    else
      false
    end
  end
  
  def authenticated_request(endpoint)
    self.class.get(endpoint, {
      headers: { 'Cookie' => @cookies }
    })
  end
end

エラーハンドリングとデバッグ

require 'httparty'

# 包括的なエラーハンドリング
def safe_api_request(url, options = {})
  begin
    response = HTTParty.get(url, options)
    
    case response.code
    when 200..299
      return { success: true, data: response.parsed_response }
    when 400
      return { success: false, error: 'Bad Request', details: response.body }
    when 401
      return { success: false, error: 'Unauthorized', details: '認証が必要です' }
    when 403
      return { success: false, error: 'Forbidden', details: 'アクセス権限がありません' }
    when 404
      return { success: false, error: 'Not Found', details: 'リソースが見つかりません' }
    when 429
      return { success: false, error: 'Too Many Requests', details: 'リクエスト制限に達しました' }
    when 500..599
      return { success: false, error: 'Server Error', details: 'サーバーエラーが発生しました' }
    else
      return { success: false, error: 'Unknown Error', details: "ステータス: #{response.code}" }
    end
    
  rescue Net::TimeoutError => e
    return { success: false, error: 'Timeout', details: 'リクエストがタイムアウトしました' }
  rescue Net::OpenTimeout => e
    return { success: false, error: 'Connection Timeout', details: '接続タイムアウトが発生しました' }
  rescue SocketError => e
    return { success: false, error: 'Network Error', details: 'ネットワークエラーが発生しました' }
  rescue JSON::ParserError => e
    return { success: false, error: 'Parse Error', details: 'レスポンスの解析に失敗しました' }
  rescue StandardError => e
    return { success: false, error: 'Unexpected Error', details: e.message }
  end
end

# 使用例
result = safe_api_request('https://api.example.com/data', {
  headers: { 'Authorization' => 'Bearer token' },
  timeout: 10
})

if result[:success]
  puts "データ取得成功:"
  puts result[:data]
else
  puts "エラー: #{result[:error]}"
  puts "詳細: #{result[:details]}"
end

# リトライ機能付きリクエスト
def request_with_retry(url, options = {}, max_retries = 3)
  retries = 0
  
  begin
    response = HTTParty.get(url, options)
    
    if response.success?
      return response
    elsif response.code >= 500 || response.code == 429
      # サーバーエラーまたはレート制限の場合リトライ
      raise StandardError, "HTTP #{response.code}: #{response.message}"
    else
      # クライアントエラーはリトライしない
      return response
    end
    
  rescue StandardError => e
    retries += 1
    
    if retries <= max_retries
      sleep_time = 2 ** retries  # exponential backoff
      puts "リクエスト失敗 (試行#{retries}/#{max_retries}): #{e.message}"
      puts "#{sleep_time}秒後に再試行..."
      sleep(sleep_time)
      retry
    else
      puts "最大再試行回数に達しました: #{e.message}"
      raise e
    end
  end
end

# デバッグ用APIクライアント
class DebugAPIClient
  include HTTParty
  base_uri 'https://api.example.com'
  
  # デバッグ出力の有効化
  debug_output $stdout
  
  def self.verbose_request(method, endpoint, options = {})
    puts "=== リクエスト詳細 ==="
    puts "メソッド: #{method.upcase}"
    puts "URL: #{base_uri}#{endpoint}"
    puts "ヘッダー: #{headers.merge(options[:headers] || {}).inspect}"
    puts "ボディ: #{options[:body].inspect}" if options[:body]
    puts "========================"
    
    response = send(method, endpoint, options)
    
    puts "=== レスポンス詳細 ==="
    puts "ステータス: #{response.code} #{response.message}"
    puts "ヘッダー: #{response.headers.inspect}"
    puts "ボディ: #{response.body}"
    puts "========================"
    
    response
  end
end

# 使用例
DebugAPIClient.verbose_request(:get, '/users/123', {
  headers: { 'Authorization' => 'Bearer token' }
})

# レスポンス時間測定
def timed_request(url, options = {})
  start_time = Time.now
  response = HTTParty.get(url, options)
  end_time = Time.now
  
  duration = ((end_time - start_time) * 1000).round(2)
  
  puts "リクエスト完了: #{response.code} in #{duration}ms"
  puts "URL: #{url}"
  puts "レスポンスサイズ: #{response.body.length} bytes"
  
  response
end

# バッチリクエスト処理
def batch_requests(urls, options = {})
  results = []
  
  urls.each_with_index do |url, index|
    puts "処理中 (#{index + 1}/#{urls.length}): #{url}"
    
    result = safe_api_request(url, options)
    results << result.merge(url: url)
    
    # API負荷軽減のための待機
    sleep(0.5) if index < urls.length - 1
  end
  
  successful = results.count { |r| r[:success] }
  puts "完了: #{successful}/#{urls.length} 成功"
  
  results
end

# 使用例
urls = [
  'https://api.example.com/users/1',
  'https://api.example.com/users/2',
  'https://api.example.com/users/3'
]

results = batch_requests(urls, {
  headers: { 'Authorization' => 'Bearer token' },
  timeout: 10
})

results.each do |result|
  if result[:success]
    puts "✓ #{result[:url]}: 成功"
  else
    puts "✗ #{result[:url]}: #{result[:error]}"
  end
end

コマンドラインツールの活用

# 基本的なHTTPリクエスト
httparty "https://api.example.com/users"

# 整形されたJSON出力
httparty --json "https://api.example.com/users"

# 整形されたXML出力
httparty --xml "https://api.example.com/data.xml"

# POSTリクエスト(データ送信)
httparty --post --body='{"name":"test"}' --headers='Content-Type:application/json' "https://api.example.com/users"

# 認証付きリクエスト
httparty --headers='Authorization:Bearer your-token' "https://api.example.com/protected"

# クエリパラメータ付きリクエスト
httparty "https://api.example.com/search?q=ruby&limit=10"

# レスポンスヘッダーの表示
httparty --include-headers "https://api.example.com/users"

# リダイレクト詳細表示
httparty --verbose "https://api.example.com/redirect"

# タイムアウト設定
httparty --timeout=30 "https://slow-api.example.com/data"

# ファイルへの出力
httparty "https://api.example.com/users" > users.json

# エラー時の詳細表示
httparty --debug "https://api.example.com/error-endpoint"

# 複数リクエストの実行(Bashループ)
for i in {1..5}; do
  echo "ユーザー $i:"
  httparty "https://api.example.com/users/$i"
  echo "---"
done

# APIエンドポイント探索
httparty "https://api.example.com" | grep -E '"[a-zA-Z_]+_url"'