Rails 使用者驗證:Devise Gem Customize Controllers
Table of Contents
我們 Rails 使用者驗證:Devise Gem Getting Start 一文中使用 Devise gem 在 Rails 專案中快速實現最基礎的使用者驗證功能。本文進一步對其路由與功能進行客製化。
簡介 ¶
我們可以透過 Devise 的 Generator 來產生 Devise 使用的 Controller,並依照我們的需求來修改功能的行為。
本文我們會使用 demo-devise Rails 專案,從 Rails 使用者驗證:Devise Gem Customize Views 一文的進度開始修改。
環境 ¶
本文使用的環境:
- macOS
- Ruby 3.2.2
- Rails 7.1.2
取得專案程式碼:
git clone https://github.com/timfanda35/demo-devise.git
cd demo-devise
git checkout customize_views
產生 Controller 檔案 ¶
以下指令可以產生 Devise 提供的所有 Controllers,我們需要指定 SCOPE
也就是驗證 Model。在 demo-devise
中我們使用的是慣例的 users
:
bin/rails generate devise:controllers users
在訊息中可以看到,我們將需要修改 config/routes.rb
,應用程式運行的時候才能將請求送往我們客製化後的 Controller 進行處理。
如果只需要客製化部分 Controllers,可以用 -c
指定,多個可以用空白分隔,例如:
bin/rails generate devise:controllers users -c sessions passwords
可以選擇的功能有:
confirmations
:驗證信箱passwords
:忘記密碼registrations
:註冊/更新sessions
:登入unlocks
:帳號解鎖omniauth_callbacks
:OmniAuth Callback
一但產生 Controller 檔案後,官方文件 建議將產生的 Devise 頁面範本從專案目錄 app/views/devise
移到 app/views/users
下。這並不是一個必要的步驟,但會讓我們的專案結構更為一致。
修改路由 ¶
執行指令確認目前 SCOPE
為 users
的路由:
bin/rails routes --grep="users"
可以發現目前 URI 在 /users
底下的路徑,都會將請求送到 devise
底下的 Controllers,而不是 users
底下的 Controllers。所以我們才需要依照提示修改 config/routes
修改路由將指定路徑的請求送到我們客製化的 Controller。
我們可以啟動 Rails Server 來觀察一下目前請求所使用的 Controller
bin/rails server
用瀏覽器開啟網址 http://localhost:3000/,點擊 Sign In,確認一下 Log:
可以發現送往 /users/sign_in
的請求是由 Devise::SessionsController
處理。
讓我們修改 config/routes.rb
,改成由上面步驟產生的 Controller 來處理:
Rails.application.routes.draw do
devise_for :users, controllers: {
sessions: 'users/sessions'
}
resources :posts
root "posts#index"
get "up" => "rails/health#show", as: :rails_health_check
end
重新啟動 Rails Server
bin/rails server
用瀏覽器開啟網址 http://localhost:3000/,點擊 Sign In,確認一下 Log:
可以發現送往 /users/sign_in
的請求變成由 Users::SessionsController
來處理。
客製化 Controller ¶
我們在前述步驟產生的 Controller 都繼承了 Devise::Controller
,而且檔案中已經註解好方法:
註解中的方法使用 super
去執行父物件的方法,我們可以查看 Devise::SessionsController 的原始碼 其中一段:
# POST /resource/sign_in
def create
self.resource = warden.authenticate!(auth_options)
set_flash_message!(:notice, :signed_in)
sign_in(resource_name, resource)
yield resource if block_given?
respond_with resource, location: after_sign_in_path_for(resource)
end
可以看到中間有一段
yield resource if block_given?
我們只需要傳入 block
,就能夠在登入成功後接著進行我們自訂的額外行為,最後再繼續依照原本流程渲染回應頁面:
class Users::SessionsController < Devise::SessionsController
def create
super do |resource|
# do something...
end
end
end
我們移除 Users::SessionsController
中的 create
方法註解,並進行修改,使其能夠在使用者成功登入後,印出登入時來源 IP 的 Log。
class Users::SessionsController < Devise::SessionsController
def create
super do |resource|
Rails.logger.warn "\n\nUser ##{resource.id} has signed in from #{request.remote_ip}\n\n"
end
end
end
我們啟動 Rails Server,登入後確認 Log:
登入成功會顯示 User #1 has signed in from 127.0.0.1
。
登入失敗則不會顯示。
總結 ¶
我們可以透過 Devise 提供的 Generator 產生可供客製化 Controller 檔案,在該 Controller 中我們將 block 傳入父物件的方法來在該方法的 Happy Path 中去執行我們所想要添加的額外行為。