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

Use sawyer with multipart POST? #36

Open
ldonnet opened this issue Apr 20, 2015 · 7 comments
Open

Use sawyer with multipart POST? #36

ldonnet opened this issue Apr 20, 2015 · 7 comments

Comments

@ldonnet
Copy link

ldonnet commented Apr 20, 2015

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?

@ldonnet
Copy link
Author

ldonnet commented Apr 24, 2015

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

@pengwynn
Copy link
Member

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?

@ldonnet
Copy link
Author

ldonnet commented Apr 24, 2015

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,
                        }   

@ldonnet
Copy link
Author

ldonnet commented Apr 27, 2015

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.

@ldonnet
Copy link
Author

ldonnet commented Apr 27, 2015

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.

@pengwynn
Copy link
Member

@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?

@technoweenie
Copy link
Member

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants