Faraday: Need help with uploading json and file as multipart/form-data

Created on 5 Jan 2020  ·  3Comments  ·  Source: lostisland/faraday

Below is what I need to upload

-data-binary $'

------WebKitFormBoundaryInmulX6ait6ZMLdu\r\n
Content-Disposition: form-data; name="message"; filename="blob"\r\n
Content-Type: application/json
{
 "title":"title of the message to be sent",
 "body":"body of the message to be sent",
 "attachedFileName":null,
 "attachedFileName2":null,
 "attachedFileName3":null,
 "recipientIdList":[186554]
}
 ------WebKitFormBoundaryInmulX6ait6ZMLdu--\r\n' --compressed

Please note content type that the server receives is multipart/form-data

my current code looks like below:

client = Faraday::Connection.new(url: BASE_URL) do |builder|
    builder.use :cookie_jar
    builder.use :multipart
    builder.use :url_encoded
    builder.adapter :net_http
end

message = {title: "title", body: "body of message", recipientIdList:[186554]}
payload = {message: JSON.dump(message)}

response = @client.post(URL) do |request|
    request.headers['Content-Type'] = 'multipart/form-data'
    request.body = payload
end

However, the response I get from the server is {"success"=>false, "errorCode"=>nil, "message"=>"Content type 'application/octet-stream' not supported", "data"=>nil}

I tweaked the code little by little and tried again, and searched google for hours but could not find solution.
It would be greatly appreciated if someone can help me with this.
Thank you in advance :D

P.S. this issue seems to be related with https://github.com/lostisland/faraday/issues/830#issue-372589645 and https://github.com/lostisland/faraday/issues/769#issue-295426091 , but I cannot find solution.. Please help..

Most helpful comment

The issue is with the way the payload is constructed. According to your curl -data-binary snippet, your server wants the message form value to have the application/json content-type. However, the constructed payload doesn’t specify this anywhere.

Here’s how to fix it in Faraday v1.0. See more about the Multipart Middleware

-payload = {message: JSON.dump(message)}
+payload = { message: Faraday::ParamPart.new(JSON.dump(message), ‘application/json’) }

In Faraday v0.1x (confirmed in 0.17.3). Note that this still sends a “file” that's just reading from an in-memory IO object. Hopefully the server doesn’t mind the extra multipart header values. You can see the details in the 3rd request at the bottom of this message.

-payload = {message: JSON.dump(message)}
+{ message: Faraday::UploadIO.new(StringIO.new(JSON.dump(message)), 'application/json') }

I don’t know what to make of the error message your server replied with though.

Content type ‘application/octet-stream’ not supported

I'm hoping that fixing the multipart content type issue will solve it, as I don't see where application/octet-stream is coming from.

Let me know if you have any more questions!

Showing my work

You can see the working code snippets in multipart.rb · GitHub. Since the RequestBin that the code references only holds the request info temporarily, I’m recording the details below:

Request with NO content type in payload:

# AMZ/Cloudfront headers removed
Content-Type: multipart/form-data; boundary=—————RubyMultipartPost-10fbc5eb433c89c0eca87785f09ae45d
Connection: close
Connect-Time: 1
X-Request-Id: e802a260-56a9-4d01-a4ee-77f652380185
Content-Length: 253
Host: requestbin.io
User-Agent: Faraday v1.0.0
Accept: */*

——————RubyMultipartPost-10fbc5eb433c89c0eca87785f09ae45d
Content-Disposition: form-data; name=“message”

{“title”:”title”,”body":"body of message”,”recipientIdList":[186554]}
——————RubyMultipartPost-10fbc5eb433c89c0eca87785f09ae45d——————

Request WITH content type (Faraday 1.0)

Content-Type: multipart/form-data; boundary=—————RubyMultipartPost-9d77ee5700e0658dddb09ac41fad5fa4

Connection: close
Connect-Time: 0
Content-Length: 285
User-Agent: Faraday v1.0.0
X-Request-Id: 2bbb3705-42ec-4de2-877c-2afd588ed679
Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept: */*
Host: requestbin.io

——————RubyMultipartPost-9d77ee5700e0658dddb09ac41fad5fa4
Content-Disposition: form-data; name=“message”
Content-Type: application/json

{“title”:”title”,”body":"body of message”,”recipientIdList”:[186554]}
——————RubyMultipartPost-9d77ee5700e0658dddb09ac41fad5fa4——————

Request WITH content type (Faraday v0.17.3)

