47

How does one properly check the response from Net::HTTP::Get (for example) for "success" (i.e., a 2xx return code)? The documentation seems to be sadly silent on this simple question.

I have:

response=Net::HTTP.new( host, port ).request my_get_request # details not important

After a bunch of Googling and near-random typing, I finally determined that this works:

response.class < Net::HTTPSuccess

Is that actually the canonical way to do it?

Anand Bait
  • 331
  • 1
  • 12
cbmanica
  • 3,502
  • 7
  • 36
  • 54

3 Answers3

83

For Net::HTTP, yes, checking the class of the response object is the way to do it. Using kind_of? (aliased also as is_a?) is a bit clearer (but functionally equivalent to using <):

response.kind_of? Net::HTTPSuccess

Calling value on response will also raise a Net::HTTPError if the status code was not a successful one (what a poorly named method…).

If you can, you may want to consider using a gem instead of Net::HTTP, as they often offer better APIs and performance. Typhoeus and HTTParty are two good ones, among others.

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
  • Thanks! Couldn't find a single example anywhere. – cbmanica Aug 19 '12 at 00:43
  • Is this for any response 2xx, or also 3xx? Can't find any docs on it. – Maarten Dec 03 '13 at 12:49
  • 1
    @Maarten 3xx is not a successful response as defined in HTTP, and Net::HTTP abides by that. 3xx would be of type `Net::HTTPRedirection`. – Andrew Marshall Dec 03 '13 at 15:01
  • 2
    Then I guess anyone using this should be aware that servers may return `304 Not Modified`, and so may want to check for `Net::HTTPRedirection` cases as well! – Maarten Dec 03 '13 at 15:13
  • 3
    This code reads funny. "response kinda success? Nah, not really. This part was good, though". :) – Sergio Tulentsev Sep 15 '15 at 18:05
  • This code has a hidden bomb inside. When response code is `204` `response.body` is `nil`. So if you want to check like `is_a? Net::HTTPSuccess` you have to use `response.body || ''` – puchu Mar 23 '19 at 15:24
  • Man, I thought you were kidding about `value` being misnamed, like maybe it read the `body` and would _also_ `raise` when the response is not successful. Nope! Literally, it's only purpose is to `Raise an HTTP error if the response is not 2xx (success).` Unreal. Very poor choice of word. Would have expected `success?` or `successful?` or `okay?`. – Joshua Pinter Oct 27 '20 at 16:54
23

You can take advantage of Ruby's case statement which idiomatically performs class comparisons, thanks to its use of === under the hood.

Here's an example from a JSON client that catches particular errors but otherwise just returns the server's message:

  case response
    when Net::HTTPSuccess
      JSON.parse response.body
    when Net::HTTPUnauthorized
      {'error' => "#{response.message}: username and password set and correct?"}
    when Net::HTTPServerError
      {'error' => "#{response.message}: try again later?"}
    else
      {'error' => response.message}
  end

Note above Net::HTTPResponse parent classes (e.g. Net::HTTPServerError) work too.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
tantrix
  • 1,248
  • 12
  • 14
4

If all you're looking to grab is the HTTP status code of an external API or website, then try Net::HTTP.get_response.

Net::HTTP.get(url) returns a string. You won't be able to easily parse the header response from it:

url = URI('http://example.com')
string_response = Net::HTTP.get(url)
# => "<!doctype html>\n<html>\n<head>\n    <title>Example Domain</title>\n\n    <meta charset=\"utf-8\" />\n    <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    <style type=\"text/css\">\n    body {\n        background-color: #f0f0f2;\n        margin: 0;\n        padding: 0;\n        font-family: \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n        \n    }\n    div {\n        width: 600px;\n        margin: 5em auto;\n        padding: 50px;\n        background-color: #fff;\n        border-radius: 1em;\n    }\n    a:link, a:visited {\n        color: #38488f;\n        text-decoration: none;\n    }\n    @media (max-width: 700px) {\n        body {\n            background-color: #fff;\n        }\n        div {\n            width: auto;\n            margin: 0 auto;\n            border-radius: 0;\n            padding: 1em;\n        }\n    }\n    </style>    \n</head>\n\n<body>\n<div>\n    <h1>Example Domain</h1>\n    <p>This domain is established to be used for illustrative examples in documents. You may use this\n    domain in examples without prior coordination or asking for permission.</p>\n    <p><a href=\"http://www.iana.org/domains/example\">More information...</a></p>\n</div>\n</body>\n</html>\n"

string_response.class
# => String
string_response.kind_of? Net::HTTPSuccess
# => false

status_response = Net::HTTP.get_response(url)
# => #<Net::HTTPOK 200 OK readbody=true>
status_response.class
# => Net::HTTPOK
status_response.kind_of? Net::HTTPSuccess
# => true
the Tin Man
  • 158,662
  • 42
  • 215
  • 303