Я использую гем devise для организации авторизации...
创建于:2025年6月5日
使用 O4 Mini High 回答 Chat01
创建于:2025年6月5日
使用 O4 Mini High 回答 Chat01
Я использую гем devise для организации авторизации в моем rails приложении. При нажатии на кнопку log out у меня возникает такая ошибка
<nav class="navbar navbar-expand-lg"> <div class="container"> <h1 class="navbar-brand text-white fs-4 fw-bold"><%= t('application.header.title') %></h1> <div class="d-flex justify-content-end"> <%= render partial: 'layouts/partials/language_dropdown' %> <% if user_signed_in? %> <%= link_to current_user.name, edit_user_registration_path, class: "btn btn-lightpink me-2" %> <%= link_to t("application.header.menu.log_out_button"), destroy_user_session_path, method: :delete, class: "btn btn-darkpink me-2" %> <% else %> <%= link_to t("application.header.menu.sign_up_button"), new_user_registration_path, class: "btn btn-lightpink me-2" %> <%= link_to t("application.header.menu.log_in_button"), new_user_session_path, class: "btn btn-darkpink me-2" %> <% end %> </div> </div> </nav>routes.rb
Rails.application.routes.draw do
devise_for :users
get 'up' => 'rails/health#show', as: :rails_health_check
get 'service-worker' => 'rails/pwa#service_worker', as: :pwa_service_worker
get 'manifest' => 'rails/pwa#manifest', as: :pwa_manifest
get '/', to: redirect('/en')
scope '(:locale)', locale: /en|ru/ do
root 'pages#home'
end
end
в config/initializers/devise.rb есть строка config.sign_out_via = :delete
Как исправить
Ошибка возникает из-за того, что Devise по умолчанию ожидает, что запрос на выход будет отправлен методом DELETE, а ваша ссылка без подключённого JS отправляет его как GET. В результате Rails пытается обработать GET-запрос на «/users/sign_out», а такого маршрута нет (см. destroy_user_session_path
в rake routes
— он настроен только на DELETE).
Убедиться, что Rails UJS (или jquery-ujs) действительно подключён и работает
Devise-овская ссылка:
erb<%= link_to t("application.header.menu.log_out_button"), destroy_user_session_path, method: :delete, class: "btn btn-darkpink me-2" %>
рассчитывает на то, что через JavaScript (Rails UJS) будет сгенерирован настоящий DELETE-запрос. Если на странице не подключён или не инициализируется rails-ujs
(или, в старых версиях Rails, jquery-ujs
), браузер просто отправляет обычный GET, и вы получаете «No route matches [GET] "/users/sign_out"».
Проверьте в app/views/layouts/application.html.erb
, что у вас есть:
erb<head> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track": "reload" %> <%= javascript_include_tag "application", "data-turbolinks-track": "reload" %> </head>
и в app/javascript/packs/application.js
(или app/assets/javascripts/application.js
, в зависимости от того, чем вы собираете ассеты):
js// Для Rails 6+ (Webpacker + rails-ujs): import Rails from "@rails/ujs" Rails.start() // (Если используете Turbolinks, то затем import "turbolinks"; Turbolinks.start(); и т.д.)
или (для старых версий Rails с Sprockets):
js//= require rails-ujs //= require turbolinks //= require_tree .
После этого перезапустите сервер и проверьте в браузере в консоли разработчика, что на странице загружен rails-ujs
и что при клике на ссылку «Выход» срабатывает JavaScript (метод должен меняться на DELETE). Если JS не грузится — браузер будет делать GET.
(Менее предпочтительный вариант) Сменить способ выхода на GET
Если по каким-то причинам вы не хотите (или не можете) подключать UJS, можно заставить Devise принимать выход по GET-запросу. Для этого в файле config/initializers/devise.rb
замените строку:
rubyconfig.sign_out_via = :delete
на
rubyconfig.sign_out_via = :get
После этого ссылка
erb<%= link_to t("...log_out_button"), destroy_user_session_path, class: "btn btn-darkpink me-2" %>
без method: :delete
будет работать как GET, и Rails найдёт маршрут. Но этот способ менее безопасен (CSRF-уязвимость), поэтому рекомендуется всё же сделать первый пункт.
Итого: наиболее корректный путь — убедиться, что в вашем проекте подключён и инициализируется rails-ujs
(или jquery-ujs
), чтобы link_to ... method: :delete
действительно посылал DELETE-запрос. Если же вы не хотите/не можете подключать JS-обработку, можно временно поменять config.sign_out_via
на :get
, но помните о рисках.