Need to rescue 401 status code using RestClient - ruby-on-rails

I am using a Rails gem to send requests to an api using RestClient. I need to rescue a 401 error code. I saw the following on RestClient documentation:
> RestClient.get('http://my-rest-service.com/resource'){ |response,
> request, result, &block| case response.code when 200
> p "It worked !"
> response when 423
> raise SomeCustomExceptionIfYouWant else
> response.return!(request, result, &block) end }
I have attempted to implement a similar case statement:
case response.code
when 200
JSON.parse(response.body)
when 401
raise AuthenicationError, "Unauthorized"
else
raise RestClient::ExceptionWithResponse
end
It captures the 200 case fine but ignores the 401 case and goes straight to the else. Any suggestions on raising an exception for 401 on a response that's coming back through RestClient?

I can't tell you and why I'm sure the rest-client repo can tell you :) ... but using RestClient::Request.new then executing the api call with a block works for me.
I think it probably has to do with the fact that the RestClient has built in exceptions.
request = RestClient::Request.new(
method: :get,
url: 'https://my-rest-service.com/resource.json')
response = request.execute {|response| response}
case response.code
when 200
puts "Good"
when 401
puts "Bad"
raise Exception
end

It captures the 200 case fine but ignores the 401 case and goes straight to the else.
I rather suspect it does not go to the else, actually; you'd still get a RestClient::ExceptionWithResponse raised even if you took out the else clause completely, because that's what RestClient.get does when it gets an error response such as in the 400 or 500 range. From the README:
for result codes between 200 and 207, a RestClient::Response will be returned
for result codes 301, 302 or 307, the redirection will be followed if the request is a GET or a HEAD
for result code 303, the redirection will be followed and the request transformed into a GET
for other cases, a RestClient::ExceptionWithResponse holding the Response will be raised; a specific exception class will be thrown for known error codes
call .response on the exception to get the server's response

You are using wrong HTTP code. Unauthorized is actually 401, not 410.

If you catch the exception from Request.execute in a rescue block, note that you can also get the response body form the exception, example:
def request(method, url, params = {})
resp = RestClient::Request.execute(
method: method,
url: url,
timeout: 30,
accept: :json,
payload: params.to_json,
headers: {
content_type: :json,
}
)
JSON.parse(resp.body)
rescue => e
{ error: e.message, body: JSON.parse(e.response.body) } # <-------------
end

Related

How do you retry an HTTP request to an API if the response code is other than "200 OK"?

I'm making requests to an external API that 9 out of 10 times returns a JSON string that I use to create records in my own App.
That one time though, it will return an "Internal Server Error" (code 500), crashing the rest of my App as it tries to parse a nil JSON String.
How can I retry the external API call if the response.code is other than "200 OK" ?
External API #connector (HTTP Request)
def fetch_client(client_identification)
url = URI("#{BASE_URL}/clients/#{client_identification}")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = "Bearer 1234567890qwertyuiopasdfghjklzxcvbnm"
response = https.request(request)
end
My Adapter that does something with the response (it fails as it tries to parse an empty string)
def get_client(client_identification)
response = #connector.fetch_client(client_identification)
# how to retry if response.code != "200" ?
JSON.parse(response.body) # crash
end
I've tried something like this but my code fails to compile throwing an "Invalid retry" error
retry if response.code != "200"
First you are missing a lot of error handling such as Net::ReadTimeout, Errno::ECONNRESET, Errno::ECONNABORTED, Errno::EPIPE, OpenSSL::SSL::SSLError, Timeout::Error and probably others. For those Net::HTTP has max_retries but you'll need to rescue if not ok after max_retries is reached.
But for the best case scenario, you could call get_client again (recursion) BUT you need to set a retry limit to not get a stack overflow OR if you want to retry only once and don't care about DRY:
def get_client(client_identification)
response = #connector.fetch_client(client_identification)
if response.code != "200"
response = #connector.fetch_client(client_identification)
end
response.code != "200" ? {} : JSON.parse(response.body)
end

