Net::HTTP
Ruby標準ライブラリのHTTPクライアント。Ruby 1.8から提供される歴史あるライブラリで、レガシーコードベースで広く使用。基本的なHTTP/HTTPS通信、認証、Cookie処理機能を提供。外部依存関係不要だが、APIが古く使いにくいとされる。
概要
Net::HTTPは、Rubyの標準ライブラリに含まれるHTTPクライアントです。外部の依存関係なしでHTTP/HTTPSリクエストを実行でき、基本的な認証、ファイルアップロード、SSL/TLS通信など、HTTPプロトコルの主要な機能をサポートしています。
主な特徴
- 標準ライブラリ: Rubyに標準で含まれており、追加インストール不要
- 完全なHTTPサポート: GET、POST、PUT、DELETE等の全HTTPメソッドに対応
- HTTPS/SSL対応: セキュアな通信をネイティブサポート
- 基本認証・ダイジェスト認証: RFC2617準拠の認証機能
- マルチパートフォームデータ: ファイルアップロードのサポート
- リダイレクトハンドリング: 自動・手動でのリダイレクト処理
- Keep-Alive: 接続の再利用による性能向上
インストール
Net::HTTPはRubyの標準ライブラリのため、追加のインストールは不要です。
require 'net/http'
require 'uri'
require 'json' # JSON処理用(オプション)
基本的な使い方
シンプルなGETリクエスト
require 'net/http'
require 'uri'
uri = URI('https://api.example.com/data')
response = Net::HTTP.get_response(uri)
puts response.code # => "200"
puts response.body # レスポンスボディ
より詳細な制御
uri = URI('https://api.example.com/data')
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
request = Net::HTTP::Get.new(uri)
response = http.request(request)
puts response.body
end
実装例
1. 各種HTTPメソッドの実装
require 'net/http'
require 'uri'
require 'json'
class HttpClient
# GETリクエスト
def self.get(url, headers = {})
uri = URI(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Get.new(uri)
headers.each { |key, value| request[key] = value }
response = http.request(request)
handle_response(response)
end
# POSTリクエスト(JSON)
def self.post_json(url, data, headers = {})
uri = URI(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
headers.each { |key, value| request[key] = value }
request.body = data.to_json
response = http.request(request)
handle_response(response)
end
# PUTリクエスト
def self.put(url, data, headers = {})
uri = URI(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Put.new(uri)
request['Content-Type'] = 'application/json'
headers.each { |key, value| request[key] = value }
request.body = data.to_json
response = http.request(request)
handle_response(response)
end
# DELETEリクエスト
def self.delete(url, headers = {})
uri = URI(url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Delete.new(uri)
headers.each { |key, value| request[key] = value }
response = http.request(request)
handle_response(response)
end
private
def self.handle_response(response)
case response
when Net::HTTPSuccess
{ status: response.code, body: response.body }
when Net::HTTPRedirection
{ status: response.code, location: response['location'] }
else
{ status: response.code, error: response.message, body: response.body }
end
end
end
# 使用例
result = HttpClient.get('https://api.example.com/users/1')
puts result[:body]
2. 認証の実装
# 基本認証
def basic_auth_request(url, username, password)
uri = URI(url)
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
request = Net::HTTP::Get.new(uri)
request.basic_auth(username, password)
response = http.request(request)
response.body
end
end
# Bearer トークン認証
def bearer_auth_request(url, token)
uri = URI(url)
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
request = Net::HTTP::Get.new(uri)
request['Authorization'] = "Bearer #{token}"
response = http.request(request)
response.body
end
end
# APIキー認証(ヘッダー)
def api_key_auth_request(url, api_key)
uri = URI(url)
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
request = Net::HTTP::Get.new(uri)
request['X-API-Key'] = api_key
response = http.request(request)
response.body
end
end
3. ファイルアップロード
require 'net/http/post/multipart' # gem install multipart-post が必要
# 方法1: Ruby 2.7以降の set_form を使用
def upload_file_modern(url, file_path, additional_params = {})
uri = URI(url)
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
request = Net::HTTP::Post.new(uri)
form_data = additional_params.map { |k, v| [k.to_s, v] }
form_data << ['file', File.open(file_path)]
request.set_form(form_data, 'multipart/form-data')
response = http.request(request)
response.body
end
end
# 方法2: 手動でマルチパートフォームを構築
def upload_file_manual(url, file_path, field_name = 'file')
uri = URI(url)
boundary = "RubyMultipartPost-#{Time.now.to_i}"
post_body = []
post_body << "--#{boundary}\r\n"
post_body << "Content-Disposition: form-data; name=\"#{field_name}\"; filename=\"#{File.basename(file_path)}\"\r\n"
post_body << "Content-Type: #{content_type_for(file_path)}\r\n"
post_body << "\r\n"
post_body << File.read(file_path)
post_body << "\r\n--#{boundary}--\r\n"
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
request = Net::HTTP::Post.new(uri)
request.body = post_body.join
request["Content-Type"] = "multipart/form-data; boundary=#{boundary}"
response = http.request(request)
response.body
end
end
def content_type_for(file_path)
case File.extname(file_path).downcase
when '.jpg', '.jpeg' then 'image/jpeg'
when '.png' then 'image/png'
when '.gif' then 'image/gif'
when '.pdf' then 'application/pdf'
when '.txt' then 'text/plain'
else 'application/octet-stream'
end
end
4. エラーハンドリングとリトライ
class ResilientHttpClient
MAX_RETRIES = 3
RETRY_DELAY = 1 # 秒
def self.request_with_retry(url, method = :get, options = {})
uri = URI(url)
retries = 0
begin
Net::HTTP.start(uri.host, uri.port,
use_ssl: uri.scheme == 'https',
open_timeout: 5,
read_timeout: 10) do |http|
request = create_request(method, uri, options)
response = http.request(request)
case response
when Net::HTTPSuccess
return { success: true, data: parse_response(response) }
when Net::HTTPRedirection
# リダイレクトを追跡
return request_with_retry(response['location'], method, options)
else
raise HTTPError.new(response.code, response.message)
end
end
rescue => e
retries += 1
if retries <= MAX_RETRIES
sleep(RETRY_DELAY * retries) # 指数バックオフ
retry
else
return { success: false, error: e.message }
end
end
end
private
def self.create_request(method, uri, options)
request_class = case method
when :get then Net::HTTP::Get
when :post then Net::HTTP::Post
when :put then Net::HTTP::Put
when :delete then Net::HTTP::Delete
else raise ArgumentError, "Unsupported method: #{method}"
end
request = request_class.new(uri)
# ヘッダーの設定
options[:headers]&.each { |k, v| request[k] = v }
# ボディの設定
if options[:body]
request.body = options[:body].is_a?(String) ? options[:body] : options[:body].to_json
request['Content-Type'] = 'application/json' unless request['Content-Type']
end
# 認証の設定
if options[:basic_auth]
request.basic_auth(options[:basic_auth][:username], options[:basic_auth][:password])
end
request
end
def self.parse_response(response)
content_type = response['Content-Type']
if content_type&.include?('application/json')
JSON.parse(response.body)
else
response.body
end
end
class HTTPError < StandardError
attr_reader :code, :message
def initialize(code, message)
@code = code
@message = message
super("HTTP Error #{code}: #{message}")
end
end
end
# 使用例
result = ResilientHttpClient.request_with_retry(
'https://api.example.com/data',
:post,
headers: { 'Authorization' => 'Bearer token' },
body: { name: 'Test', value: 123 }
)
5. ストリーミングとプログレス
# 大きなファイルのダウンロード
def download_file_with_progress(url, destination)
uri = URI(url)
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
request = Net::HTTP::Get.new(uri)
http.request(request) do |response|
total_size = response['Content-Length'].to_i
downloaded = 0
File.open(destination, 'wb') do |file|
response.read_body do |chunk|
file.write(chunk)
downloaded += chunk.size
# 進捗を表示
progress = (downloaded.to_f / total_size * 100).round(2)
print "\rダウンロード中: #{progress}%"
end
end
puts "\nダウンロード完了!"
end
end
end
# ストリーミングレスポンスの処理
def stream_response(url)
uri = URI(url)
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
request = Net::HTTP::Get.new(uri)
http.request(request) do |response|
response.read_body do |chunk|
# チャンクごとに処理
process_chunk(chunk)
end
end
end
end
6. 並列リクエスト
require 'thread'
class ParallelHttpClient
def self.fetch_multiple(urls, max_threads = 5)
queue = Queue.new
urls.each { |url| queue << url }
results = {}
mutex = Mutex.new
threads = Array.new(max_threads) do
Thread.new do
while url = queue.pop(true) rescue nil
begin
response = fetch_single(url)
mutex.synchronize { results[url] = response }
rescue => e
mutex.synchronize { results[url] = { error: e.message } }
end
end
end
end
threads.each(&:join)
results
end
private
def self.fetch_single(url)
uri = URI(url)
response = Net::HTTP.get_response(uri)
{
status: response.code,
body: response.body,
headers: response.to_hash
}
end
end
# 使用例
urls = [
'https://api.example.com/users/1',
'https://api.example.com/users/2',
'https://api.example.com/users/3'
]
results = ParallelHttpClient.fetch_multiple(urls)
他のライブラリとの比較
Net::HTTP vs HTTParty
- Net::HTTP: 標準ライブラリ、詳細な制御が可能、やや冗長な記述
- HTTParty: よりシンプルなAPI、便利なヘルパーメソッド
Net::HTTP vs Faraday
- Net::HTTP: 依存関係なし、基本的な機能
- Faraday: ミドルウェアシステム、アダプター切り替え可能
Net::HTTP vs RestClient
- Net::HTTP: 低レベルな制御、標準ライブラリ
- RestClient: 高レベルな抽象化、使いやすいAPI
ベストプラクティス
-
接続の再利用
# Keep-Aliveを使用した接続の再利用 Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http| http.keep_alive_timeout = 20 # 複数のリクエストで同じ接続を使用 response1 = http.request(Net::HTTP::Get.new('/api/v1/users')) response2 = http.request(Net::HTTP::Get.new('/api/v1/posts')) end -
タイムアウトの設定
http = Net::HTTP.new(uri.host, uri.port) http.open_timeout = 5 # 接続タイムアウト http.read_timeout = 10 # 読み取りタイムアウト http.write_timeout = 10 # 書き込みタイムアウト -
SSL証明書の検証
http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_PEER # デフォルト # 開発環境でのみ(本番環境では使用しない) # http.verify_mode = OpenSSL::SSL::VERIFY_NONE
まとめ
Net::HTTPは、Rubyの標準HTTPクライアントライブラリとして、外部依存なしで幅広いHTTP機能を提供します。やや冗長な記述が必要な場合もありますが、詳細な制御が可能で、小規模から中規模のプロジェクトに適しています。より高度な機能や簡潔なAPIが必要な場合は、HTTPartyやFaradayなどのサードパーティライブラリの使用も検討してください。