rubyfaraday

Faraday Connection: Switching the request mode?


I am using faraday to handle some requests to an internal API. The API endpoints use a CSRF token, so I am also using faraday-cookie_jar.

For a couple of the API endpoints, they require a :multipart request. Others do not.

Question

Is there any way to utilize the same Connection object, but switch whether or not you are doing a :multipart or a :url_encoded request?

Currently, I have to use two connections depending on which type of request I'm making. It does not seem you can change a connection's request method after it has done at least 1 request.

@connection = Faraday.new(url: 'http://localhost:3000') do |faraday|
  faraday.request :url_encoded
  faraday.use :cookie_jar
  faraday.adapter Faraday.default_adapter
end

@connection.get '/something'

# try to change to :multipart
@connection.request :multipart # => Faraday::RackBuilder::StackLocked: can't modify middleware stack after making a request

It doesn't seem to allow you to switch after you've made a request. I know that you can modify the request a bit for each request itself by passing a block, but I can't seem to find where to modify to switch to :multipart in it.

Thanks in advance.


Solution

  • You'll want to include both middleware options when creating your connection (:url_encoded and :multipart), and then switch using explicit Content-Type headers.

    Per the Faraday ReadMe, you can specify 'em both. An excerpt:

     Faraday.new(...) do |conn|
        # POST/PUT params encoders:
        conn.request :multipart
        conn.request :url_encoded
        conn.adapter :net_http
      end
    

    This request middleware setup affects POST/PUT requests in the following way:

    1. Request::Multipart checks for files in the payload, otherwise leaves everything untouched;
    2. Request::UrlEncoded encodes as "application/x-www-form-urlencoded" if not already encoded or of another type.

    So adding :multipart still allows for url-encoded posts, because it only matters when there are files in the payload. Then, if you explicitly set your Content-Type for the file upload, you should be fine - Faraday will use the correct request middleware because you explicitly told it to use multipart. But if you don't specify, it will default to url-encoded.

    # works using :url_encoded
    @connection.post '/something' do |req|
      req.body = { some: 'posted fields' }
    end
    
    # works using :multipart because Content-Type was explicitly set
    @connection.post '/some_file_endpoint' do |req|
      req.headers['Content-Type'] = 'multipart/form-data'
      req.body = { file_field: Faraday::UploadIO.new(file_path, 'text/plain') }
    end