Help us understand the problem. What is going on with this article?

RubyのHTTPリクエストをできるだけシンプルに実装する

More than 1 year has passed since last update.

背景

Rubyの標準ライブラリであるnet/httpを用いたHTTPリクエストって、Net::HTTP.newしたりNet::HTTP::Get.newしたりhttp.start {...}したり色々と実装が面倒臭いなとずっと思っていたんですが、実は1行で書けることを知ったので open-uri との比較も含めて少しまとめてみました。

環境

  • Ruby 2.6.0

目次

  1. 一番シンプルな方法
  2. リクエストヘッダを指定する方法
  3. Basic認証を使う方法
  4. Proxyを挟む方法
  5. open-uri を使う方法

一番シンプルな方法

GET

require "net/http"

uri = URI.parse("https://jsonplaceholder.typicode.com/todos/1")
response = Net::HTTP.get_response(uri)

response.code # status code
response.body # response body

ポイント

  • Net::HTTP.get_response(uri)の引数はURIクラスのインスタンスのみ。文字列のURLを入れるとNoMethodError: undefined method 'hostname'とかいうエラーが返る。
  • response.code は数字が文字列型で返ってくる("200"みたいな)。
  • response.body は文字列型なので、JSONレスポンスの場合はパースする必要がある。

POST

require "net/http"

params = { title: "my task" }
uri = URI.parse("https://jsonplaceholder.typicode.com/todos")
response = Net::HTTP.post_form(uri, params)

response.code # status code
response.body # response body

ポイント

  • Net::HTTP.post_form 第一引数にURIクラスのインスタンス、第二引数にハッシュ形式でリクエストボディを渡す。
  • Content-Typeapplication/x-www-form-urlencoded 形式になる。
  • params の中に File オブジェクトを入れてみたが、文字列に変換されてしまった。

リクエストヘッダを指定する方法

GET

シンプルな書き方

require "net/http"

uri = URI.parse("https://jsonplaceholder.typicode.com/todos/1")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme === "https"

headers = { "Content-Type" => "application/json" }
response = http.get(uri.path, headers)

response.code # status code
response.body # response body

よく紹介される書き方

require "net/http"

uri = URI.parse("https://jsonplaceholder.typicode.com/todos/1")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme === "https"

headers = { "Content-Type" => "application/json" }
req = Net::HTTP::Get.new(uri.path)
req.initialize_http_header(headers)

response = http.request(req)

response.code # status code
response.body # response body

ポイント

  • Net::HTTP.newの第一引数にホスト名、第二引数にポートを渡す。
  • httpsで始まるURLにアクセスする場合はNet::HTTP#use_ssl=trueを渡す。
  • Net::HTTP#get の第一引数にパス名、第二引数にリクエストヘッダを渡す。
  • Net::HTTP::Get を使う場合は、初期化時の第一引数にはパス名を渡し、Net::HTTP::Get#initialize_http_header にハッシュを渡せばリクエストヘッダを指定できる。
  • Net::HTTP::Get#[]= でもヘッダを指定できる。
    • req["Content-Type"] = "application/json" みたいな。

POST

シンプルな書き方

require "net/http"
require "json"

uri = URI.parse("https://jsonplaceholder.typicode.com/todos")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme === "https"

params = { name: "Takuya" }
headers = { "Content-Type" => "application/json" }
response = http.post(uri.path, params.to_json, headers)

response.code # status code
response.body # response body

よく紹介される書き方

require "net/http"

uri = URI.parse("https://jsonplaceholder.typicode.com/todos")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme === "https"

params = { name: "Nobuto" }
headers = { "Content-Type" => "application/json" }

req = Net::HTTP::Post.new(uri.path)
req.set_form_data(params)
req.initialize_http_header(headers)

response = http.request(req)

response.code # status code
response.body # response body

ポイント

  • Net::HTTP#postの第一引数にパス名、第二引数にリクエストボディ、第三引数にリクエストヘッダを渡す。
  • Net::HTTP#postを使う場合、リクエストボディは文字列を渡す。
  • Net::HTTP::Post#set_form_dataを使う場合、リクエストボディはハッシュを渡す。
  • Content-Typeを指定しなければ自動でapplication/x-www-form-urlencodedが指定される。

Basic認証を利用する方法

URIオブジェクトを使う方法

require "net/http"

uri = URI.parse("https://username:password@jsonplaceholder.typicode.com/todos/1")
response = Net::HTTP.get_response(uri)

