我正在使用 Ruby on Rails 構建一個簡單的博客應用程式,它允許用戶根據權限和限制登錄/注銷、注冊并對其文章和個人資料執行操作。我遇到了銷毀用戶操作的問題。在 users/index 視圖中(列出所有現有用戶),由于 url 路徑不包含 {:id},它不會導致錯誤,但 redirect_to root_path 不起作用。如果在用戶/顯示頁面(包含一些資訊和相關文章的個人資料頁面)中執行相同的操作,由于 url 是 localhost/users/id,當用戶被洗掉時,我得到“找不到用戶” id'=33” 錯誤如下所示。如果我手動轉到根路由,則會顯示成功的帳戶洗掉訊息并且操作正確執行。所以這不是 DESTROY 不起作用,而是重定向我相信。我已經嘗試重定向到不同的路徑,但它仍然不起作用。以下是相關檔案:
路線.rb
Rails.application.routes.draw do
root "pages#home"
get "about", to: "pages#about"
resources :articles
get "signup", to: "users#new"
resources :users, except: [:new]
get 'login', to: 'sessions#new'
post 'login', to: 'sessions#create'
get 'logout' => :destroy, to: 'sessions#destroy'
end
pages_controller
class PagesController < ApplicationController
def home
redirect_to articles_path if logged_in?
end
def about
end
end
users_controller
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
before_action :require_user, only: [:edit, :update]
before_action :require_same_user, only: [:edit, :update, :destroy]
def index
@users = User.all
end
def show
@articles = @user.articles
end
def new
@user = User.new
end
def edit
end
def create
@user = User.new(user_params)
if(@user.save)
session[:user_id] = @user.id #logs user in automatically once they are signed up
flash[:notice] = "Welcome to AlphaBlog, #{@user.username}!"
redirect_to articles_path
else
render 'new'
end
end
def update
if @user.update(user_params)
flash[:notice] = "Account updated!"
redirect_to @user
else
render 'edit'
end
end
def destroy
@user.destroy
session[:user_id] = nil
flash[:notice] = "Account and all associated articles deleted!"
redirect_to root_path
end
private
def user_params
params.require(:user).permit(:username, :email, :password)
end
def set_user
@user = User.find(params[:id])
end
def require_same_user
if current_user != @user
flash[:alert] = "You can only edit your own profile!"
redirect_to current_user
end
end
end
session_controller
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
session[:user_id] = user.id
flash[:notice] = "Logged in successfully!"
redirect_to user
else
flash.now[:alert] = "There was something wrong with your login details!"
render 'new'
end
end
def destroy
session[:user_id] = nil
flash[:notice] = "Logged out."
redirect_to root_path
end
end
用戶/index.html.erb
<div class = "header">
<h1>
AlphaBlog
<% if logged_in? %>
<%= link_to 'Articles', articles_path, method: :get, class: "index-button-to" %>
<% else %>
<%= link_to 'Home', root_path(), method: :get, class: "index-button-to" %>
<%= link_to 'Articles', articles_path, method: :get, class: "index-button-to" %>
<% end %>
<%= render 'layouts/log_in_out_navigation'%>
</h1>
</div>
<h2>Alpha Bloggers</h2>
<div >
<%# cycle through all articles and show them all in a table %>
<% @users.each do |user| %>
<div class = "index-article-container">
<div style = "color:rgb(16, 136, 255);">
<%= user.username %>
</div>
<div class="white">
<div class="index-article-title">
<%= gravatar_for(user, size: 150) %>
</div>
<div >
<%# gives the plural word for multiple articles %>
<%= pluralize(user.articles.count, "article") %>
</div>
<div class="index-article-actions">
<%# shows selected article page %>
<%= link_to 'View Profile', user, class: "index-link-to show" %>
<% if logged_in? && current_user.username == user.username %>
<%# shows selected article EDIT page. edit_article_path because in routes,
the prefix for edit is edit_article && (article) because we need the id for the path as well%>
<%= link_to 'Edit Profile', edit_user_path(user), data: { turbo_method:
:get}, class: "index-link-to edit" %>
<%= link_to 'Delete Profile', user_path(current_user), data: {
turbo_method: :delete, turbo_confirm: "Are you sure? (This will also delete all of your
articles)" }, class: "index-link-to delete" %>
<% end %>
</div>
</div>
<div class="index-created-updated">
Joined <%= time_ago_in_words(user.created_at) %> ago.
</div>
</div>
<% end %>
用戶/show.html.erb
<div class = "header">
<h1>
AlphaBlog
<% if logged_in? %>
<%= link_to 'Articles', articles_path, method: :get, class: "index-button-to" %>
<%= link_to 'Bloggers', users_path, method: :get, class: "index-button-to" %>
<% else %>
<%= link_to 'Home', root_path(), method: :get, class: "index-button-to" %>
<%= link_to 'Articles', articles_path, method: :get, class: "index-button-to" %>
<%= link_to 'Bloggers', users_path, method: :get, class: "index-button-to" %>
<% end %>
<%= render 'layouts/log_in_out_navigation'%>
</h1>
</div>
<h2> <%= @user.username %>'s profile </h2>
<div >
<%# gravatar_for method created in helpers/application_helper %>
<%= gravatar_for @user, size: 200 %>
<% if logged_in? && current_user.username == @user.username %>
<div >
<%= link_to "Edit Profile", edit_user_path(@user), class: "index-link-to edit" %>
<%= link_to 'Delete Profile', user_path(current_user), data: { turbo_method: :delete,
turbo_confirm: "Are you sure? (This will also delete all of your articles)" }, class: "index-
link-
to delete", style: "margin-top:0.3vh" %>
</div>
<% end %>
</div>
<h3 style = "text-align:center">Articles</h3>
<%= render 'articles/article' %>
錯誤頁面