app signal gem send error to app signal without a crash request

I am using appsignal gem to track if there is an error processing in my app.
This case i do call external API using faraday.
def truck_information(req_params)
response = #conn.post('truck/info') do |req|
req.headers['Content-Type'] = 'application/json'
req.body = req_params
end
return JSON.parse(response.body) if response_successful?(response)
response_error(response)
end
def response_successful?(response)
response.status == 200
end
def response_error(response)
err = NctError, "Code: #{response.status}, response: #{response.body}"
Appsignal.set_error(err)
raise NctError, I18n.t('error_messages.ppob.server_error')
end
my truck_information is used to call external api. and if success i will parse it to json. but if error i will call response_error method parser to create custom class error (NctError) and i want to send to appsignal to show the error without breaking the application process.
But when i was tested it, it doesn't send to appsignal. How to do send error to appsignal, even if it doesn't crash a request? because i need to track the error.
Thank you
You could try Appsignal.send_error
Appsignal.send_error(err)
If above doesn't work either, then set_error and send_error may only work with Exception:
def response_error(response)
raise NctError, I18n.t('error_messages.ppob.server_error')
rescue => e
Appsignal.send_error(e) do |transaction|
transaction.params = { code: response.status, response: response.body }
end
end

Ruby: failed to make a successful GET request

I am trying to send a http.get request to different websites. Here is the code I am using:
def makePing
begin
url = URI.parse(#URI)
req = Net::HTTP::Get.new(url.to_s)
res = Net::HTTP.start(url.host, url.port) {|http|
http.read_timeout = #request_timeout_limit
http.request(req)
}
# debugger
rescue Exception => echo
puts "Error is: Failed to open TCP connection to #{#URI}"
end
end
It returns the result of 200 for 'http://www.example.com'
but
for http://www.google.com or http://www.facebook.com
it returns
<Net::HTTPNotFound 404 Not Found readbody=true>
1-I am wondering why it happens like this?
2-How can I get the body of the response?
3- I expect that, the request get expired exactly after #request_timeout_limit, and it stop trying, but it is not working in this way?

how to access this kind of hash

I am using RestClient to make a post request and i made it so i an error response back so i can print those error messages in console
i tried the following per the restclient gem documentation
begin
response = RestClient.post base_uri, params.to_json, content_type: 'application/json', accept: 'application/json'
rescue RestClient::ExceptionWithResponse => err
error = err.response
p "this is the error response #{error}"
end
when i print err.response i get the following
"this is the error response {\"error\":{\"message\":\"An active access token must be used to query information about the current us
er.\",\"type\":\"OAuthException\",\"code\":2500,\"fbtrace_id\":\"HTzmJ0CcIfd\"}}"
how do i access the message in the above hash to display it in console?
tried
p "this is the error response #{error.message}"
and it gives me "Bad request" - have no idea where it gets that
If you're just looking to output it:
error = JSON.load(err.response)
puts error['error']['message']
You can always format it a bit better:
puts '[Code %d %s] %s' % [
error['error']['code'],
error['error']['type'],
error['error']['message']
]
Note that using puts inside of a Rails process is not going to work very well. You might want to use Rails.logger.debug instead.
The response you received is in JSON. You'll need to decode the JSON first and then interact with the data. Personally, I like MultiJson for this:
begin
response = RestClient.post base_uri, params.to_json, content_type: 'application/json', accept: 'application/json'
rescue RestClient::ExceptionWithResponse => err
error = MultiJson.load(err.response)
p "this is the error response #{error[:message]}"
end

Failing HTTP request for localhost?

I am using RestClient to get a URL in a Rake task.
I'm getting this error:
the scheme http does not accept registry part: :80 (or bad hostname?)
on the second line here:
def check_url url
RestClient.get('http://localhost:2828'){ |response, request, result, &block|
case response.code
when 200
return true
else
# response.return!(request, result, &block)
return false
end
}
end
What am I missing? RestClient.get with localhost works fine for me in the console.
Any help would be great.

Resources