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
Перейдите по этой ссылке , чтобы найти обходной путь.
- создайте контроллер для ответа на html и turbo_stream
- изменить 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}
с помощниками формы может решить эту проблему