Why is devise not displaying authentication errors

2020-02-23 20:52发布

I'm using rails 4.2

I have a helper file called devise_helper.rb

module DeviseHelper
    def devise_error_messages!
        return "" if resource.errors.empty?

    messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
    sentence = I18n.t("errors.messages.not_saved",
                  count: resource.errors.count,
                  resource: resource.class.model_name.human.downcase)

     html = <<-HTML
     <div class="row">
     <div class="large-12 columns">
        <div data-alert class="alert-box alert radius">
          <h4>#{sentence}</h4>
          <ul>#{messages}</ul>
        </div>
     </div>
     </div>
     HTML
     html.html_safe
  end
end

to customize error messages and it's working for registrations and passwords pages, but not for sessions pages. Why is this? I know that I can add something like this:

  <div class="row">
    <% if notice %>
      <div data-alert class="alert-box info radius">
        <%= notice %><%= link_to "X", '#', :class => 'close' %>
      </div>
    <% end %>
    <% if alert %>
      <div data-alert class="alert-box alert radius">
        <%= alert %><%= link_to "X", '#', :class => 'close' %>
      </div>
    <% end %>
  </div>

To my application.html.erb file and error messages will display there, but I don't understand why I have to add that when I have the devise helper already. For the passwords and registrations, I just had to add <%= devise_error_messages! %> but the sessions pages don't seem to work that way. I'm not sure if this is just how devise works or if there's something I'm missing.

EDIT: I generated the sessions controller but I never changed anything in it since generating it. From what I understand, devise will just use its default controller until I change the one i generated. My passwords controller is like this as well. I did make some changes to the registrations controller to configure permitted parameters.

class Users::SessionsController < Devise::SessionsController

end

4条回答
混吃等死
2楼-- · 2020-02-23 21:30

For newer version of Rails/Devise I recommend:

Instead of <%= devise_error_messages! %> do:

      <% if flash[:alert] %>
        <%= flash[:alert] %>
      <% end %>

Your test may look something like this (note, you must make this a 'feature' functional test to have access to the "page" variable):

RSpec.feature 'Sign In', :type => :feature do

  describe "correct error message w/ wrong password" do
    before :each do
      @user = create(:user)
      @user.confirm

      visit new_user_session_path
      fill_in "user_email", with: @user.email
      fill_in "user_password", with: "wrongpassword"
      click_button "Log in"
    end

    it "tells user on page 'Invalid Email or password'" do
      expect(page).to have_text("Invalid Email or password")
    end
  end

end
查看更多
干净又极端
3楼-- · 2020-02-23 21:32

When you ran devise:install (or something similar), it must have created views with some specific line of codes to display these messages.

If you look at this link, they explain that you can add <%= devise_error_messages! %> in your views to display those error messages (which isn't really different from having a generic <% flash.each {...} %> somewhere in your HTML...)

Most likely, the views related to sessions do not contain this line of code.

查看更多
啃猪蹄的小仙女
4楼-- · 2020-02-23 21:39

A login with blank/wrong fields does not trigger (your) validations on the model, and therefore won't show your validation errors !

if you debug with byebug (in the first line of your view for example), you'll notice

resource.errors.count # => 0
flash # => ....@flashes={"alert"=>"Invalid email or password."}

Devise populates the "alert flash" with specific sign in error messages unique to this context of sign-in.

Why do you not see all model validation error messages ? Because that wouldn't make sense : suppose your model has a mandatory :gender attribute with validates_presence_of :gender. If normal model errors were added, then you would also see "gender cannot be blank" in the list of errors when your user tries to sign in with a wrong login :oops:.

devise_error_messages! is a specific devise method meant to show those specific errors. You can think of it as a partial validation on the fields that are used for sign in (and that are defined in your devise config file)

WORKAROUND :

If you really want to show all your error messages, you could just explicitely run the validations :

at the beginning of devise_error_messages!

resource.validate # It will generate errors and populate `resource.errors`

I believe it shouldn't mess up with other actions that already work well (register, etc.)

查看更多
Deceive 欺骗
5楼-- · 2020-02-23 21:50

I like this way. The HTML part is kind of original though.

I do not want to separate displaying error messages about authentication. So, I'm using this way.

module DeviseHelper
  def devise_error_messages!

    flash_alerts = []

    if !flash.empty?
      flash_alerts.push(flash[:error]) if flash[:error]
      flash_alerts.push(flash[:alert]) if flash[:alert]
      flash_alerts.push(flash[:notice]) if flash[:notice]
      sentence = I18n.t("devise.failure.invalid") #=> "Invalid email or password."
    else
      sentence = I18n.t("errors.messages.not_saved",
                        count: resource.errors.count,
                        resource: resource.class.model_name.human.downcase)
    end

    return "" if resource.errors.empty? && flash_alerts.empty?

    errors = resource.errors.empty? ? flash_alerts : resource.errors.full_messages
    messages = errors.map { |msg| content_tag(:li, msg) }.join

    html = <<-HTML
    <div class="alert alert-danger">
      <div id="error_explanation">
        <strong>#{sentence}</strong>
        <ul class="m-b-0 p-l-30 ">#{messages}</ul>
      </div>
    </div>
    HTML

    html.html_safe
  end
end
查看更多
登录 后发表回答