Looks like a bug with rails but want to make sure I didn't miss anything before reporting a bug.
I am trying to rescue from ActionController::InvalidAuthenticityToken in the application controller with the following code:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
rescue_from ActionController::InvalidAuthenticityToken, with: :show_errors
....
private
def show_errors
redirect_to root_path, alert: "Cookies are disabled"
end
end
The redirect works fine, but the alert message does not show. The flash hash is empty in the new page: #<ActionDispatch::Flash::FlashHash:0x007f9dbdb5c1d0 #discard=#<Set: {}>, #flashes={}, #now=nil>
There are no other filters in the application controller that could be affecting the hash / causing another redirect. The logs show only one redirect as expected.
Tried flash.keep[:alert] = .. and flash.now[:alert] = .. as well; no luck.
Getting this behavior on two different rails apps, one with 4.2.0 and another with 4.1.6.
Try
def show_errors
flash[:error] = "Cookies are disabled"
redirect_to root_path
end
Andrew White gave an explanation on what is happening on the issue I created:
Since flash is dependent on the session and the session is dependent
on cookies working then it's never going to work if you think about
it, :-)
I'd suggest redirecting to a custom url or add a query param to the
url that triggers the display of the message.
I am redirecting to a custom url as suggested.
Related
I'm using CanCanCan in my rails app for authorization. The routes and redirects work fine but I don't manage to display the AccessDenied error message (it worked before but I must have messed something up on the way and now it just won't show).
Here's my code:
controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_url, error: exception.message
end
...
end
(I changed the default :alert to :error as otherwise I was ending up with an alert from devise ('You are already logged in').
views/index.html.haml (root)
= flash[:error] if flash[:error]
...
Any ideas on how to get it to work again? Thanks in advance.
---EDIT---
Some more fixes I tried (without success):
Replacing error with message and notice
Removing the rescue_from CanCan::AccessDenied method completely - it brought me to the standard CanCan error page (so I know the method itself is working).
Replacing the exception.message with a regular string.
Adding a puts statement before redirection - it gets printed in the console as one would expect.
As #Anand Shah suggest in an answer to this question, I added the following in my view:
- if flash[:error].blank?
%p flash hash is blank
The hash was indeed empty, so it seems like the flash is not being saved at all. Then I added
flash.keep(:error)
in my controller but that didn't change anything.
The reason for this issue turned out to be my routes.rb. In addition to defining the root for devise scope:
devise_scope :user do
root to: "devise/sessions#new"
end
authenticated :user do
root 'application#show', as: :authenticated_root
end
I also had an extra root defined (which basically caused double redirection and therefore loss of the alert):
root to: 'application#show'
The code from the documentation works just fine after the necessary modification (note I could bring it back from error to alert without breaking things):
rescue_from CanCan::AccessDenied do |exception|
redirect_to root_url, alert: exception.message
end
Have you tried it through a private browser in case the cookies have messed up?
Can you try this format?
redirect_to(root_url, {:flash => { :error => exception.message }})
Have you tried not handling this inline but rather something like:
rescue_from CanCan::AccessDenied do |exception|
flash[:error] = exception.message
redirect_to root_url
end
The reason it was working before is Rails understands :alert and :notice as flash messages during the redirect_to otherwise you should use the "general purpose flash bucket" as they call it via flash: {error: 'your message'}
However the Hash passed to redirect_to can also contain the http status code (:status) and url query parameters (anything that is not :alert, :notice, :status, or :flash and it all just seems like too much noise (IMO) to put a flash message in there to save 1 line of explicit code.
I am trying to catch a custom error, and redirect to root path after it occurs. My code in controller looks like this:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
rescue_from Errors::MyError, :with => :my_error
private
def my_error
redirect_to root_path, alert: "my Error"
end
end
Redirect is not working, after error occurs I am still at the same page. What is funny, my server logs shows that I was redirected.
Redirected to http.....
Completed 302 Found in 1908ms (ActiveRecord: 0.5ms)
What happened? And why redirect havent occur?
When you are doing an AJAX call to your server, the browser expect the response to be in javascript ( you talk to me in the JS language, I answer using JS language too, quite simple ). In your case, you need to test if the request is HTML or Javascript, and use the corresponding redirect:
def my_error
path_to_redirect = root_path
if request.xhr? # tests if the request comes from an AJAX call
render :js => "window.location = '#{path_to_redirect}'"
else
redirect_to path_to_redirect, alert: "my Error"
end
end
For more answers about AJAX redirects in Rails: Rails 3: How to "redirect_to" in Ajax call?
I need only show a message when send the reset password instructions, I don't need redirect to new session, I overwritten the controllerPassword but when I put a redirect_to there is a error with this render.
The path used after sending reset password instructions
def after_sending_reset_password_instructions_path_for(resource_name)
flash[:notice] = "We have sent an email with the instructions to reset your password"
redirect_to new_user_password_path and return
end
this is the error:
Render and/or redirect were called multiple times in this action......
How can I fix it?
If you remove redirect_to new_user_password_path and return from your code entirely it will stop redirecting. However, if you do this it won't show your flash notice until the user manually refreshes the page. From here there are two fixes:
redirect to the current page to force a refresh and show the notice.
bind your flash notice to an AJAX request so that it's sent asynchronously. There are a lot of ways to do that, but this answer covers it pretty well.
The controller action which is calling after_sending_reset_password_instructions_path_for has a render or a redirect_to.
Try removing redirect_to in after_sending_reset_password_instructions_path_for, and let the calling action handle it.
I have to overwrite other class called: SessionsController < Devise::SessionsController
I don't know if this is the best solution but worked for me.
This was I did:
class SessionsController < Devise::SessionsController
# GET /resource/sign_in
def new
url = (request.env["HTTP_REFERER"]).to_s
path = url.split("?").first
if path == "http://#{Rails.application.secrets.domain_name}/users/password/new"
redirect_to new_user_password_path and return
end
self.resource = resource_class.new(sign_in_params)
clean_up_passwords(resource)
yield resource if block_given?
respond_with(resource, serialize_options(resource))
end
end
There are many posts on SO about this ( respond_with redirect with notice flash message not working Why is :notice not showing after redirect in Rails 3, among others) , I've read at least 4 and still can't solve this issue.
I've got a portion of my site that lets people do some things before they create an account. I prefer this from a UX perspective. So they're allowed to do X and Y then they get redirected to the "Create account" page (uses Devise).
The redirect looks like:
if userIsNew
... stow information in a cookie to be retrieved later ...
redirect_to "/flash", flash[:notice]
=> "Ok, we'll get right on that after you sign up (we need your email)."
and return # this has to be here, since I'm terminating the action early
end
So "/flash" is a plain page that I made to test this. It doesn't do anything, has no markup of its own, just has the basic html from the application.html, which has this line in the body:
<% if flash[:notice] %>
<p><%= notice %></p>
<% else %>
No notice!
<% end %>
It says 'No notice' every time.
I have tried:
adding in a flash.keep to my before_filter in the static controller
using :notice => instead of flash[:notice] =>
putting the notice in a cookie and pulling that text out of the cookie and into a flash in the before_filter of my application controller
redirect_to :back with the flash[:notice] =>
It's either
flash[:notice] = 'blablabla'
redirect_to foo_url
or
redirect_to foo_url, notice: 'blablabla'
I'm overriding ApplicationController#redirect_to to call flash.keep so that any messages are persisted on redirect without having to explicitly call flash.keep in my controller actions. Works well so far. Haven't had a scenario yet where unwanted messages are persisted.
class ApplicationController < ActionController::Base
def redirect_to(*args)
flash.keep
super
end
end
Let me know if there are any scenarios where this isn't a good solution.
I have been fighting with the same problem for some time and none of the posts seemed to help.
It turns out that - like usually it happens - the the problem was in my code. I did have a "redirect_to" that I forgot about, which was clearing the flash.
Namely, "root_path" for me was served by the StaticPagesController's home method. "home" was doing some checks and then redirecting you to the user_path.
In my code I had in numerous places
redirect_to root_path, :flash => {error: #error}
These redirects were never displaying the flash because my hidden "home" controller serving the "root_path" was making another redirect that cleared the flash.
Therefore my problem was solved when i added the "flash.keep" in my "home" controller method
def home
if current_user
#user = current_user
flash.keep
redirect_to #user unless #user.no_role?
end
end
Faced the same problem, flash just disappeared after any redirect, nothing helped, then, I found that it was switched off...
Check your /config/application.rb for this:
config.middleware.delete ActionDispatch::Flash
I am using Authlogic-connect to connect various service providers. There is a method in user.rb
def complete_oauth_transaction
token = token_class.new(oauth_token_and_secret)
old_token = token_class.find_by_key_or_token(token.key, token.token)
token = old_token if old_token
if has_token?(oauth_provider)
self.errors.add(:tokens, "you have already created an account using your #{token_class.service_name} account, so it")
else
self.access_tokens << token
end
end
When a service provider is already added it gives the error as stated in the has_token? method and the page breaks. I need to redirect the app to the same page and flash the error. How do i do this? I have overridden the method in my own user.rb so that I can change the code.
Hmm, well you could put a method that handles the error that has_token? throws, and tell your controller to redirect that exact error. something like this in your controller:
rescue_from OauthError::RecordNotFound, :with => :deny_access
then you can put
def deny_access
redirect_to your_view_path, :alert => "Too bad sucker" #some flash message
end
Or you could do something like this in the controller:
if complete_oauth_transaction.errors.present?
redirect_to your_view_path
else
# continue on with the normal code here
end
This is how you could generically handle errors. Your exact code will vary, as this is all we have to go off of.