How to send JSON as form data using Faraday's post method
Asked Answered
P

2

5

How should I send this JSON in Faraday using the post method with the "application/x-www-form-urlencoded" and "multipart/form-data;" headers?

message = {
  "name":"John",
  "age":30,
  "cars": {
    "car1":"Ford",
    "car2":"BMW",
    "car3":"Fiat"
  }
 }

I've tried:

conn = Faraday.new(url: "http://localhost:8081") do |f|
  f.request :multipart
  f.request :url_encoded
  f.adapter :net_http
end

conn.post("/", message)

This cURL request works

curl -X POST \
  http://localhost:8081 \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
  -F 'message=2018-12-27 12:52' \
  -F source=RDW \
  -F object_type=Responses

But I don't quite know how to get this working in Faraday. Also the data in the cURL request isn't nested JSON, so I need to be able to dynamically create the body of the request as I won't know ahead of time the exact structure of the JSON.

And please ask any questions if you need more details or clarity.

Thanks!

Playacting answered 10/1, 2019 at 21:39 Comment(0)
B
6

The default content type for POST is x-www-form-urlencoded so a hash will be automatically encoded. There isn't such automatic data handling for JSON which is why the second example below passes in a stringified representation of the hash.

Faraday.new(url: 'http://localhost:8081').post('/endpoint', {foo: 1, bar: 2})
# => POST http://localhost:8081/endpoint
#         with body 'bar=2&foo=1'
#         including header 'Content-Type'=>'application/x-www-form-urlencoded'

Faraday.new(url: 'http://localhost:8081').post('/endpoint', {foo: 1, bar: 2}.to_json, {'Content-Type'=>'application/json'})
# => POST http://localhost:8081/endpoint
#         with body '{"foo":1,"bar":2}'
#         including header 'Content-Type'=>'application/json'

I'm not sure what you're intending to do but you could send something like the following

Faraday.new(url: 'http://localhost:8081').post('/endpoint', {foo: 1, bar: 2}.to_json)
# => POST http://localhost:8081/endpoint
#         with body '{"foo":1,"bar":2}'
#         including header 'Content-Type'=>'application/x-www-form-urlencoded'

However, this would be interpreted to be {"{\"foo\":1,\"bar\":2}" => nil} in Ruby. If you're parsing the data at the other end you can make it work but it's always harder to fight convention.

Batfish answered 28/1, 2019 at 22:42 Comment(2)
Sorry to revive this, but I just ran into the problem you describe and I don't quite understand your comment completely. If I pass a Ruby hash to the Faraday connector it is stringified as you described so the request fails (bad request error). The only workaround is passing the params directly between {}, but anything else like passing a Ruby hash.to_json does not.Skinned
Faraday uses convention to assume that any content you're sending in a POST request is of the type application/x-www-form-urlencoded. Often we want to POST JSON instead so we need a couple of mechanisms. The to_json method turns a Ruby hash into its JSON representation which is a string. However, Faraday cannot distinguish between any old string and a string that represents JSON data. That's why you must supply the Content-Type header of application/json in order for the receiver to know you're sending JSON and not poorly formatted form data.Batfish
B
2

For POST requests, Faraday wants the form data as a JSON string and not as a Ruby hash. This can easily be accomplished by using the json gem's Hash#to_json method like so:

require 'json'

url       = 'http://localhost:8081'
form_data = {
  name: 'John',
  age: '30',
  cars: {
    car1: 'Ford',
    car2: 'BMW',
    car3: 'Fiat'
  }
}.to_json

headers = {} 

Faraday.post(url, form_data, headers)

or in your instance just simply:

conn = Faraday.new(url: "http://localhost:8081") do |f|
  f.request :multipart
  f.request :url_encoded
  f.adapter :net_http
end

# exact same code, except just need to call require json and call to_json here
require 'json'
conn.post("/", message.to_json)
Blitz answered 26/3, 2020 at 20:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.