Rails 7 + Devise Не вижу флеш-сообщений от devise

У меня есть новое приложение rails 7 [rails new devisetest], простой контроллер со статической страницей и добавленным devise [gem 'devise' + rails g devise user]. Все по умолчанию. Флэш-сообщения добавлены в application.html.erb в соответствии с инструкциями по разработке.

Сообщения об ошибках Devise не отображаются, но я вижу, что они генерируются в консоли (или, по крайней мере, я вижу визуализированное сообщение, но они не отображаются, и я вижу, что визуализировано для партиала _links, и это действительно отображается) В другом тесте используя быструю основу (и скопировав представления devise в приложение, но не изменив их), flash-сообщения отображаются для scaffold, но все же не для devise.

Если я захожу на http: // localhost:3000 / users / sign_up и создаю пользователя со слишком коротким паролем, я вижу это в журнале

      Started POST "/users" for ::1 at 2021-12-24 08:02:10 +0000
Processing by Devise::RegistrationsController#create as TURBO_STREAM
  Parameters: {"authenticity_token"=>"[FILTERED]", "user"=>{"email"=>"asdf@asdf.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "commit"=>"Sign up"}
  TRANSACTION (0.1ms)  begin transaction
  User Exists? (2.7ms)  SELECT 1 AS one FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "asdf@asdf.com"], ["LIMIT", 1]]
  TRANSACTION (0.2ms)  rollback transaction
  Rendering layout layouts/application.html.erb
  Rendering /home/sroot/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/devise-4.8.1/app/views/devise/registrations/new.html.erb within layouts/application
  Rendered /home/sroot/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/devise-4.8.1/app/views/devise/shared/_error_messages.html.erb (Duration: 0.7ms | Allocations: 582)
  Rendered /home/sroot/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/devise-4.8.1/app/views/devise/shared/_links.html.erb (Duration: 0.2ms | Allocations: 92)
  Rendered /home/sroot/.rbenv/versions/3.0.2/lib/ruby/gems/3.0.0/gems/devise-4.8.1/app/views/devise/registrations/new.html.erb within layouts/application (Duration: 3.9ms | Allocations: 2199)
  Rendered layout layouts/application.html.erb (Duration: 77.5ms | Allocations: 4429)
Completed 200 OK in 473ms (Views: 78.7ms | ActiveRecord: 3.1ms | Allocations: 15288)

Кто-нибудь может указать на мою ошибку, пожалуйста? Или предложить другие шаги, которые я могу предпринять, чтобы приблизиться к моей ошибке?

Спасибо

10 ответов

