I have 2 ruby on rails apps. With app A I post app B some data (in the form of a hash). I then want app B to send a hash on this data (with some modifications) back to app A in the response.
I have tried the code below App A
response = Net::HTTP.post_form(uri, params)
quotes.push(response.body)
and in App B
details = get_details //returns a hash
respond_with details
But its not working. Is what im doing even possible? Is there a way I can place this hash in my response?
Any help would be appreciated
Solution #1
If you use respond_with you need also specify formats which your app should respond to. For this you should use respond_to method.
Example:
class TestController < ApplicationController
respond_to :json
def index
details = get_details
respond_with(details)
end
end
Also check this good article about respond_to method.
Solution #2
Just use render json: {...} in your controller action.
Example:
class TestController < ApplicationController
def index
details = get_details
render json: details
end
end
In your app A response.body will contain a string with the data from your app B. So you need to parse that string.
In your app A:
require 'json' # this is unnecessary if app A is a Rails app
response = Net::HTTP.post_form(uri, params)
parsed_response = JSON.parse(response.body)
quotes.push(parsed_response)
The rails way to do that is using JSON as an exchange format. have a look at the guides for how to use that: http://guides.rubyonrails.org/layouts_and_rendering.html#rendering-json
It is also possible to use ActiveResource for such a communication. It allows direct access to your rails API.
Related
My current Rails application has the following subdomains:
ui.myapp.com
api.myapp.com
I want to set up a staging environment, and I am wondering what is the best approach in order to set the domains.
Usually, I would do:
staging.myapp.com
But then, how do I access the UI/API sites?
Is it 'ok' to do:
ui.staging.myapp.com
api.staging.myapp.com
?
Assumption: The output from API call is expected in JSON format.
It will not be advisable to use separate subdomain for functionality like api.staging.myapp.com. You can always use the same url for web application and API
Better way would be to use same controller-action from Rails application but instead to return JSON output hash.
For example to get the users, you will have some code in users_controller.rb as
class UsersController < ApplicationController
def index
#users = User.all
respond_to do |format|
format.html do
render :index
end
format.json do
render :json => {:users => #users}
end
end
end
end
Now here if the request is html it will render the html page else it will return the json response.
staging.myapp.com/users will get you the html page of web application displaying users collection and that of staging.myapp.com/users.json will provide you the json response hash of users collection.
So, you will not need different subdomain to distinguish the api and normal site. But using format of request you can distinguish the functionality.
I hope this answers your question. If you still have any query feel free to ask.
I am trying to configure my controller to process the params sent through a POST from another website. My log shows that the parameters that I receive are as follows:
{"page_id"=>"8b62f4ac-8588-11e3-a094-12314000b04c", "page_name"=>"test form", "variant"=>"b", "page_url"=>"http://get.xxxxxxx.com/test-form", "data.json"=>"{\"name\":[\"Dave\"],\"email\":[\"xxxx#me.com\"],\"phone\":[\"4447177265\"],\"ip_address\":[\"64.114.175.126\"],\"time_submitted\":[\"07:34 AM UTC\"]}", "data.xml"=>"\n\n Dave\n xxxx#me.com\n 2507177265\n 64.114.175.126\n 07:34 AM UTC\n"}
Initially I thought that Rails would automatically parse the JSON in the params and I could access them in the normal way. So I wrote the Registrations Controller like this:
class Api::RegistrationsController < Devise::RegistrationsController
skip_before_filter :verify_authenticity_token
respond_to :json
def create
#user = User.new(user_params)
if #user.save
render json: #user.as_json( email: #user.email), status: 201
return
else
warden.custom_failure!
render json: #user.errors, status: 422
end
end
def user_params
params.require(:'data.json').permit(:email, :name, :phone, :comments, :residency, :qualification, :acknowledgement) if params.present?
end
end
However, it is simply not working at all. I get an error undefined method 'permit' for string. So obviously I'm not accessing the JSON correctly. Is it possible that because the JSON is escaped that it's throwing the errors?
I've been googling and asking in IRC for a couple of days but I'm not any farther ahead.
I can pass a properly formatted JSON to the controller and it works fine (with changes to the require arguments)
I'm stumped since I need to be able to create a new user with the JSON data. Any help would be HUGELY appreciated. I just don't know what direction to even go from here.
The params.require(:'data.json') returns a JSON body which is a string, however your controller does not interpret the string but expects a Hash.
You can convert the JSON string to a Hash object using the parse class method for JSON like so:
require 'json'
JSON::parse(json_string)
I'm attempting to integrate Omniauth into an API written in rails, to be used by an Android application. This means that I want to be able to handle the omniauth callback with JSON.
By default, Omniauth sends its callbacks to /auth/:provider/callback, is there a way that I can force Omniauth to instead send its callbacks to /auth/:provider/callback.json?
You can specify format in action where handling callback:
# in route.rb
match '/auth/:provider/callback' => 'authentications#create'
# in authentications_controller.rb
class AuthenticationsController < ApplicationController
def create
# your code here
respond_to do |format|
format.json { ... } # content to return
end
end
end
I managed to do that by inspecting the request object on my rails backend.
When I make the request on my app, I add data on the submition defining the format:
format: "json"
And the omniauth then makes the callback for
/auth/:provider/callback
Wich in my case matches
sessions#create
as an HTML request. But once there, if you look at your request object in rails, and search for the omniauth.params hash you'll see that one of the values there is the format passed on tha data of the initial request:
"omniauth.params"=>{"format"=>"json", "provider"=>"facebook", "code"=>"..."}
Its a mather of you searching for this "format"=>"json" and doing a render json as an answear.
I hope it solves your problem.
# app/controllers/users_controller.rb
def authenticate
#credentials = request.env['omniauth.auth']
render json: #credentials
end
# config/routes.rb
get '/auth/:provider/callback', to: 'users#authenticate', as: 'user_auth'
And then all requests made to /auth/:provider/callback will return a JSON response by default.
RoR noob here... :)
I need to create a Rails API that clients can call and send an XML file through a POST request.
I've create my route like this:
namespace :api do
namespace :v1 do
resource :report
end
end
and my controller like this:
class Api::V1::ReportsController < ApplicationController
respond_to :xml
def create
#report_submission = ReportSubmission.create :login => params[:login],
:status => :success
respond_with(#report_submission)
end
end
What do I need to do in the controller to receive the XML file that the client will be posting, and then read is content so I can ultimately put it in the database?
How can I test that?
I've created a sandbox project to try this out and got stuck... no idea what to do next. I've pushed it up here:
https://github.com/claudiolassala/api-samples/
Any help will be just awesome!
end
After doing some more research, I've managed to get that working. I've updated my repository on GitHub with the solution.
The main changes were to modify my controller so to read the contents of the file being posted:
class Api::V1::ReportsController < ApplicationController
respond_to :xml
def create
#report_submission = ReportSubmission.create :login => params[:login],
:status => :success,
:results => read_file_data(params[:reportxml])
respond_with(#report_submission)
end
private
def read_file_data(file)
xml_contents = ""
if file.respond_to?(:read)
xml_contents = file.read
elsif file.respond_to?(:path)
xml_contents = File.read(file.path)
else
logger.error "Bad file_data: #{file.class.name}: #{file.inspect}"
end
xml_contents
end
end
And I've fixed my Cucumber step that performs the post, changing it to this:
When /^I send a POST request containing the file$/ do
#login = "some-login"
#file_path = "#{::Rails.root.to_s}/features/step_definitions/test_report.xml"
post "api/v1/report.xml?login=#{#login}",
:reportxml => Rack::Test::UploadedFile.new(#file_path, 'text/xml')
end
Please let me know whether there's any better way to do this. Thanks!
You need to do absolutely nothing apart from making absolutely 100 % certain that any data that is posted is not harmful :)
RESTfull routes give you a params hash in your controller action in the normal way when posting xml. This is exactly how ActiveResource works
So just interrogate your params as normal.
The only potential gotcha is that Rails expects properly formatted xml for this to work.
The easiest way for you to check what happens is try posting an xml file to your action and have a close look in the log file at the params the controller receives
http://apidock.com/rails/ActiveResource/Base Shows how this works in quite good detail (No you don't have to be posting the xml via an ActiveResource object)
As far as testing is concerned Ryan Bates has a great Railscast on ActiveResource here http://railscasts.com/episodes/94-activeresource-basics and here http://railscasts.com/episodes/95-more-on-activeresource
They are old casts but still totally valid and you can see exactly how to test
I'm trying to work with the data in my Rails application from within a separate Ruby script.
I read this forum post in which some people suggest that the best way to work with your data is to encapsulate the database within one application, and then have this application provide an API for working with that data. Because it's apparently bad to integrate your database into several different applications.
Well, now I want to work with some data from my Rails app from another script, but on the same system. How can I achieve this?
I might want to work with the data from my Rails app remotely in the future, but also from a script. I'm assuming this might require JSON or SOAP, but I would like to know before I go researching it.
Have you take a look at ActiveResource? It's specifically designed to expose data from a Rails model to another Rails app over HTTP.
Since Ruby on Rails follows REST, your application is, by default, it's own API. For example, say you have the following controller:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
respond_to do |format|
format.html
format.xml { render :xml => #user}
format.js
end
end
def index
#users = User.all
respond_to do |format|
format.html
format.xml { render :xml => #users}
format.js
end
end
end
Now, when hitting that controller via the web browser, it will render your views as you would expect. For example:
GET /users/1 => /app/views/users/show.html.erb
GET /users => /app/views/users/index.html.erb
However, if you change your requests to be something like:
GET /users/1.xml
GET /users.xml
You'll be returned XML data instead of your HTML views. You can now access this data from any other application by using some sort of REST Client, or simply by calling cURL from any command line.
You can append any extension to the end of your URL, and it will find the appropriate respond_to section.
Accessing the data is simple too, just make a request to your application using something like HTTParty. Look at the examples, they're pretty good.