Use sawyer with multipart POST?
ldonnet opened this issue · 7 comments
Hi, I use your great gem to make an API client ruby. It works well when I make basic requests but I have one probleme when I use multipart post. It seems that body isn't parsed by multipart-post.
When I try this with Faraday it works like a charm :
conn = Faraday.new(:url => "http://localhost:8080/chouette_iev/") do |conn|
# POST/PUT params encoders:
conn.request :multipart
conn.request :url_encoded
conn.adapter :net_http
end
action_params_io = Faraday::UploadIO.new("/home/luc/parameters_import_neptune.json", "application/json", "parameters.json")
transport_data_io = Faraday::UploadIO.new("/home/luc/demo/15568799.xml", "application/xml", "15568799.xml")
payload = {
:file1 => action_params_io,
:file2 => transport_data_io,
}
conn.post "referentials/test2/importer/neptune", payload
But when I user this sawyer configuration body is not splitted in parts :
opts = {
:links_parser => Sawyer::LinkParsers::Hal.new
}
conn_opts = {
:headers => {
:accept => "multipart/form-data",
:user_agent => " Ievkit Ruby Gem 0.1.0"
}
}
conn_opts[:builder] = Faraday::RackBuilder.new do |builder|
builder.use Faraday::Request::Multipart
builder.use Faraday::Request::UrlEncoded
builder.use Ievkit::Response::RaiseError
builder.use FaradayMiddleware::FollowRedirects
builder.use Faraday::Response::Logger
builder.adapter Faraday.default_adapter
end
conn_opts[:proxy] = {}
opts[:faraday] = Faraday.new(conn_opts)
opts
@agent ||= Sawyer::Agent.new(api_endpoint, sawyer_options) do |http|
http.headers[:accept] = default_media_type
http.headers[:content_type] = "application/json"
http.headers[:user_agent] = user_agent
Is it possible to make a multipart-post request? Is there a specific way to call?
If I comment the encoding of the body multipart works again :
# Makes a request through Faraday.
#
# method - The Symbol name of an HTTP method.
# url - The String URL to access. This can be relative to the Agent's
# endpoint.
# data - The Optional Hash or Resource body to be sent. :get or :head
# requests can have no body, so this can be the options Hash
# instead.
# options - Hash of option to configure the API request.
# :headers - Hash of API headers to set.
# :query - Hash of URL query params to set.
#
# Returns a Sawyer::Response.
def call(method, url, data = nil, options = nil)
if NO_BODY.include?(method)
options ||= data
data = nil
end
options ||= {}
url = expand_url(url, options[:uri])
started = nil
res = @conn.send method, url do |req|
if data
req.body = data #.is_a?(String) ? data : encode_body(data) <= Comment here
end
if params = options[:query]
req.params.update params
end
if headers = options[:headers]
req.headers.update headers
end
started = Time.now
end
Response.new self, res, :sawyer_started => started, :sawyer_ended => Time.now
end
I think I could have the right behaviour with an updated serializer. Do you have a specific one for multipart request?
Thanks
Luc Donnet
Good find @ldonnet. This line is more of a convenience to encode Ruby objects before making the request. What's data
at this point in your case? A Faraday::UploadIO
object?
data is an hash of Faraday::UploadIO object as I describe before :
action_params_io = Faraday::UploadIO.new("/home/luc/parameters_import_neptune.json", "application/json", "parameters.json")
transport_data_io = Faraday::UploadIO.new("/home/luc/demo/15568799.xml", "application/xml", "15568799.xml")
payload = {
:file1 => action_params_io,
:file2 => transport_data_io,
}
Do you have any idea how to make this feature works?
I could make a fix but if you have a beginning of idea it could be useful.
I'm not very proud of this solution but it should be a fix for me. I create my own serializer and create fake class :
def self.multipart
new(IevMultipart)
rescue LoadError
end
class IevMultipart
def self.dump(data)
data
end
def self.load(data)
data
end
end
But I need now to have 2 sawyer configurations : one for json request and one for multipart.
@ldonnet I think a custom Serializer is the way to go. Could you get by with a single configuration if your custom serializer knows how to handle Faraday::UploadIO
as a special case?
But I need now to have 2 sawyer configurations : one for json request and one for multipart.
Oof. In a better world, that #encode_body
would take some other request details so the serializer can make informed decisions about how to encode that body. You may be able to get by with a single serializer that checks for any Faraday::UploadIO
objects in the data values to decide whether to encode as json or let Faraday handle it.