class WorkoutScreen < PM::WebScreen
title "Workouts"
def content
#response
end
def on_load
set_nav_bar_button :left, title: "Menu", action: :nav_left_button
end
def load_started
#response = ''
BubbleWrap::HTTP.get("my_url", {async: false, :headers => { "User-Agent" => "value"}}) do |response|
#response = response.body.to_str
end
end
def nav_left_button
app_delegate.menu.show(:left)
end
end
I need to send HTTP request with specific header, but content always nil . I have checked response by sniffer - everything is Ok.
If I do this way
class WorkoutScreen < PM::WebScreen
title "Workouts"
def content
#response = ''
BubbleWrap::HTTP.get("my_url", {async: false, :headers => { "User-Agent" => "value"}}) do |response|
#response = response.body.to_str
end
#response
end
I see
eb_screen_module.rb:50:in `set_content:': Is a directory - read() failed (Errno::EISDIR)
exception
NSUserDefaults.standardUserDefaults.registerDefaults({UserAgent: "value"})
Found solution myself
Related
I've got pure Ruby app where I want to create request to external API. To do so I'm using standard Ruby Net::HTTP like below:
require 'net/http'
require 'uri'
class Api
BASE_URI = 'https://staging.test.com'
WORKFLOW = 'tests'
QUIZ_PATH = "/v3/accounts/workflows/#{WORKFLOW}/conversations"
def initialize(payload:)
#payload = payload
end
def post_quiz
handle_response(Net::HTTP.post_form("#{BASE_URI}#{QUIZ_PATH}", options))
end
attr_reader :payload
private
def options
{
basic_auth: basic_auth,
body: payload.to_json,
headers: headers
}
end
def basic_auth
{
username: Settings.ln_username,
password: Settings.ln_password
}
end
def headers
{
'User-Agent' => 'Mozilla/5.0',
'Accept-Language' => 'en-US,en;q=0.5',
'Content-Type' => 'application/json'
}
end
def handle_response(response)
return response.body if response.success?
end
end
But instead of response I'm getting an error:
NoMethodError: undefined method `user' for #String:0x00007f80eef9e6f8
Did you mean? super
/Users/usr/.rvm/rubies/ruby-2.7.0/lib/ruby/2.7.0/net/http.rb:527:in `post_form'
I don't have any user there, what is it?
Net::HTTP.post_form is used to send FormData pairs - its not what you want to send JSON and it doesn't even allow you to send headers (You're actually putting them in the request body!).
If you want to send a POST request with HTTP Basic auth and custom headers and JSON body you need to create the request object manually:
require 'net/http'
require 'uri'
class Api
BASE_URI = 'https://staging.test.com'
WORKFLOW = 'tests'
QUIZ_PATH = "/v3/accounts/workflows/#{WORKFLOW}/conversations"
attr_reader :payload
def initialize(payload:)
#payload = payload
end
def post_quiz
url = URI.join(BASE_URI, QUIZ_PATH)
request = Net::HTTP::Post.new(url, headers)
request.basic_auth = Settings.ln_username, Settings.ln_password
request.body = #payload.to_json
# open a connection to the server
response = Net::HTTP.start(url.hostname, url.port, use_ssl: true) do |http|
http.request(request)
end
handle_response(response)
end
private
def headers
{
'User-Agent' => 'Mozilla/5.0',
'Accept-Language' => 'en-US,en;q=0.5',
'Content-Type' => 'application/json'
}
end
# How to respond from an API client is a whole topic in itself but a tuple or hash might
# be a better choice as it lets consumers decide what to do with the response and handle stuff like logging
# errors
def handle_response(response)
# Net::HTTP doesn't have a success? method - you're confusing it with HTTParty
case response
when Net::HTTPSuccess, Net::HTTPCreated
response.body
else
false
end
end
end
Here is the source code that raises the error:
def HTTP.post_form(url, params)
req = Post.new(url)
req.form_data = params
>> req.basic_auth url.user, url.password if url.user
start(url.hostname, url.port,
:use_ssl => url.scheme == 'https' ) {|http|
http.request(req)
}
end
From the docs:
post_form(url, params)
Posts HTML form data to the specified URI object. The form data must be provided as a Hash mapping from String to String.
That means Net::HTTP.post_form(URI("#{BASE_URI}#{QUIZ_PATH}"), options) fixes it. You are currently sending a string as url instead of a URI.
I have been following a tutorial on GitHub Pages and
I am trying to pass an Apikey to a webservice as basic auth 'apiKey' => 'huda7da97hre3rhr1yrh0130409u1u' for example but I cannot work out how to implement it into the method, or even if that is the proper place for it.
I have a class called connection with my request method in it. I need to post 'apiKey' as header and not in the body. I have read the ruby docs but I cannot work out how to apply it to this specific class.
require "net/http"
require "uri"
require "ostruct"
require "json"
class Connection
ENDPOINT = "http://localhost"
APP_LOCATION = "/task_manager/v1/"
VERB_MAP = {
:get => Net::HTTP::Get,
:post => Net::HTTP::Post,
:put => Net::HTTP::Put,
:delete => Net::HTTP::Delete
}
def initialize(endpoint = ENDPOINT)
uri = URI.parse(endpoint)
#http = Net::HTTP.new(uri.host, uri.port)
end
def get(path, params)
request_json :get, path, params
end
def post(path, params)
request_json :post, APP_LOCATION + path, params
end
def put(path, params)
request_json :put, path, params
end
def delete(path, params)
request_json :delete, path, params
end
private
def request_json(method, path, params)
response = request(method, path, params)
body = JSON.parse(response.body)
OpenStruct.new(:code => response.code, :body => body)
rescue JSON::ParserError
response
end
def request(method, path, params = {})
case method
when :get
full_path = encode_path_params(path, params)
request = VERB_MAP[method.to_sym].new(full_path)
else
request = VERB_MAP[method.to_sym].new(path)
request.set_form_data(params)
end
#http.request(request)
end
def encode_path_params(path, params)
encoded = URI.encode_www_form(params)
[path, encoded].join("?")
end
end
If I post to the server using Advanced Rest Client and put the apikey in the
http://localhost/task_manager/v1/tasks?=
header
Authorization: 9c62acdda8fe12507a435345bb9b2338
and in the body
email=free%40mail.com&password=free&task=test
then I get
{
error: false
message: "Task created successfully"
task_id: 5
}
So how can I post it using this class?.
connection = Connection.new
result = connection.post("task", {'task' => 'task'})
Basic Authentication example:
req = Net::HTTP::Get.new(uri)
req.basic_auth 'user', 'pass'
http://docs.ruby-lang.org/en/trunk/Net/HTTP.html#class-Net::HTTP-label-Basic+Authentication
Or if you want to add a raw Authorization header in your request method you can do
request.add_field 'Authorization', 'huda7da97hre3rhr1yrh0130409u1u'
But basic authentication normally means that there is a user name and a password. With your API key - I am not sure you actually need basic authentication. I do not know what you API actually requires but if you have not tried it yet you can try sending the api key as an additional parameter
result = connection.post("register", {'email' => email, 'name' => name, 'password' => password, 'apiKey' => 'huda7da97hre3rhr1yrh0130409u1u' })
I have the following class performing some requests:
the first request uses a digest auth
the second request uses a basic auth
When I run the second request I have the following error:
only one authentication method, :basic_auth or :digest_auth may be used at a time
How can I invalidate the digest_auth prior to running the second request?
class Test
include HTTParty
debug_output $stdout
digest_auth 'login', 'pass'
def first_request(href)
self.class.base_uri "SERVER:PORT"
response = self.class.get(href, {:query => {}})
response
end
def second_request(href)
auth = {:username => "USERNAME", :password => "PASSWORD"}
options = { :body => xml_string, :basic_auth => auth }
response = self.class.post('', options)
response
end
end
When you use basic_auth or digest_auth, HTTParty stores the information internally in the #default_options hash. Here is the source for basic_auth:
# File 'lib/httparty.rb', line 102
def basic_auth(u, p)
default_options[:basic_auth] = {:username => u, :password => p}
end
You can get access to that hash using the default_options method:
# File 'lib/httparty.rb', line 452
def default_options #:nodoc:
#default_options
end
I'd try:
default_options.delete(:basic_auth)
or:
default_options.delete(:digest_auth)
prior to using the other authentication method.
This is untested code but looks 'bout right:
class Test
include HTTParty
debug_output $stdout
def first_request(href)
klass = self.class
klass.base_uri "SERVER:PORT"
klass.default_options.delete(:basic_auth)
klass.digest_auth 'login', 'pass'
klass.get(href, {:query => {}})
end
def second_request(href)
klass = self.class
klass.default_options.delete(:digest_auth)
klass.post(
'',
{
:body => xml_string,
:basic_auth => {
:username => "USERNAME",
:password => "PASSWORD"
}
}
)
end
end
I am not using Rails, so the send_data function will not work, unfortunately.
image = User.select("image, image_mime").where("username = '#{params[:name]}'").first
send_data( image.image,
:type => image.image_mime,
:disposition => 'inline')
Check out the source of send_file method:
def send_file(filename, content_type = nil, content_disposition = nil)
content_type ||= Rack::Mime.mime_type(::File.extname(filename))
content_disposition ||= File.basename(filename)
response.body = ::File.open(filename, 'rb')
response['Content-Length'] = ::File.size(filename).to_s
response['Content-Type'] = content_type
response['Content-Disposition'] = content_disposition
response.status = 200
throw(:respond, response)
end
You can try doing the same, only set the body to image.image instead of reading it from a file.
In my Sinatra app, I've created the following middleware to ensure the incoming request contains the parameter "token" in the query string
class CheckMandatoryParams
def initialize(app)
#app = app
end
def call(env)
# Get token from query string
h = Hash[*env["QUERY_STRING"].split('&').map {|s| s.split('=')}.flatten]
token = h["token"]
# Do not authorize request unless both parameters are not null
if token.nil? then
Log.instance.error("CheckMandatoryParams - token not provided")
[401, {"Content-Type" => "text/plain", "Content-Length" => body.length.to_s}, ["Unauthorized"]]
else
Log.instance.debug("CheckMandatoryParams - token provided")
#app.call(env)
end
end
end
In the case the params exists, the next app is calls and everything goes fine.
In the case the params is not in the query string, the response is not sent, I receive a huge html file indicating an error at the line ' [401, {"Content-Type" => "text/plain", "Content-Length" => body.length.to_s}, ["Unauthorized"]]' but I cannot figure out what is wrong.
Any idea?
UPDATE
This is working better like that :)
body = "Unauthorized"
[401, {"Content-Type" => "text/plain", "Content-Length" => body.length.to_s}, [body]]
I did not manage to retrieve the param with the following code though:
request = Rack::Request.new(env)
token = request.params["token"]
It looks like the "body" variable may be undefined. One possible way to rewrite your code would be as follows:
class CheckMandatoryParams
def initialize(app)
#app = app
end
def call(env)
request = Rack::Request.new(env)
token = request.params["token"]
if token.nil?
[401, {"Content-Type" => "text/plain", "Content-Length" => request.body.length.to_s}, ["Unauthorized"]]
else
#app.call(env)
end
end
end