Levi
Levi

Reputation: 869

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.

Upvotes: 6

Views: 2627

Answers (3)

Frederik
Frederik

Reputation: 719

Sean’s answer sounds like the right thing to do here, but if you do want to modify your connection, you have to duplicate it before:

@connection = @connection.dup
@connection.request :multipart

This can also be used to delete middleware from a connection:

@connection = @connection.dup
@connection.builder.delete(Faraday::Request::lookup_middleware(:multipart))

Upvotes: 0

Sean
Sean

Reputation: 564

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

Upvotes: 2

Kristján
Kristján

Reputation: 18803

Calling @connection.request :multipart adds Faraday::Request::Multipart to @connecton.builder.handlers. If you want to remove something, you can manipulate that array.

I make no claim that messing with Faraday's (relative) internals is a good idea. Keeping your two connections sounds like a better plan.

Upvotes: 0

Related Questions