POST request using CURL for Rails App - ruby

This question has been asked by many of users and I have tried all solutions but none of them have worked for.for ex: This Question here curl json post request via terminal to a rails app but still the same result.
I am trying to POST data through terminal using CURL by running this command:
curl -i -H 'Authorization: Token token'="9asdadsasd87t8ddaghsd" -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{"request":{"sender_mobile":"12331212","recipient_mobile":"121231231","location":"Bangalore"}}' http://localhost:3000/api/v1/requests
and Getting reponse:
HTTP/1.1 422 Unprocessable Entity
Content-Type: text/html; charset=utf-8
Content-Length: 15450
X-Request-Id: f1025e69-9ff3-4bd1-9bd5-0679fbcc50bc
X-Runtime: 0.062784
Server: WEBrick/1.3.1 (Ruby/2.1.1/2014-02-24)
Date: Thu, 08 Jan 2015 10:35:45 GMT
Connection: Keep-Alive
with so much lines of garbage but I guess this response is sufficient to understand what might be the cause.Here is the link to complete error http://pastebin.com/n342HeYL
I am able to do GET request successfully with command:
curl -i 'http://localhost:3000/api/v1/requests' -H 'Authorization: Token token'="952aa4598ec2cd87c8b69056616a00af"
Response:
HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
Etag: "b956523e0345d2bb466d39823195b600"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: 91d21235-246f-4557-a67a-92dca02b9cc1
X-Runtime: 0.011781
Server: WEBrick/1.3.1 (Ruby/2.1.1/2014-02-24)
Date: Thu, 08 Jan 2015 10:51:55 GMT
Content-Length: 486
Connection: Keep-Alive
So, I dont know whats wrong in POST request.
Here is my request_controller.rb file
module Api
module V1
class RequestsController < ApplicationController
# skip_before_action :verify_authenticity_token
before_filter :restrict_access
respond_to :json
def index
respond_with Request.all
end
def show
respond_with Request.find(params[:id])
end
def create
respond_with Request.create(params[:request])
end
def update
respond_with Request.update(params[:id], params[:request])
end
def destroy
respond_with Request.destroy(params[:id])
end
private
def restrict_access
authenticate_or_request_with_http_token do |token, options|
ApiKey.exists?(access_token: token)
end
end
end
end
end

With respect to the conversation above, let me write an answer.
If you see a form in rails, it will have a line as mentioned below
<input name="authenticity_token" type="hidden" value="+wFCV3kv0X/WI0qb54BgYlDzi+Tp+6HIGM61a4O6gg0=">
Now, if in ApplicationController you have this line protect_from_forgery then it would always expect authenticity_token and if its not available, it will throw the error thats mentioned in your logs hence I suggested to remove that line because you won't be passing an authenticity_token as a param from you api POST requests.
The reason why your get request is working just fine is because rails doesn't expect authenticity_token on a GET request.
Now, you said you removed protect_from_forgery but you got an internal server error, well that has to be handled by you as it has got nothing to do with GET or POST request but it is an error in your rails application. So solve that error and it should work just fine. Or also post the error log here so may be I can help you with that.
EDIT: Solution to your internal server 500 error
With the logs pasted below, it is also necessary that you allow the attributes in your controller, just as #abhinay mentioned in the code.
Hope that helps

Related

Rspec Failure/Error Content-type expected:"json" got:"text/html"