response.code # status code
response.body # response body

ポイント

  • URI.parseに渡すURLをプロトコル://ユーザー名:パスワード@ホスト名・パス名形式で書けばOKなので、多分これが一番シンプル。

Net::HTTP::Get#basic_authを使う方法

require "net/http"
username, password = "username", "password"
uri = URI.parse("https://jsonplaceholder.typicode.com/todos/1")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme === "https"

req = Net::HTTP::Get.new(uri.path)
req.basic_auth(username, password)
response = http.request(req)

response.code # status code
response.body # response body

Proxyを挟む方法

require "net/http"

base_uri = URI.parse("https://jsonplaceholder.typicode.com/todos/1")
proxy_uri = URI.parse("http://username:password@proxy-server.com")

http = Net::HTTP.new(base_uri.host, base_uri.port, proxy_uri.host, proxy_uri.port, proxy_uri.username, proxy_uri.password)
http.use_ssl = base_uri.scheme === "https"
response = http.get(base_uri.path)

response.code # status code
response.body # response body

ポイント

  • Net::HTTP.newの第三引数以降にプロキシサーバーのホスト名、ポート、ユーザー名、パスワードを渡せば、他は通常のHTTPリクエストと同様に書ける。

Net::HTTPResponseについて

余談ではあるが、レスポンスとして返ってくるNet::HTTPResponseオブジェクトのメソッドについて見てみたい。

Net::HTTPResponse#body -> String

  • 文字列型でレスポンスボディを返す。

Net::HTTPResponse#code -> String

  • 文字列型でステータスコードを返す。

Net::HTTPResponse#http_version -> String

  • 文字列型でHTTPのバージョンを返す。

Net::HTTPResponse#message -> String

  • 文字列型でステータスコードのメッセージを返す。

Net::HTTPResponse#value -> nil

  • レスポンスコードが2xxの場合はnilを返す。
  • レスポンスコードが3xxの場合は例外HTTPRetriableErrorraiseする
  • レスポンスコードが4xxの場合は例外HTTPServerExceptionraiseする
  • レスポンスコードが5xxの場合は例外HTTPFatalErrorraiseする
  • レスポンスコードが1xxもしくは未知のコードの場合は例外HTTPErrorraiseする
require "net/http"

uri = URI.parse("https://jsonplaceholder.typicode.com/todos/1")
response = Net::HTTP.get_response(uri)

p response.code # -> "200"
p response.body # -> "{ \"userId\": 1, \"id\": 1, \"title\": \"delectus aut autem\", \"completed\": false}"
p response.http_version # -> "1.1"
p response.message # -> "OK"
p response.value # -> nil

なんで#valueってプロパティのようなメソッド名なのに例外をraiseするっていう処理内容なのかちょっと謎。

open-uri を使う方法

Kernel.#openを使う方法

require "open-uri"

open("https://jsonplaceholder.typicode.com/todos/1").read # response body

URI#openを使う方法

require "open-uri"

uri = URI.parse("https://jsonplaceholder.typicode.com/todos/1")
uri.open.read # response body

リクエストヘッダを指定する方法

require "open-uri"

open("https://jsonplaceholder.typicode.com/todos/1",
  "User-Agent" => "Ruby/#{RUBY_VERSION}"
).read # response body

ポイント

  • open-uriはGETリクエストのみ対応。
  • open-urirequireするとURIURI#openメソッドが追加される。
  • openメソッドの第二引数以降にキーワード引数形式でリクエストヘッダを指定できる。
  • ステータスコードが2xxでなかった場合にはOpenURI::HTTPErrorraiseする。

感想

  • 何の変哲も無いGET/POSTリクエストについてはNet::HTTP.get_responseNet::HTTP.post_formがシンプルなので積極的に使っていきたい。
  • ちょっとカスタマイズする必要がある場合はNet::HTTP#getNet::HTTP#postを使う。ほとんどの場合Net::HTTP::GetNet::HTTP::Postを使う必要なさそう。
  • Rails環境ではnet/httprequireなしで実行できる一方でopen-urirequireが必要なので、特に理由が無ければ前者を使っていきたい。

参考

takano-h
TECH::CAMPでRuby on Railsに触れたことで営業から足を洗った系WEBエンジニア。 Ruby on Rails / Vue.js / React / Angular / MySQL / AWS 等。趣味は出会い系サイトの開発。
https://match-lab.com
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした