I am using the curb gem to do a Curl Multi post using JSON data. However I am unable to actually get the parameters to get posted and have been unable to figure out how to properly configure the parameters.
urls = [
{
:url => "http://localhost:5000/",
:method => :post,
:headers => {'Accept' => 'application/json', 'Content-Type' => 'application/json'},
:post_fields => {'field1' => 'value1', 'k' => 'j'}
}
]
Curl::Multi.http(urls) do |easy, code, method|
puts "#{easy.body_str.inspect}, #{method.inspect}, #{code.inspect}"
end
=>
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n<title>400 Bad Request</title>\n<h1>Bad Request</h1>\n<p>The browser (or proxy) sent a request that this server could not understand.</p>\n", :post, nil
Do that:
urls = [
{
:url => "http://localhost:5000/",
:method => :post,
:headers => {'Accept' => 'application/json', 'Content-Type' => 'application/json'},
:post_fields => {},
:post_body => {'field1' => 'value1', 'k' => 'j'}.to_json,
}
]
The problem: curb doesn't know that you are sending a JSON data. Curb don't read and interprets the contents of :headers. As you can see here, curb transforms your hash into a string separated by "&", which is the default for a normal (non-json) http data sending (eg.: "field1=value1&k=j"). When the server (Rails) read and interprets the header explicity saying that the data is in JSON format, it tries to decode and the result is the same exception that you get when you do that: JSON.parse("field1=value1&k=j").
To solve this, you need to send "post_fields" as an empty hash, and send your actual data by using "post_body". Also, you need to convert your hash to json manually with to_json.
I don't know if they (the curb project owners) know this problem, but I suggest you to warning them about it.
Related
i am new to ruby RestClient. i have search many example of this restclient and in docruby. For me is important while using ruby restclient, to get the data very fast.
But some are not answers, this is why i would like to question to you all.
i am working on this ruby restclient example Code:
restClient = RestClient::Request.new(
:method => :get,
:url => url,
:verify_ssl => true, #required using https
:content_type => :json,
:accept => :json,
:headers => {
:Authorization => "Bearer #{token}",
}
)
result = restClient.execute()
My first question is what is different of using double point and astrophobe?
restClient = RestClient::Request.new(
:method => :get,
:method => 'get',
...
)
Second question is, is sequences/order in Code important like first url then method or method then url and so on?
restClient = RestClient::Request.new(
:url => :url,
:method => :get,
...
)
#or
restClient = RestClient::Request.new(
:method => :get,
:url => :url,
...
)
third question is, about accept to put in headers. some put accept and content-type in headers and some not, what is different?
restClient = RestClient::Request.new(
:content_type => 'application/json',
:accept => 'application/json',
#or
:headers => {
'hello-token' => "Bearer #{token}",
'content_type'=> 'application/json',
'ACCEPT' => 'application/json'
}
)
What is different of using double point and astrophobe?
:get is a Symbol, 'get' is a String.
It depends on the implementation of the gem if the gem is able to process both. Because the RestClient documentation uses a Symbol in its examples I would recommend doing the same.
But actually – at least in its current version - it doesn' make a difference because the gem translate the argument internally into a string anyway (see initialize and normalize_method)
is sequences/order in Code important
In theory, a hash is an unordered data structure. Therefore the order should not be important in this case. But keep in mind that Ruby's implementation of a hash is actually preserving the order in which the keys are inserted when iterating the hash.
Accept headers
I didn't find any example in the gem's documentation in which they used the first version. Did you actually try both versions? I would be surprised when both worked. Therefore I suggest using the header: version.
I am trying to create some simple Ruby code to add emails using the Campaign Monitor API. Below is my code.
require 'httparty'
require 'json'
def request
url = 'https://api.createsend.com/api/v3.1/subscribers/MYLISTID.json'
auth = {:username => 'MYAPIKEY', :password => 'x'}
response = HTTParty.post(url,
:basic_auth => auth, :body => {
'EmailAddress' => 'mike#hotmail.com',
'Name' => 'Test',
'Resubscribe' => true,
'RestartSubscriptionBasedAutoresponders' => true
})
puts response
puts response.code
end
request
I can connect with the API. However, when I try to add the email I am getting the following response.
{"Code"=>400, "Message"=>"Failed to deserialize your request.
Please check the documentation and try again.
Fields in error: subscriber"}
400
When I change the request to get instead of put
my response is:
{"Code"=>1, "Message"=>"Invalid Email Address"}
I can't understand what I am doing wrong as I have followed the documentation on the Campaign Monitor API
It looks like you have everything setup correctly, you just need to turn the body of the post into a json string.
response = HTTParty.post(url,
:basic_auth => auth, :body => {
'EmailAddress' => 'mike#hotmail.com',
'Name' => 'Test',
'Resubscribe' => true,
'RestartSubscriptionBasedAutoresponders' => true
}.to_json)
I'd like to point out that a Campaign Monitor API gem also exists that will do all of that work for you.
Campaign Monitor API Gem
I received the following code from a development team:
curl -u EMAILADDRESS:PASSWORD -d "sender=NAME <EMAILADDRESS>&message=[Invite Link]&collector=COLLECTOR&subject=Test Invite&footer=My Custom Text [Unsubscription Link]"
I have been told that the above works fine. This is what I translated it to in Ruby 1.9.3, using the httparty gem:
call= "/api/v2/emails/?survey=#{i}"
puts collector_final_id
url= HTTParty.post("https://www.fluidsurveys.com#{call}",
:basic_auth => auth,
:headers => { 'Content-Type' => 'application/x-www-form-urlencoded','Accept' => 'application/x-www-form-urlencoded' },
:collector => collector,
:body => {
"subject" => "Test Invite",
"sender" => "NAME <EMAILADDRESS>",
"message" => "[Invite Link]"
},
:footer => "My Custom Text [Unsubscription Link]"
)
Everything within this works fine except for the :footer and :collector parameters. It doesn't seem to recognize them at all.
There are no errors thrown, they just aren't included in the actual email I am sending. What am I doing wrong when passing in those two parameters?
Your :collector and :footer are not correct.
I wrote a little Sinatra service to receive a POST request with any parameters:
require 'pp'
require 'sinatra'
post "/*" do
pp params
end
And ran it, launching the web-server on my Mac OS laptop. As Sinatra apps do, it resides at 0.0.0.0:4567.
Running this code:
require 'httparty'
url = HTTParty.post(
"http://localhost:4567/api/v2/emails?survey=1",
:headers => {
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept' => 'application/x-www-form-urlencoded'
},
:body => {
"subject" => 'subject',
"sender" => 'sender',
"message" => 'message',
},
:collector => 'collector',
:footer => 'footer'
)
puts url
Outputs:
["survey", "1"]["subject", "subject"]["sender", "sender"]["message", "message"]["splat", ["api/v2/emails"]]["captures", ["api/v2/emails"]]
Sinatra said:
127.0.0.1 - - [11/Sep/2013 17:58:47] "POST /api/v2/emails?survey=1 HTTP/1.1" 200 - 0.0163
{"survey"=>"1",
"subject"=>"subject",
"sender"=>"sender",
"message"=>"message",
"splat"=>["api/v2/emails"],
"captures"=>["api/v2/emails"]}
Changing :collector and :footer to strings and moving them inside the body, where they should be:
require 'httparty'
url = HTTParty.post(
"http://localhost:4567/api/v2/emails?survey=1",
:headers => {
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept' => 'application/x-www-form-urlencoded'
},
:body => {
"subject" => 'subject',
"sender" => 'sender',
"message" => 'message',
'collector' => 'collector',
'footer' => 'footer'
},
)
puts url
Outputs:
["survey", "1"]["subject", "subject"]["sender", "sender"]["message", "message"]["collector", "collector"]["footer", "footer"]["splat", ["api/v2/emails"]]["captures", ["api/v2/emails"]]
And Sinatra said:
127.0.0.1 - - [11/Sep/2013 18:04:13] "POST /api/v2/emails?survey=1 HTTP/1.1" 200 - 0.0010
{"survey"=>"1",
"subject"=>"subject",
"sender"=>"sender",
"message"=>"message",
"collector"=>"collector",
"footer"=>"footer",
"splat"=>["api/v2/emails"],
"captures"=>["api/v2/emails"]}
The problem is, the POST request ONLY uses a URL and a :body hash. Inside the :body hash go all the variables you're sending to the server. That's why the second version of the code, with 'collector' and 'footer' works.
There is no comma after your :body parameter
I'm working on a ruby application, and am trying to upload a file to box.net. I have it working with the curl call
curl https://www.box.com/api/2.0/files/data -H "Authorization: BoxAuth api_key=<API_KEY>&auth_token=<AUTH_TOKEN>" -F folder_id=0 -F filename=#test.txt --trace ~/Desktop/log.txt
I've tried to translate this into ruby, and have tried the following
request = RestClient::Request.new(:method => :post,:url => "https://www.box.com/api/2.0/files/data",:authorization => "BoxAuth api_key=<API_KEY>&auth_token=<AUTH_TOKEN>",:filename => "test.txt", :payload => { :multipart => true, :file => File.new("test.txt"))
request.execute
but I keep getting back a "401: Unauthorized" response. I've also tried using the box-api gem, but that seems to only work with version 1.0 of the API, and I'm trying to interface with 2.0.
Try to use :headers => {:authorization => "BoxAuth api_key=<API_KEY>&auth_token=<AUTH_TOKEN>"} in the call. That should fix the missing authorization header.
Complete request would then be:
request = RestClient::Request.new(:method => :post,:url => "https://www.box.com/api/2.0/files/data",:headers => {:authorization => "BoxAuth api_key=<API_KEY>&auth_token=<AUTH_TOKEN>"},:filename => "test.txt", :payload => { :multipart => true, :file => File.new("test.txt")})
I have tried to do the following, but the web-service is NOT REST and does not take multi-part. What do I do in order to POST the image?
#response = RestClient.post('http://www.postful.com/service/upload',
{:upload => {
:file => File.new("#{#postalcard.postalimage.path}",'rb')
}
},
{"Content-Type" => #postalcard.postalimage.content_type,
"Content-Length" => #postalcard.postalimage.size,
"Authorization" => 'Basic xxxxxx'
} # end headers
) #close arguments to Restclient.post
Got the answer: use I/O to stream as a string instead of using File.new....
:file => IO.read("#{#postalcard.postalimage.path}")