I'm currently getting the following error when I run my rspec test
Failure/Error: expect(last_response.header['Content-Type']).to eq('application/json')
expected: "application/json"
got: "text/html"
(compared using ==)
I specify the content type in the method here
get '/restart/?' do
content_type :json
# token authentication
need_token!
# restart operation
exit_process
status 200
JSON.pretty_generate({ 'ok' => true, 'message' => 'Restarting ...' })
end
And when I run the curl get command I get the following out showing the Content-Type: application/json
curl -X GET -H X-AUTH-TOKEN:$TOKEN --url localhost:8080/restart -I
HTTP/1.1 200 OK
Content-Type: application/json
X-Content-Type-Options: nosniff
Vary: Accept-Encoding
Content-Length: 85
Why is rspec test returning "text/hmtl"?
Aloha!
I was working on a similar test recently! I can see a little difference in your approach defining the (sinatra) route. I have specified the Content-Type using headers and it looks like this:
headers['Content-Type'] = 'application/pdf'
Then in my test (I'm using RSpec) I'm doing this:
expect(last_response.header['Content-Type']).to eq('application/pdf')
Just simply put the headers in request
request.headers['Content-Type'] = "application/json"
Or you can modify according to requirements

How to properly format Http multipart/form-data request to upload file to the server

I have a simple node js server/app that receive files. I have tried uploading jpeg files with CURL and works perfectly.I have tried with postman also works fine.But when i try to upload jpeg with simple ruby script through Tcp socket it doesn't work.The request is received by the server but with no file object.In Node js server route i am debugging the request like so console.log(request.body); and this returns undefined. With Curl and postman i get a proper request object(file).This seems like my http request in the ruby script is not formatted properly, Can anybody point out what am i doing wrong here?Thank you in advance.Here is my ruby script.
require 'socket'
host = "127.0.0.1"
port = 8080
client = TCPSocket.open(host, port)
client.write("POST /api/binary HTTP/1.1\r\n")
client.write("Host: 127.0.0.1\r\n")
client.write ("Accept: */* \r\n")
client.write ("Content-Type: multipart/form-data; boundary=AaB03x\r\n")
client.write ("\r\n")
client.write("AaB03x"+ "\n" + "Content-Disposition: form-data; name='datafile'; filename='cam.jpg' \n Content-Type: image/jpeg \r\n")
data = File.open("./dom.jpg", "rb") {|io| io.read}
client.write (data)
client.write("boundary=AaB03x\r\n")
client.write ("\r\n")
client.close
Generated code snipet from postman
POST /api/binary HTTP/1.1
Host: myapp.herokuapp.com
Cache-Control: no-cache
Postman-Token: c15a79a2-3a4b-0555-a876-9032afeab5de
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name=""; filename=""
Content-Type:
----WebKitFormBoundary7MA4YWxkTrZu0gW
I was not passing Content-Length: length Final version that works is this one.
require 'socket'
host = "myapp.herokuapp.com"
port = 80
client = TCPSocket.open(host, port)
client.write("POST /api/binary HTTP/1.1\r\n")
client.write("Host: #{host}\r\n")
client.write ("Accept: */*\r\n")
client.write ("Content-Type: multipart/form-data; boundary=AaB03x\r\n")
body = "--AaB03x\r\n"
body << "Content-Disposition: form-data; name='datafile'; filename='cam.jpg'\r\n"
body << "Content-Type: image/jpeg\r\n"
body << "\r\n"
data = File.open("./pic.jpg", "rb") {|io| io.read}
body << data
body << "\r\n"
body << "--AaB03x--\r\n"
client.print "Content-Length: #{body.bytesize}\r\n"
client.print "\r\n"
client.print body
client.close

Reproduce curl request in ruby

I have next curl request:
curl -X POST -H "Content-Type: application/json" -H "charset: UTF-8" -H "Cache-Control: no-cache" -d '{"destId":684}' https://viatorapi.viator.com/service/search/products?apiKey=VIATOR_API_KEY
This curl request is working correct. If I will try to move apiKey parameter to data hash, I'm getting an error that apiKey is missing. For example
curl -X POST -H "Content-Type: application/json" -H "charset: UTF-8" -H "Cache-Control: no-cache" -d '{"destId":684, "apiKey":VIATOR_API_KEY}' https://viatorapi.viator.com/service/search/products
I can't understand what is the difference between this two requests.
Obviously, instead of VIATOR_API_KEY I'm using real value.
And now I'm trying to reproduce this curl request in ruby.
require 'uri'
require 'net/http'
require 'json'
API_KEY = ENV['VIATOR_API_KEY']
BASE_URL = "http://viatorapi.viator.com/service"
path = "/search/products"
# Full reference
uri = URI.parse "#{BASE_URL}#{path}?apiKey=#{API_KEY}"
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.initialize_http_header({"Content-type" => "application/json", "charset" => "UTF-8", "Cache-Control" => "no-cache"})
request.set_form_data('destId' => 684)
response = http.request(request)
puts response.body
if response.code == "200"
# Do something
end
Now I'm getting 415 error Unsupported Media Type.
Any ideas what could be the problem?
UPDATE
I've figured out what it has been due to.
Before setting data parameters request.set_form_data('destId' => 684)
request header is
{"content-type"=>["application/json"], "charset"=>["UTF-8"], "cache-control"=>["no-cache"]}
after
{"content-type"=>["application/x-www-form-urlencoded"], "charset"=>["UTF-8"], "cache-control"=>["no-cache"]}
So, somehow it set_form_data changed content-type
I think that you can use the gem Typhoeus is a curl wrapper is quite easy to translate curl request, here is the example for your request
require 'typhoeus'
request = Typhoeus::Request.new(
"https://viatorapi.viator.com/service/search/products",
method: :post,
params: { apiKey: "VIATOR_API_KEY" },
body: { destId: 684},
headers: { 'Content-Type' => "application/json", charset: "UTF-8",'Cache-Control' => "no-cache" }
)
request.on_complete do |response|
if response.success?
# hell yeah
elsif response.timed_out?
# aw hell no
log("got a time out")
elsif response.code == 0
# Could not get an http response, something's wrong.
log(response.return_message)
else
# Received a non-successful http response.
log("HTTP request failed: " + response.code.to_s)
end
end
request.run
#curl -X POST -H "Content-Type: application/json" -H "charset: UTF-8" -H "Cache-Control: no-cache" -d '{"destId":684}' https://viatorapi.viator.com/service/search/products?apiKey=VIATOR_API_KEY
executing:
irb(main):112:0' => #<Typhoeus::Response:0x007fd46396b920 #options={:httpauth_avail=>0, :total_time=>0.933899, :starttransfer_time=>0.93374, :appconnect_time=>0.52442, :pretransfer_time=>0.5244530000000001, :connect_time=>0.25838099999999997, :namelookup_time=>0.000657, :redirect_time=>0.0, :effective_url=>"https://viatorapi.viator.com/service/search/products?apiKey=VIATOR_API_KEY", :primary_ip=>"54.165.220.73", :response_code=>200, :request_size=>269, :redirect_count=>0, :return_code=>:ok, :response_headers=>"HTTP/1.1 200 OK\r\nDate: Thu, 28 Apr 2016 17:24:23 GMT\r\nContent-Type: application/json;charset=ISO-8859-1\r\nContent-Length: 372\r\nVary: Accept-Encoding\r\n\r\n", :response_body=>"{\"errorReference\":\"~21084218484370761736807418\",\"data\":null,\"dateStamp\":\"2016-04-28T10:24:23+0000\",\"errorType\":\"EXCEPTION\",\"errorMessage\":[\"API ACCESS DENIED!!! Api Key 'VIATOR_API_KEY' is invalid or missing\"],\"errorName\":\"Exception\",\"success\":false,\"totalCount\":1,\"vmid\":\"331004\",\"errorMessageText\":[\"API ACCESS DENIED!!! Api Key 'VIATOR_API_KEY' is invalid or missing\"]}", :debug_info=>#<Ethon::Easy::DebugInfo:0x007fd463988ca0 #messages=[]>}, #request=#<Typhoeus::Request:0x007fd464810f98 #base_url="https://viatorapi.viator.com/service/search/products", #original_options={:method=>:post, :params=>{:apiKey=>"VIATOR_API_KEY"}, :body=>{:destId=>684}, :headers=>{"Content-Type"=>"application/json", :charset=>"UTF-8", "Cache-Control"=>"no-cache"}}, #options={:method=>:post, :params=>{:apiKey=>"VIATOR_API_KEY"}, :body=>{:destId=>684}, :headers=>{"User-Agent"=>"Typhoeus - https://github.com/typhoeus/typhoeus", "Content-Type"=>"application/json", :charset=>"UTF-8", "Cache-Control"=>"no-cache"}, :maxredirs=>50}, #response=#<Typhoeus::Response:0x007fd46396b920 ...>, #on_headers=[], #on_complete=[#<Proc:0x007fd464810e58#/Users/toni/learn/ruby/stackoverflow/scripting/typhoeus_request.rb:11>], #on_success=[]>, #handled_response=nil>
irb(main):113:0>
a little bit pretty
irb(main):039:0> require 'yaml'
=> true
irb(main):043:0> puts request.response.to_yaml
--- &3 !ruby/object:Typhoeus::Response
options:
:httpauth_avail: 0
:total_time: 0.6263529999999999
:starttransfer_time: 0.6262380000000001
:appconnect_time: 0.264124
:pretransfer_time: 0.264161
:connect_time: 0.131438
:namelookup_time: 0.00059
:redirect_time: 0.0
:effective_url: https://viatorapi.viator.com/service/search/products?apiKey=VIATOR_API_KEY
:primary_ip: 54.165.220.73
:response_code: 200
:request_size: 269
:redirect_count: 0
:return_code: :ok
:response_headers: "HTTP/1.1 200 OK\r\nDate: Fri, 29 Apr 2016 06:25:36 GMT\r\nContent-Type:
application/json;charset=ISO-8859-1\r\nContent-Length: 371\r\nVary: Accept-Encoding\r\n\r\n"
:response_body: '{"errorReference":"~2155052986177618334013969","data":null,"dateStamp":"2016-04-28T23:25:36+0000","errorType":"EXCEPTION","errorMessage":["API
ACCESS DENIED!!! Api Key ''VIATOR_API_KEY'' is invalid or missing"],"errorName":"Exception","success":false,"totalCount":1,"vmid":"331009","errorMessageText":["API
ACCESS DENIED!!! Api Key ''VIATOR_API_KEY'' is invalid or missing"]}'
:debug_info: !ruby/object:Ethon::Easy::DebugInfo
messages: []
request: !ruby/object:Typhoeus::Request
base_url: https://viatorapi.viator.com/service/search/products
original_options:
:method: :post
:params: &1
:apiKey: VIATOR_API_KEY
:body: &2
:destId: 684
:headers:
Content-Type: application/json
:charset: UTF-8
Cache-Control: no-cache
options:
:method: :post
:params: *1
:body: *2
:headers:
User-Agent: Typhoeus - https://github.com/typhoeus/typhoeus
Content-Type: application/json
:charset: UTF-8
Cache-Control: no-cache
:maxredirs: 50
on_complete:
- !ruby/object:Proc {}
on_headers: []
response: *3
on_success: []
handled_response:
=> nil
I couldn't find the API documentation for that site, but it appears that the endpoint expects apiKey to be a url query parameter and not in the payload.
If you want to stick to pure Ruby and Net::HTTP then I recommend requests, a wrapper on Net::HTTP that makes it very easy to send requests.

Authenticate via Ruby into Jira

Authenticate via Ruby into Jira and post changes via ZAPI (Zephyr API)
What I'm trying to do: I'm trying to authenticate via Ruby into Jira and then make changes to Zephyr (Jira plugin w/Z(ephyr)API) issues.
I'm trying to execute this code (taken from: http://docs.getzephyr.apiary.io/#executionresourceapis)
require 'rubygems' if RUBY_VERSION < '1.9'
require 'rest_client'
values = "{\n \"issueId\": 10600,\n \"versionId\": \"10000\",\n \"cycleId\": \"16\",\n \"projectId\": 10000\n}"
headers = {:content_type => "application/json"}
response = RestClient.post "http://getzephyr.apiary-mock.com/jira_server/rest/zapi/latest/execution", values, headers
puts response
My main issue is I cannot authenticate my Jira instance via Ruby. My attempts include (among countless others):
A) I think this works (even though this works, I don't think it'll work for my use b/c I need to pass in other values to change Jira/Zephyr tickets):
curl -D- -u fyousuf:password -X GET -H Content-Type: application/json http://jira.test.local/rest/zapi/latest/execution
(tried curl via https://developer.atlassian.com/display/JIRADEV/JIRA+REST+API+Example+-+Basic+Authentication)
A) Output:
curl: (6) Could not resolve host: application; nodename nor servname provided, or not known
HTTP/1.1 200 OK
Server: nginx/1.0.11
Date: Wed, 06 Aug 2014 20:14:35 GMT
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
X-AREQUESTID: 974x1935522x1
Set-Cookie: JSESSIONID=5F5D8411206C9B99054CABAD93AAE715; Path=/; HttpOnly
X-Seraph-LoginReason: OK
Set-Cookie: atlassian.xsrf.token=A8NH-S9GW-2ZYX-9R4V|a6024d3cb5c5585d2b6f765001ebfefa67dd5a7a|lin; Path=/
X-ASESSIONID: 1x3gunc
X-AUSERNAME: fyousuf
Cache-Control: no-cache, no-store, no-transform
{"status":{"1":{"id":1,"color":"#75B000","description":"Test was executed and passed successfully.","name":"PASS"},"2":{"id":2,"color":"#CC3300","description":"Test was executed and failed.","name":"FAIL"},"3":{"id":3,"color":"#F2B000","description":"Test execution is a work-in-progress.","name":"WIP"},"4":{"id":4,"color":"#6693B0","description":"The test execution of this test was blocked for some reason.","name":"BLOCKED"},"-1":{"id":-1,"color":"#A0A0A0","description":"The test has not yet been executed.","name":"UNEXECUTED"}},"executions":[],"currentlySelectedExecutionId":""}
B) This doesn't work:
values = "{\n \"issueId\": 32640,\n \"versionId\": \"11163\",\n \"cycleId\": \"5\",\n \"projectId\": 10460\n,\n \"status\": \"1\"}"
headers = {:content_type => "application/json"}
auth = 'Basic ' + Base64.encode64( 'fyousuf:password' ).chomp
url = 'http://jira.test.local/rest/zapi/latest/execution'
resource = RestClient::Resource.new( url )
response = resource.post( :Authorization => auth )
#response = resource.post "http://jira.test.local/rest/zapi/latest/execution", values, headers
puts response
puts response.code
B) Output:
{"errorDesc":"You do not have the permission to make this request. Login Required.","errorId":"ERROR"}
200
I'm thinking that if I'm able to authenticate into Jira with 'curl' then via Ruby should work too, but not sure what I'm doing wrong...
to access Jira's API from Ruby I use following construction. The net/http object does the work. For clearity I removed error-handling and logging from the example.
require 'net/http'
require 'json'
http = Net::HTTP.new(host, port)
resp = nil
http.start do |http|
req = Net::HTTP::Post.new(api call + parameter)
req.add_field('Content-Type', 'application/json') if "POST" == req.method
req.add_field('Accept', 'application/json')
# we make an HTTP basic auth by passing the
# username and password
req.basic_auth username, password
resp = http.request(req)
end
return resp.body

