I am using a custom error controller. I would like to have a 200 response code when visiting the 500 page URL (otherwise my middleware that intercepts 500 response codes sends me an exception email whenever I want to show off my 500)
It would seem using the self.routes as exceptions_app will change the request.path to always be equal to the error number
Target
visit www.example.com/crashy_url # => Should show custom 500 error template with 500 response code
visit www.example.com/500 # => Should show custom 500 error template with 200 response code
Problem
visit www.example.com/crashy_url # => request.path is equal to `/500' so my controller sends a 200 response code
How can I extract the really visited URL with this ?
# config/application.rb
config.exceptions_app = self.routes
# routes
match '/500', to: 'errors#server_error', via: :all
# app/controllers/errors_controller.rb
def server_error
#status = 500
can_be_visited_with_ok_response_code
show
end
def can_be_visited_with_ok_response_code
# We want to hide hide the faulty status if the user only wanted to see how our beautiful /xxx page looks like
# BUT `action_dispatch/middleware/debug_exceptions` will change request.path to be equal to the error !!
# #status = 200 if request.path == "/#{#status}" # BAD request.path will always return response code
end
def show
respond_to do |format|
format.html { render 'show', status: #status }
format.all { head #status }
end
end
I love Ruby and did_you_mean... I was able to guess the correct method name: request.original_fullpath should be used instead to get the original URL entered by the user.
#status = 200 if request.original_fullpath == "/#{#status}"
Note that request.original_url can also give you the full path including the host name
Related
I want to do something like this post but in the actual Rails app, not the testing.
I want to see if the HTTP request was successful or not. If it's not successful (aka 404 Not Found), then I want to render a different HTML. But I can't figure out the syntax to compare.
Currently, I have:
def videos
# get current_user's wistia_project_id & authorization token
#current_user = current_user
project_id = #current_user.wistia_project_id
auth_token = "blah"
request = "https://api.wistia.com/v1/projects/#{project_id}.json?api_password=#{auth_token}"
#response = HTTP.get(request).body
puts HTTP.get(request).status
# handle errors: not 200 OK
if !HTTP.get(request).status:
render "/errors.html.erb/"
end
# get embed code for each video using the hashed_id, put in list
#video_iframe_urls = JSON.parse(#response)['medias'].map do |p|
"https://fast.wistia.com/embed/iframe/#{p["hashed_id"]}?version=v1&controlsVisibleOnLoad=true&playerColor=aae3d8"
end
end
require 'net/http'
uri = URI("https://api.wistia.com/v1/projects/#{project_id}.json?api_password=#{auth_token}")
res = Net::HTTP.get_response(uri)
# Status
puts res.code # => '200'
puts res.message # => 'OK'
puts res.class.name # => 'HTTPOK'
# Body
puts res.body if res.response_body_permitted?
So I want to render an error page and pass in an error message inside a rescue_from section in a Rails controller. It looks like this currently:
rescue_from ActiveSupport::MessageVerifier::InvalidSignature do
render json: { errors: I18n.t(:invalid_token, scope: :errors) }, status: :bad_request
end
But we no longer want to render json. We want to render a rails template. How do we do this but also passing in the errors hash? Can this be done inside a rescue_from?
A common way of responding when there are errors is by using an exceptions_app for your application, first create a controller to serve the error responses:
class ErrorsController < ApplicationController
def not_found
#details = "foo bar, something that happened in this request"
render status: 404
end
def internal_server_error
render status: 500
end
end
You can assign instance vars in the controller, like ordinary controller actions, here you can build a message to the user or handle it however you like.
Then you can add some routes in your router:
match "/404", to: "errors#not_found", via: :all
match "/500", to "errors#internal_server_error", via: :all
In you application.rb file, add:
config.exceptions_app = self.routes
And last be sure to remove the 404.html and 500.html in your public dir if they are present.
NOTE:
If you want to see these pages locally during development you'll need to modify your config/environments/development.rb:
config.consider_all_requests_local = false
For example...
module Api
module V1
class SessionsController < ApplicationController
respond_to :json
skip_before_filter :verify_authenticity_token
def create
#user = User.find_by(email: params[:session][:email].downcase)
if #user && #user.authenticate(params[:session][:password])
token = User.new_remember_token
#user.update_attribute(:remember_token, User.digest(token))
respond_with :api, :v1, _____________
else
#error
end
end
end
end
end
The #error part of the code, if the user is not properly authenticated. What syntax do I need to properly convey to the caller that the authentication did not go through for example, or in other cases, maybe data was not saved?
Like CBroe said, respond with an appropriate status code, such as 400 or 403. You could do just that (using 'head' to return the status code only), or also add an error message in JSON format:
{ 'error' : 'Authorization failed' }
The client code will want to check the status code and possibly the 'error' key in the JSON response and handle it appropriately.
Examples to put at the end of your controller action (pick one):
return head(:bad_request) # returns a 400 status code only
render :json => { :error => 'That was an invalid request' } # defaults to 200 status
render :json => { :error => 'Oops! Bad request' }, :status => 400
The last example overrides the default status to make it a 400. In general, the status can be an integer like that, or a symbol like :not_found or :bad_request. Hope that helps.
I'm running a Rails 4.0.0.rc application using New Relic for availability / exception monitoring. I modified application.rb with this snippet to enable dynamic exception pages:
config.exceptions_app = self.routes
However, I no longer see 404, 422 or 500 exceptions in New Relic. Any idea how I get them back?
Edit:
Note: this is what the controller handling the status looks like:
class ErrorsController < ApplicationController
# GET /404
def missing
render status: 404
end
# GET /422
def unprocessable
render status: 422
end
# GET /500
def exception
render status: 500
end
end
It sounds like you want to call NewRelic::Agent.notice_error manually.
You can reconstruct the request object from the Rack env and build an exception however you would like.
Something like this:
request = Rack::Request(env)
options = {
:uri => request.url,
:referrer => request.referrer,
:request_params => request.params
}
NewRelic::Agent.notice_error(your_custom_exception, options)
Note that the request parameters will be transmitted as is so be careful to filter anything sensitive.
Sources:
I work for New Relic as Ruby Agent Engineer
Documentation for NoticedError: http://rubydoc.info/gems/newrelic_rpm/frames
You will have to set the html status code to the correct value in your errors controller. If you for example have something like this:
class ErrorsController < ApplicationController
# 404
def not_found
render "not_found", status: 404
end
end
Otherwise will rails render the error page with a 200 status code, and new relic will not pick it up as an error.
I am overriding Devise's failure response so that I can set a 401 status code. However, when the user fails to sign in, they are redirected to a page with a "you are being redirected" link. If I remove this :status => 401 from the redirect it works fine.
class CustomFailure < Devise::FailureApp
def redirect_url
new_user_session_url(:subdomain => 'secure')
end
def respond
if http_auth?
http_auth
else
store_location!
flash[:alert] = i18n_message unless flash[:notice]
redirect_to redirect_url, :status => 401
end
end
end
edit
Alternatively I would like to display the flash message and remain on the same page but adding this line of code:
render :text => "unauthorized", :status => 401
causes ruby to complain:
undefined method `render' for #<CustomFailure:0x00000103367f28>
What's happening here?
Proper HTTP statuses for a redirection are in the 30x form (301 and 302 being the most frequently used). By default, the redirect_to helper sets a 302 status header on the HTTP response. If you override that and set that to a 401, your web browser will assume that the response is a regular web page and will render the response body --which, in a redirection, is the boilerplate text "You are being redirected".
As said by #pantulis the browser will display this standard message if the response code is not a 3xx
To workaround this you can perform a javascript redirect:
# example with status 500:
render text: "<script>window.location = '#{url}';</script>", status: 500
This is off-course valid only if you are sure that all your users are using javascript. If your application can be browsed by users that may have disabled javascript you should also include a noscript tag and fallback in the standard "You are being redirected" message
I was actually running into this problem on our QA server, but not locally. It turned out that our memcache was intercepting the message and rendering it as a 200, and causing this message to appear. This was due indirectly to our memcache settings which didn't expect a re-direct from a GET.
From:
$document_root/cache/$uri.html /cache/$uri /cache/$uri.html $uri #memcached
To:
$document_root/cache/$uri.html /cache/$uri /cache/$uri.html $uri #rails
When I have this problem what I have done in the past is something like this:
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
after_filter :check_page_content
...
private
def check_page_content
if response.body.include? "You are being"
html_doc = Nokogiri::HTML(response.body)
uri = html_doc.css('a').map { |link| link['href'] }.first
response.body = "<script>
window.location.replace('#{uri}');
</script>"
end
end
end
What I am doing is checking to see if the page content is "You are being". if this is true I know I am not where I want to be. and I just update the page to where I really want to be with some help of Javascript.
I know its not the most elegant solution but it really does help
Happy Hacking