In rails 3.0, I'm trying to get exception handling around middleware code. Specifically, if a request comes in with a content-type: application/json but an invalid json input, rails currently renders public/500.html - which is unfortunate.
Since this isn't in a controller yet, most of the things I've seen don't work/apply.
You can rescue from the Exceptions thrown and decide what to do:
rescue_from Exception, :with => :render_exception
def render_exception
# examine the Exception here
# and decide which template to render
render :template => "shared/???.html", :status => ???, :layout => 'error'
end
Place this code in the app/controllers/application_controller.rb
I hope this is kind of what you're looking for ...
Related
I am trying to get around this weird error for some time now. This is the basic controller I have:
get :index, provides: :json do
#requests = Request.order(:created_at)
render 'requests/index'
end
get :show, with: :id, provides: :json do
#request = Request.find_by(id: params[:id])
render 'requests/show'
end
This is how my rabl json files look like in each case:
index.json.rabl:
collection #requests
attributes :created_at, :updated_at, :user_id, :request_type
show.json.rabl:
object #request
attributes :id
The first route i.e. :index returns the array of Request objects nicely but the second route i.e :show throws the following error:
NoMethodError at /show/1
undefined method `head?' for #<Request:0x007f6814485840>
Can someone point at what this error could be about? Why is it looking for head? function?
Don't use #request instance variable. Rack stack uses it to store HTTP request there.
I have been looking all over for how to properly check respond to a application/json type as well as a submitted form. I finally got it working on my own with the following code. Can someone explain why it works? Or offer advice on a better solution to achieve the same thing?
post '/login', provides: :json do
p = params
if request.content_type == 'application/json'
params = JSON.parse(request.body.read, :symbolize_names => true)
else
params = p
end
requires(params, :email, :password)
if #user = User.find_by_email(params[:email])
if #user.authenticate(params[:password])
log_user_in(#user)
rabl :login, object: #user
else
error 404, {error: "incorrect credentials"}.to_json
end
else
error 404, {error: "user not found"}.to_json
end
end
JSON requests are submitted in the body of the html request so this works but overriding the params hash is not advisable if using RESTful routes.
Hey Curtis.
Just use ::Rack::JSONBodyParser from rack-contrib:
A Rack middleware that makes JSON-encoded request bodies available in
the request.params hash. By default it parses POST, PATCH, and PUT
requests whose media type is application/json. You can
configure it to match any verb or media type via the :verbs
and :media options.
Examples:
Parse POST and GET requests only
use Rack::JSONBodyParser, verbs: ['POST', 'GET']
Parse POST|PATCH|PUT requests whose Content-Type matches 'json'
use Rack::JSONBodyParser, media: /json/
Parse POST requests whose Content-Type is 'application/json' or 'application/vnd+json'
use Rack::JSONBodyParser, verbs: ['POST'], media: ['application/json', 'application/vnd.api+json']
Noob
How should I send a single string variable to another site from my rails app? ie the outside service sends a get request to my controller/route and then the controller must respond with the string (and I assume a response code). The string is intended to be added to their html code.
In my controller should I
render :text => "string"
or
respond_with("string) #as xml or json
or something completely different?
Just try the following code. Here your application gives the text and json as per the request.
respond_to do |format|
format.json do
render :json => 'string'
end
format.html do
render :text => 'string'
end
end
I have been playing around with using rest-client to access a rails app I have written. I've written a quick script to log in and make a post request. Everything is working but I did have to work round the fact that no authenticity_token is served if you make a request for a form in json. I had to make a regular html request in other get the authenticity_token and then included this in the json I submitted as part of my post request. Basically I have a quick an dirty script like the one below
private_resource = RestClient::Resource.new( 'https://mysite.com')
params = {:user => {:email => 'user#mysite.com', :password => 'please'}}
#log in
login_response = private_resource['users/sign_in'].post(params, :content_type => :json, :accept => :json)
#get cookie
cookie = login_response.cookies
#get json
json_response = private_resource['products/new'].get(:content_type => :json, :accept => :json, :cookies => cookie)
#another request that returns html form with authenticity token
response_with_token = private_resource['products/new'].get( :cookies => cookie)
#extract token
token = Nokogiri::XML(response_with_token).css('input[name=authenticity_token]').first.attr('value')
#update cookie
cookie = response_with_token.cookies
#populate form and insert token
form = JSON.parse(json_response)
form['name'] = "my product"
form['authenticity_token'] = token
#submit the request
private_resource['products'].post(form.to_json, {:cookies => cookie, :content_type => :json, :accept => :json})
There is the option to turn off CSRF protection for json requests but I would rather not do that. I could go the mechanize route or something similar and then I wouldn't worry about json requests with CSRF but I just wanted to play around with doing this stuff with rest-client
I guess I'm just curious to know if there is a reason why no authenticity_token is served for json requests and I'm also wondering if there is a better way of solving the token problem than the pretty hacky approach I've taken here
Put the below code into your application controller :
def verified_request?
if request.content_type == "application/json"
true
else
super()
end
end
And call this method using before_filter .
For more details check :
http://blog.technopathllc.com/2011/09/rails-31-csrf-token-authenticity-for.html
And check this issue in rails : https://github.com/rails/rails/issues/3041
In your app/views/products/new.json.jbuilder, add this:
json.authenticity_token form_authenticity_token
This will insert a key "authenticity_token" with value being the token, so in your json_response you get the token as well. Idea from this answer.
I'm encountering a strange behavior in my controllers. They seem to occasionally want to redirect instead of render a json response.
respond_to :json, :html, :js
def create
#favorite = current_user.favorites.build(:location_id=>params[:location_id])
if #favorite.save
respond_with(#favorite)
else
respond_with(#favorite.errors)
end
end
I think it works most of the time but today I was notified of this error:
NoMethodError: undefined method `favorite_url' for #<FavoritesController:0x00000006171dc0>
The params hash was logged as:
{"format"=>"json",
"action"=>"create",
"user_id"=>"56",
"auth_token"=>"iGSty8CMIaWsbShYZEtw",
"location_id"=>"47943",
"controller"=>"favorites"}
Especially strange since it seems to work most of the time... I have changed a few of my other controllers to use the old format.json { render :json => #object } syntax but I'd like to avoid that if possible.
How could this be?
On paths that are not GETs, respond_with tries to redirect to the url for whatever it is given. You can override this with a custom Responder