Content-Type: multipart/form-data; boundary=—————RubyMultipartPost-697c8614200bc0d7ae2fcc0215a2a697
Connection: close
Content-Length: 363
User-Agent: Faraday v0.17.3
X-Request-Id: af65fc06-3914-4ab6-8dbf-b347a1ac498a
Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept: */*
Host: requestbin.io

——————RubyMultipartPost-697c8614200bc0d7ae2fcc0215a2a697
Content-Disposition: form-data; name=“message”; filename=“local.path"
Content-Length: 69
Content-Type: application/json
Content-Transfer-Encoding: binary

{“title”:”title”,”body”:”body of message”,”recipientIdList”:[186554]}
——————RubyMultipartPost-697c8614200bc0d7ae2fcc0215a2a697—

All 3 comments

The issue is with the way the payload is constructed. According to your curl -data-binary snippet, your server wants the message form value to have the application/json content-type. However, the constructed payload doesn’t specify this anywhere.

Here’s how to fix it in Faraday v1.0. See more about the Multipart Middleware

-payload = {message: JSON.dump(message)}
+payload = { message: Faraday::ParamPart.new(JSON.dump(message), ‘application/json’) }

In Faraday v0.1x (confirmed in 0.17.3). Note that this still sends a “file” that's just reading from an in-memory IO object. Hopefully the server doesn’t mind the extra multipart header values. You can see the details in the 3rd request at the bottom of this message.

-payload = {message: JSON.dump(message)}
+{ message: Faraday::UploadIO.new(StringIO.new(JSON.dump(message)), 'application/json') }

I don’t know what to make of the error message your server replied with though.

Content type ‘application/octet-stream’ not supported

I'm hoping that fixing the multipart content type issue will solve it, as I don't see where application/octet-stream is coming from.

Let me know if you have any more questions!

Showing my work

You can see the working code snippets in multipart.rb · GitHub. Since the RequestBin that the code references only holds the request info temporarily, I’m recording the details below:

Request with NO content type in payload:

# AMZ/Cloudfront headers removed
Content-Type: multipart/form-data; boundary=—————RubyMultipartPost-10fbc5eb433c89c0eca87785f09ae45d
Connection: close
Connect-Time: 1
X-Request-Id: e802a260-56a9-4d01-a4ee-77f652380185
Content-Length: 253
Host: requestbin.io
User-Agent: Faraday v1.0.0
Accept: */*

——————RubyMultipartPost-10fbc5eb433c89c0eca87785f09ae45d
Content-Disposition: form-data; name=“message”

{“title”:”title”,”body":"body of message”,”recipientIdList":[186554]}
——————RubyMultipartPost-10fbc5eb433c89c0eca87785f09ae45d——————

Request WITH content type (Faraday 1.0)

Content-Type: multipart/form-data; boundary=—————RubyMultipartPost-9d77ee5700e0658dddb09ac41fad5fa4

Connection: close
Connect-Time: 0
Content-Length: 285
User-Agent: Faraday v1.0.0
X-Request-Id: 2bbb3705-42ec-4de2-877c-2afd588ed679
Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept: */*
Host: requestbin.io

——————RubyMultipartPost-9d77ee5700e0658dddb09ac41fad5fa4
Content-Disposition: form-data; name=“message”
Content-Type: application/json

{“title”:”title”,”body":"body of message”,”recipientIdList”:[186554]}
——————RubyMultipartPost-9d77ee5700e0658dddb09ac41fad5fa4——————

Request WITH content type (Faraday v0.17.3)

Content-Type: multipart/form-data; boundary=—————RubyMultipartPost-697c8614200bc0d7ae2fcc0215a2a697
Connection: close
Content-Length: 363
User-Agent: Faraday v0.17.3
X-Request-Id: af65fc06-3914-4ab6-8dbf-b347a1ac498a
Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept: */*
Host: requestbin.io

——————RubyMultipartPost-697c8614200bc0d7ae2fcc0215a2a697
Content-Disposition: form-data; name=“message”; filename=“local.path"
Content-Length: 69
Content-Type: application/json
Content-Transfer-Encoding: binary

{“title”:”title”,”body”:”body of message”,”recipientIdList”:[186554]}
——————RubyMultipartPost-697c8614200bc0d7ae2fcc0215a2a697—

Thank you very much for your help, @technoweenie ! You're awesome!!
The request now works just fine with the newly constructed payload, and it does not complain about the application/octet-stream anymore.
Hope you have a great day :D

Glad to hear it works as expected now!
Well done @technoweenie, thanks for tackling this tricky issue so quickly 👏 !

Was this page helpful?
0 / 5 - 0 ratings