Я слежу за учебными пособиями (видео: https://gorails.com/episodes/devise-hotwire-turbo?autoplay=1), а также попробовал это с этим учебным пособием здесь: https://medium.com/@nejdetkadir/how -использовать-придумать-жемчужину-с-руби-на-рельсах-7-33b89f9a9c13

Но флеш-сообщения или не отображаются.

Я добавил в свой devise.rb сейчас:

Если вы просто хотите, чтобы он "работал", вы можете добавить data: { turbo: false }к вашей форме разработки. Это предотвратит захват ваших отправленных форм со всей своей автоматической магией.

Кредит Джону-999 здесь https://github.com/heartcombo/devise/issues/5446#issuecomment-1083485163

В моем случае мой views/devise/sessions/new.html.erb выглядел так:

          <%= form_for(resource, as: resource_name, url: session_path(resource_name), data: { turbo: false }) do |f| %>
      <% resource.errors.full_messages.each do |msg| %>
        <div><%= msg %></div>
      <% end %>

      <div class="mb-3">
        <%= f.label :email, class: "form-label" %>
        <%= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control' %>
      </div>

      <div class="mb-3">
        <%= f.label :password, class: "form-label" %>
        <%= f.password_field :password, autocomplete: "current-password", class: 'form-control' %>
      </div>

      <div class="mb-4 mt-4">
        <%= f.submit t('.sign_in'), class: "btn btn-lg btn-primary" %>
      </div>
    <% end %>

У меня такая же проблема. Это каким-то образом связано с совместимостью Rails 7.0 и Devise 4.8.1. Я не могу зарегистрировать пользователя. База данных sqlite3 также остается пустой, поэтому я не думаю, что это просто проблема флэш-сообщений, а пользователи не вводятся. Некоторые люди решили различные проблемы с Rails 7 / Devise, добавив следующее в config/initializers/devise.rb:

      config.navigational_formats = ['*/*', :html, :turbo_stream]

Я добавил в config/initializers/devise.rb вверху это:

      class TurboFailureApp < Devise::FailureApp
  def respond
    if request_format == :turbo_stream
      redirect
    else
      super
    end
  end

  def skip_format?
    %w(html turbo_stream */*).include? request_format.to_s
  end
end

внутри devise.setup выполните |config|, это:

        # ==> Navigation configuration
  # Lists the formats that should be treated as navigational. Formats like
  # :html, should redirect to the sign in page when the user does not have
  # access, but formats like :xml or :json, should return 401.
  #
  # If you have any extra navigational formats, like :iphone or :mobile, you
  # should add them to the navigational formats lists.
  #
  # The "*/*" below is required to match Internet Explorer requests.
  config.navigational_formats = ['*/*', :html, :turbo_stream]

  # ==> Controller configuration
  # Configure the parent class to the devise controllers.
  config.parent_controller = 'TurboController'

  # ==> Warden configuration
  config.warden do |manager|
    manager.failure_app = TurboFailureApp
  end

затем я создал контроллер в app/controllers/turbo_controller.rb с этим:

      class TurboController < ApplicationController
  class Responder < ActionController::Responder
    def to_turbo_stream
      controller.render(options.merge(formats: :html))
    rescue ActionView::MissingTemplate => error
      if get?
        raise error
      elsif has_errors? && default_action
        render rendering_options.merge(formats: :html, status: :unprocessable_entity)
      else
        redirect_to navigation_location
      end
    end
  end

  self.responder = Responder
  respond_to :html, :turbo_stream
end

Devise был обновлен для работы с Hotwire/Turbo, и вы можете работать с флэш-сообщениями в любом приложении, используя последнюю версию Devise из коробки.

Чтобы обновить существующее приложение с использованием более старой версии Devise с последней конфигурацией, вам необходимо добавить следующую конфигурацию в инициализаторы Devise (config/initializers/devise.rb):

      Devise.setup do |config|
  # ...
  # When using Devise with Hotwire/Turbo, the http status for error responses
  # and some redirects must match the following. The default in Devise for existing
  # apps is `200 OK` and `302 Found respectively`, but new apps are generated with
  # these new defaults that match Hotwire/Turbo behavior.
  # Note: These might become the new default in future versions of Devise.
  config.responder.error_status = :unprocessable_entity
  config.responder.redirect_status = :see_other
end

На основе документации Devise

При использовании Devise Gem оповещения и флэш-сообщения не отображаются в rails 7 из-за Turbo Stream

Я добавил в config/initializers/devise.rb вверху это:

      # config/initializers/devise.rb

class TurboFailureApp < Devise::FailureApp
  def respond
    if request_format == :turbo_stream
      redirect
    else
      super
    end
  end

  def skip_format?
    %w(html turbo_stream */*).include? request_format.to_s
  end
end

# I added in config/initializers/devise.rb, at the top this:

class TurboFailureApp < Devise::FailureApp
 def respond
  if request_format == :turbo_stream
   redirect
  else
   super
  end
end

 def skip_format?
  %w(html turbo_stream */*).include? request_format.to_s
 end
end

# Add these lines inside devise.setup do |config|, this:

config.parent_controller = 'TurboController'

config.navigational_formats = ['*/*', :html, :turbo_stream]

config.warden do |manager|
  manager.failure_app = TurboFailureApp
  # manager.intercept_401 = false
  # manager.default_strategies(scope: :user).unshift :some_external_strategy
end

Также создайте новый файл с именем (turbo_controller.rb) в папке контроллера. В некоторых местах я видел, что без создания этого файла контроллера они добавляли его в файл devise.rb, который не работает в моем случае, поэтому я создаю этот файл.

      # app/controllers/turbo_controller.rb

class TurboController < ApplicationController
  class Responder < ActionController::Responder
    def to_turbo_stream
      controller.render(options.merge(formats: :html))
    rescue ActionView::MissingTemplate => error
      if get?
        raise error
      elsif has_errors? && default_action
        render rendering_options.merge(formats: :html, status: :unprocessable_entity)
      else
        redirect_to navigation_location
      end
    end
  end

  self.responder = Responder
  respond_to :html, :turbo_stream
end

Перейдите по этой ссылке , чтобы найти обходной путь.

  1. создайте контроллер для ответа на html и turbo_stream
  2. изменить config/initializers/devise.rb

Я думаю, что лучшие ответы здесь от Алекса. Flash не отображается в том же представлении в Rails.

и снова Alex Rails 7 форма регистрации не показывает сообщения об ошибках

Это также верно и без разработки, массив сообщений об ошибках принадлежит Active Record и связан с объектом Active Record @user, не прошедшим проверку в модели, которая наследуется от ApplicationRecord и ActiveRecord, а не от разработки. Даже если вы создали пользовательскую модель, контроллер, представления и все действия с нуля, сообщения об ошибках все равно будут присутствовать при сбое проверки модели, но не будут отображаться в представлении. Даже флэш-сообщения не отображаются. Вместо этого в вашем контроллере, если пользователю не удается сохранить, затем в операторе else перенаправить на запрос реферера с сообщением об ошибке флэш-памяти, напримерredirect_to request.referrer, flash: {error: @user.errors.full_messages}, в методе создания или обновлении пользователя, но сообщения об ошибках, похоже, не отображаются, если вы возвращаетесь к новому действию, если только сервер не отвечает 422, поэтому вы можете делать то же самое, что и скаффолдыrender 'new', status: :unprocessable_entity, flash: {error: @user.errors.full_messages}что приводит к ответу сервера 422, это сработает. Это также относится к сообщению об успехе, если @user сохранен в БД. или обновленный вы можете просто показатьredirect_to user_url(@user), notice: " user was successfully created / updated."

Вы заметите в консоли браузера, что если вы зарегистрируетесь с недопустимыми полями, вы не получите сообщение об ошибке full_messages, и вы заметите, что консоль вашего браузера показывает:Error: Form responses must redirect to another location, это если вы возвращаетесь к новому действию, если объект не сохраняется в вашем действии создания.

Похоже, если вы зайдете в application.html.erb и удалите<%= javascript_importmap_tags %>, сохраните файл и попробуйте зарегистрироваться с недопустимыми полями, вы получите все ваши ключи и значения @user.errors.full_messsages. Не решение.

или лучше оставить javascript_importmap_tags в покое, и вы можете добавитьdata: { turbo: false }в вашейform_with. Но все же не устойчивое решение.

или, как предлагали другие, если вы используете devise для регистрации пользователей в initializers.rb, вы можете добавитьconfig.navigational_formats = ['*/*', :html, :turbo_stream]

Правильный способ сделать это на данный момент - это ответ @Sabrina. Вот еще что можно сделать:

Поместите его в рамку

Сделайте это в первую очередь:

      # config/initializers/devise.rb

config.navigational_formats = ['*/*', :html, :turbo_stream]

Теперь форма отображается при ошибках проверки, и она должна быть422. Это не было бы проблемой, если бы мы работали внутри турборамы.

Скопируйте свойapplication.html.erbмакет и переименовать его вdevise.html.erb:

      cp app/views/layouts/application.html.erb app/views/layouts/devise.html.erb

Оберните его в рамку:

      <!-- app/views/layouts/devise.html.erb -->

<!DOCTYPE html>
<html>
  <head> <%= render "layouts/head" %> </head>
  <body>
    <main class="container px-5 mx-auto mt-28">

      <!-- turbo action `advance` to update the url     vvvvvvvv     -->
      <%= turbo_frame_tag :login, data: { turbo_action: :advance } do %>

        <% if alert.present? %>
          <p class="p-3 text-red-500 bg-red-50"> <%= alert %> </p>
        <% end %>
        <%= yield %>

      <% end %>

    </main>
  </body>
</html>

Само по себе это не работает, потому что теперь ответы отображаются без макета, и мы теряем:loginрамка. Обновите контроллер, чтобы исправить это:

      # app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  layout -> { devise_controller? ? "devise" : (false if turbo_frame_request?) }
end

Обновите маршруты, чтобы иметь маршрут GET для каждого маршрута POST :

      devise_scope :user do
  get "/users",          to: redirect("/users/sign_up")
  get "/users/password", to: redirect("/users/password/new")
end

В качестве альтернативы не используйтеturbo_action: :advanceи пропустить лишние маршруты. Вы можете обновить цель фрейма ссылок, чтобы перемещаться за пределы фрейма:

      # app/views/devise/shared/_links.html.erb

<% if controller_name != 'sessions' %>
  <%= link_to "Log in", new_session_path(resource_name), data: { turbo_frame: :_top } %><br />
<% end %>

<% if devise_mapping.registerable? && controller_name != 'registrations' %>
  <%= link_to "Sign up", new_registration_path(resource_name), data: { turbo_frame: :_top } %><br />
<% end %>

# ...

или просто отредактируйте шаблоны и поместите эти ссылки за пределы рамки.


Рендер своими руками

Вторая идея немного более сомнительна, я просто хотел посмотреть, сработает ли она.

      # still need this bit
config.navigational_formats = ['*/*', :html, :turbo_stream]

Проблема в том, что турбо отказывается рендерить200формируют ответ, который мы фактически получаем.

Я имею в виду, что у нас есть ответ, так что просто визуализируйте его:

      // app/(assets/)?javascript(s)?/devise.js
document.addEventListener("turbo:before-fetch-response", async (event) => {
  event.preventDefault();
  const response = new DOMParser().parseFromString(await event.detail.fetchResponse.responseHTML, "text/html")
  document.body.replaceWith(response.body)
})

// add to devise layout, your favorite javascript (include|pack|import_module) tag
<%= javascript_include_tag "devise" %>

// turbo frame and controller layout override are not needed

Логин работает. Регистрация работает. Ссылки перемещаются, и URL-адрес обновляется. Отправка формы не обновляет URL-адрес, что идеально (поддержка обновления после отправки формы здесь не требуется).

добавлениеdata-turbo="false"илиdata: {turbo: false}с помощниками формы может решить эту проблему

Другие вопросы по тегам