Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to use Retry Adaptor and POST requests #1272

Closed
doutatsu opened this issue Apr 24, 2021 · 4 comments
Closed

Unable to use Retry Adaptor and POST requests #1272

doutatsu opened this issue Apr 24, 2021 · 4 comments
Labels
info Generic question on how to use Faraday

Comments

@doutatsu
Copy link

Basic Info

  • Faraday Version: 1.4.1
  • Ruby Version: 2.7.2

Issue description

I am trying to figure out how to make retry adapted work. I managed to get it working on a simple GET request, but when I am trying to use it with POST requests, I get an error. Using a regular connection, like this: Faraday.new(url: 'www.google.com', request: { timeout: 3, open_timeout: 1 }), doesn't produce any errors

Steps to reproduce

  1. Create a connection:
connection =
        Faraday.new(url: 'www.google.com', request: { timeout: 3, open_timeout: 1 }) do |f|
          f.request :retry, {
            max: 4,
            interval: 0.05,
            interval_randomness: 0.5,
            backoff_factor: 2,
            retry_statuses: [429, 408]
          }
        end
  1. Make a POST request using the connection connection.post("test", action: 'get_data')
  2. Will return this error:
NoMethodError (undefined method `bytesize' for {:action=>"get_data"}:Hash)
@iMacTia
Copy link
Member

iMacTia commented Apr 24, 2021

Hi @doutatsu, the retry middleware is not at fault here, it's the fact that your POST request is trying to send a body.
When sending a body, that body needs to be a string, because that's what adapters (the libraries performing the http call) will expect. Not only that, but you'll also need to set the correct header based on the type of body you're sending. This is just how HTTP works.

Now, this is where Faraday middleware comes to the rescue and make your life easier!
There are in fact middleware written to automatically convert the body into any format you may need, and also set the correct headers for you.
If your server expects a form-url-encoded (e.g. a Rails server), you can change your connection like this:

connection =
    Faraday.new(url: 'www.google.com', request: { timeout: 3, open_timeout: 1 }) do |f|
      f.request :url_encoded
      f.request :retry, {
        max: 4,
        interval: 0.05,
        interval_randomness: 0.5,
        backoff_factor: 2,
        retry_statuses: [429, 408]
      }
    end

And the url_encoded middleware will take care of your body.
If you need a JSON request, that middleware doesn't ship together with Faraday, but you can find it in faraday_middleware.

Please do let me know if the above helps!

@olleolleolle olleolleolle added the info Generic question on how to use Faraday label Apr 24, 2021
@doutatsu
Copy link
Author

@iMacTia Thanks for the detailed response, I am aware of that, but what is the reason for it just working out of the box, when I don't add additional adapters (retry in my case). Was there some default adapters already added?

Looking at the connection without a block, I can see that the POST request does generate correct headers:
{"User-Agent"=>"Faraday v1.4.1", "Content-Type"=>"application/x-www-form-urlencoded"}

Which means, when I am providing the block, all the default adapters are not used, and I need to specify them myself?

@iMacTia
Copy link
Member

iMacTia commented Apr 24, 2021

That's correct, Faraday uses the default_connection if no block is provided, which consists of the :url_encoded middleware and the :net_http adapter (unless that have been changed, which can also be done).

Providing a block will replace that default stack with yours. The adapter will still be added automatically if Faraday detects that is missing from the custom block 👍

@doutatsu
Copy link
Author

Finally wrapped work on this, so happy to close the issue.

One final note, for anyone else that might have stumbled on this: I couldn't see POST requests retrying, but then realised by default, post doesn't get retried

You can provide a methods option with a list of HTTP methods. This will replace the default list of HTTP methods: delete, get, head, options, put.

So in my case, it ended up looking like this:

        Faraday.new(url: connection_url, request: { timeout: 3 }) do |f|
          f.request :url_encoded
          f.request :retry, {
            max: 4,
            interval: 0.05,
            interval_randomness: 0.5,
            backoff_factor: 2,
            exceptions: [Faraday::TimeoutError, Faraday::ConnectionFailed],
            methods: %i[get post]
          }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
info Generic question on how to use Faraday
Projects
None yet
Development

No branches or pull requests

3 participants