uj5u.com熱心網友回復:
我認為這里的答案實際上是您的路線和控制器的布局非常不同(或者首先不重新啟動輪子)。如果您創建一個管理其他用戶的系統,則通過引數傳遞用戶 ID 會很好 - 但當用戶管理他們自己的組態檔時,它會非常不穩定。
例如,這是用戶在 vanilla Devise 設定中 CRUD 自己的組態檔的方式:
Verb URI Pattern Controller#Action
------------------------------------------------------------------------
GET /users/cancel(.:format) devise/registrations#cancel
GET /users/sign_up(.:format) devise/registrations#new
GET /users/edit(.:format) devise/registrations#edit
PATCH /users(.:format) devise/registrations#update
PUT /users(.:format) devise/registrations#update
DELETE /users(.:format) devise/registrations#destroy
POST /users(.:format) devise/registrations#create
請注意 URI 模式中缺少:id引數。那是因為它暗示有問題的資源是當前登錄的用戶,并且用戶是通過會話(或令牌)識別的。
Registrations如果程式員以后想添加一個 UsersController 來管理其他用戶,控制器的命名是為了避免歧義。
如果您想做類似的事情,您可以 使用resource宏而不是resources.
# routes for user registration
resource :registrations,
only: [:new, :edit, :update, :create, :destroy]
# routes for viewing other users
resources :users, only: [:index, :show]
這將產生:
Prefix Verb URI Pattern Controller#Action
-----------------------------------------------------------------------
new_registrations GET /registrations/new(.:format) registrations#new
edit_registrations GET /registrations/edit(.:format) registrations#edit
registrations GET /registrations(.:format) registrations#show
PATCH /registrations(.:format) registrations#update
PUT /registrations(.:format) registrations#update
DELETE /registrations(.:format) registrations#destroy
POST /registrations(.:format) registrations#create
隨意命名。這里的核心要點是不要混淆兩個完全不同的問題——用戶管理和用戶注冊,并且每個職責都有單獨的端點和控制器。
然后在您的控制器中,您只需從會話中對用戶進行身份驗證,如果用戶未通過身份驗證,則重定向用戶:
# Handles user account registration, updates and deleting accounts
class RegistrationsController < ApplicationController
before_action :require_user, except: [:new, :create]
# Displays the form for signing up a user
# GET /registrations
def new
@user = User.new
end
# Register a new user and sign them in
# POST /registrations
def create
@user = User.new(user_params)
if @user.save
reset_session # avoids session fixation attacks
session[:user_id] = @user.id #logs user in automatically once they are signed up
flash[:notice] = "Welcome to AlphaBlog, #{@user.username}!"
redirect_to articles_path
else
render :new
end
end
# Form for editing the users own profile
# GET /registrations/edit
def edit
@user = current_user
end
# Update the currently signed in user
# PATCH /registrations
def update
@user = current_user
if @user.update(user_params)
flash[:notice] = "Account updated!"
redirect_to current_user
else
render :new
end
end
# Cancel the current users registration
# DELETE /registrations
def delete
current_user.delete
reset_session # avoids session fixation attacks
flash[:notice] = "Account and all associated articles deleted!"
redirect_to root_path
end
private
def user_params
params.require(:user).permit(:username, :email, :password)
end
end
# Displays users
# Managing accounts is handled by RegistrationsController
class UsersController < ApplicationController
# GET /users
def index
@users = User.all
end
# GET /users/1
def show
@user = User.find(params[:id])
@articles = @user.articles
end
end
由于它們在路徑中沒有 id,因此您需要將洗掉按鈕設定為發送到正確的路徑:
<%= button_to "Delete your account", registrations_path, method: :delete %>
并調整您的表格:
<%= form_with(model: @user, url: registrations_path) do |form| %>
# ...
<% end %>
這樣做的正確方法實際上是像 Devise 那樣做,并且有一個正常的鏈接,將 GET 請求發送到“你確定要洗掉你的帳戶嗎?” 然后頁面要求用戶輸入他們的密碼或電子郵件并提交洗掉請求,以便用戶不會意外洗掉他們的帳戶。
但話又說回來,除非你想要一個漫長而乏味的輪子制造課程,否則不要重新發明認證輪子。
uj5u.com熱心網友回復:
所以我最終使用max's answer的簡化版本自己解決了這個問題。
發生錯誤是因為銷毀操作在此行中為會話的用戶 IDusers_controller分配了一個值nil
session[:user_id] = nil
這導致應用程式拋出那個丑陋的錯誤,盡管操作本身正在正確執行,因為user_id在 的其他操作中需要a users_controller(在錯誤的情況下,set_user.
我通過在as中取出users_controller和取出 destroy 動作解決了這個問題,這樣它就可以獨立于其他代碼,并為這個動作創建了一個特定的路線。sessions_controllerdestroy_userusers_controller
所以總結一下:
洗掉銷毀
users_controller添加
destroy_user如下sessions_controller:def destroy_user @user = User.find_by(id: session[:user_id]) if current_user != @user flash[:alert] = "You can only edit your own profile!" redirect_to current_user end @user.destroy session[:user_id] = nil flash[:notice] = "Account and all associated articles deleted!" redirect_to root_path end將此路徑添加到
routes.rbget 'destroy_user' => :destroy, to: 'sessions#destroy_user'將洗掉組態檔鏈接更改為:
<%= link_to 'Delete Profile', destroy_user_path(current_user), data: { turbo_method: :delete, turbo_confirm: "Are you sure? (This will also delete all of your articles)" }, class: "index-link-to delete" %>
uj5u.com熱心網友回復:
我不確定您是否真正了解了這一點。在您最初的方法中,我懷疑發生了兩件事:
在 users/index.html.erb 你有
link_to 'Delete Profile', user_path(current_user),但我認為你想要user_path(user). 您目前擁有的每個洗掉按鈕都會嘗試洗掉同一個用戶。錯誤表明您正在嘗試執行“顯示”而不是“破壞”這一事實使我懷疑您沒有正確加載渦輪增壓器。你沒有說你使用的是什么版本的 rails,但是對于早于 7 的版本,你沒有開箱即用的 turbo,你應該改用 UJS。將此https://guides.rubyonrails.org/getting_started.html#deleting-an-article與此https://guides.rubyonrails.org/v6.1/getting_started.html#deleting-an-article進行比較
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/460266.html