Why cookie doesn't work using Sinatra and Omniauth?

I need to change a cookie from "Session" type to "Persistent" type. Moreover I need to have it working together with "omniauth-facebook". In the example below everything is working well but when I use omniauth the cookie is not set at all. This is the test code I wrote:
require 'rubygems'
require 'sinatra'
require 'encrypted_cookie'
require 'omniauth-facebook'
use Rack::Session::EncryptedCookie, :secret => "fdstopitot9dasdsdasjm4kmt0èu54cmjff83d2'ìel.4j9c"
use OmniAuth::Builder do
provider :facebook, '290594154312564','a26bcf9d7e254db82566f31c9d72c94e'
end
get "/" do
"persistent | session | /auth/facebook"
end
get "/persistent" do
response.set_cookie 'test', {:value=> "persistent", :max_age => 2592000.to_s}
redirect "/"
end
get "/session" do
response.set_cookie 'test', {:value=> "session"}
redirect "/"
end
get '/auth/:provider/callback' do
response.set_cookie 'test', {:value=> "facebook_callback"}
redirect "/"
end
Clicking on /session or /persistent you can see the cookie changing accordingly (in Chrome you can check cookies with F12 > Resources > Cookies > localhost).
Clicking instead on /auth/facebook the cookie is not set at all.
The response header seems ok (in Chrome you can see the http response header with F12 > Network > header). I only tested it with Chrome.
HTTP/1.1 302 Moved Temporarily
X-Frame-Options: SAMEORIGIN
Location: http://localhost:4567/
X-XSS-Protection: 1; mode=block
Content-Type: text/html;charset=utf-8
Content-Length: 0
Set-Cookie: test=facebook_callback
Set-Cookie: rack.session=X8U8kupLYzIurjMS4pSCQfF%2BzPpjQhJMqyMd84o8BQdQLwmhagL1UkZ4oi7%2F%0A9bEN%2B0FZDDUAeQD%2BRizczwvepQi%2FbcMwaAjpkFcXhiWuJPQ%3D%0A; path=/
X-Content-Type-Options: nosniff
Connection: keep-alive
Server: thin 1.5.1 codename Straight Razor
Any clue?
Is was a trivial problem, just add the path to the cookie: response.set_cookie 'test', {:value=> "facebook_callback", :path => "/"}
The reason why I did not notice was that there is a redirect to "/", so Chrome was only showing me the cookies for the path "/". Removing the redirect, I notice I have two cookies named "test". One with "/" path and the other with "/auth" path.

Resources