跳到內文 跳到搜尋

Action Controller Base

Action Controller 是 Rails 網路請求的核心。它們是由一個或多個動作組成,會在收到請求時執行,然後回傳範本或重新導向到另一個動作。動作定義為控制器上的 public 方法,將會透過 Rails 路線自動提供給網路伺服器使用。

預設情況下,Rails 應用程式中的 ApplicationController 才會繼承自 ActionController::Base。所有其他控制器都會繼承 ApplicationController。這樣可以透過一個類別來設定請求偽造保護及敏感請求參數的過濾等事項。

範例控制器可能如下所示:

class PostsController < ApplicationController
  def index
    @posts = Post.all
  end

  def create
    @post = Post.create params[:post]
    redirect_to posts_path
  end
end

動作預設會在執行動作中的程式碼後,在與控制器和動作名稱對應的 app/views 目錄中回傳一個範本。例如 PostsController 的 index 動作會在設定好 @posts 執行個體變數後,預設回傳 app/views/posts/index.html.erb 範本。

不像 index,create 動作不會回傳範本。執行完它的主要目的 (建立新的文章) 後,它會執行重新導向。這個重新導向透過回傳外部 302 Moved HTTP 回應來運作,會將使用者帶到 index 動作。

這兩個方法代表 Action Controller 中使用兩個基本的動作原型:取得且顯示以及執行且重新導向。大部分的動作都是以下列這些主題為變化。

請求

在每個請求中,路由器會找出 controlleraction 鍵的值。這些值決定會呼叫哪個控制器和動作。其他請求參數、工作階段 (如果有的話) 以及包含所有 HTTP 標頭的完整請求會透過存取器方法對動作公開。再執行動作。

完整的請求物件可用於透過請求存取器來取得,它主要是用於查詢 HTTP 標頭

def server_ip
  location = request.env["REMOTE_ADDR"]
  render plain: "This server hosted at #{location}"
end

參數

所有請求參數 (無論它們是來自於 URL 中的查詢字串,或來自於透過 POST 請求送出的表單資料) 都可以用於回傳雜湊的 params 方法中取得。例如透過 /posts?category=All&limit=5 執行的動作,將會在 params 中包含 { "category" => "All", "limit" => "5" }

也可以透過指定方括號中的鍵,像是這樣來建構多維的參數雜湊:

<input type="text" name="post[name]" value="david">
<input type="text" name="post[address]" value="hyacintvej">

一個包含以下輸入項目的表單中送出的請求,將會包含 { "post" => { "name" => "david", "address" => "hyacintvej" } }。如果 address 輸入項目名稱為 post[address][street]params 會包含 { "post" => { "address" => { "street" => "hyacintvej" } } }。巢狀可以無限多層。

工作階段

工作階段允許你在請求之間儲存物件。這對於尚未儲存的物件來說很有用,例如在多頁式處理中建構的 Signup 物件,或是不太會改變而且一直需要的物件,例如需要登入的系統中的 User 物件。不過,工作階段不應當做物件的快取來使用,因為物件很有可能在不知情的情況下遭到改變。要保持所有資料同步通常會有太多工作要做,而資料庫在保持資料同步方面做得很好。

透過存取雜湊,你可以透過 session 方法將物件配置在作業階段中

session[:person] = Person.authenticate(user_name, password)

你可以透過相同的雜湊再次擷取

"Hello #{session[:person]}"

移除作業階段中物件時,你可以指定單一 key 至 nil

# removes :person from session
session[:person] = nil

或可透過 reset_session 移除整個作業階段。

預設情況下,作業階段儲存在加密瀏覽器 cookie 中(請參閱 ActionDispatch::Session::CookieStore)。因此使用者將無法讀取或編輯作業階段資料。然而,使用者可在 cookie 過期後保持一個副本,因此你應避免在基於 cookie 的作業階段中儲存敏感資料。

回應

每個動作會產生回應,包含標頭和要傳送給使用者瀏覽器的文件。實際回應物件會透過使用渲染和重新導向自動產生,不需要使用者介入。

渲染

動作控制器透過下列五種渲染方法中的其中一種將內容傳送給使用者。最通用且常見的是渲染範本。動作組合中包含動作檢視,用於啟用 ERB 範本的渲染。它已自動設定。控制器透過指定執行個體變數將物件傳遞給檢視

def show
  @post = Post.find(params[:id])
end

接著,檢視便可以自動取得這些物件

Title: <%= @post.title %>

你不必仰賴自動化渲染。例如,可能產生不同範本的動作將使用手動渲染方法

def search
  @results = Search.find(params[:query])
  case @results.count
    when 0 then render action: "no_results"
    when 1 then render action: "show"
    when 2..10 then render action: "show_many"
  end
end

深入了解 ERB 和在 ActionView::Base 中撰寫 Builder 範本。

重新導向

重新導向用於在不同的動作之間移動。例如,在將部落格條目儲存至資料庫的 create 動作之後,我們可能會想要向使用者顯示新條目。由於我們遵循良好的 DRY 原則(別重複工作),我們將重複使用(並重新導向至)我們假設已建立的 show 動作。程式碼會如下所示

def create
  @entry = Entry.new(params[:entry])
  if @entry.save
    # The entry was saved correctly, redirect to show
    redirect_to action: 'show', id: @entry.id
  else
    # things didn't go so well, do something else
  end
end

在此情況中,在將新條目儲存至資料庫後,使用者將重新導向至 show 方法,接著會執行該方法。請注意,這是外部的 HTTP 等級重新導向,將導致瀏覽器提出第二次要求(對 show 動作的 GET),而不是在一個要求中同時呼叫「create」和「show」的內部重新導向。

深入了解 ActionController::Redirecting 中的 redirect_to 以及你的選項。

呼叫多個重新導向或渲染

動作只能執行單一渲染或單一重新導向。再次嘗試執行其中一項將會導致 DoubleRenderError

def do_something
  redirect_to action: "elsewhere"
  render action: "overthere" # raises DoubleRenderError
end

如果需要根據某項條件重新導向,請務必新增「return」以停止執行。

def do_something
  if monkeys.nil?
    redirect_to(action: "elsewhere")
    return
  end
  render action: "overthere" # won't be called if monkeys is nil
end
方法
W

常數

模組 = [ AbstractController::Rendering, AbstractController::Translation, AbstractController::AssetPaths, Helpers, UrlFor, Redirecting, ActionView::Layouts, Rendering, Renderers::All, ConditionalGet, EtagWithTemplateDigest, EtagWithFlash, Caching, MimeResponds, ImplicitRender, StrongParameters, ParameterEncoding, Cookies, Flash, FormBuilder, RequestForgeryProtection, ContentSecurityPolicy, PermissionsPolicy, RateLimiting, AllowBrowser, Streaming, DataStreaming, HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods, HttpAuthentication::Token::ControllerMethods, DefaultHeaders, Logging, AbstractController::Callbacks, Rescue, Instrumentation, ParamsWrapper ]
 
PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + %i( @_params @_response @_request @_config @_url_options @_action_has_layout @_view_context_class @_view_renderer @_lookup_context @_routes @_view_runtime @_db_runtime @_helper_proxy @_marked_for_same_origin_verification @_rendered_format )
 

定義不應傳播到檢視的一些內部變數。

類別公開方法

without_modules(*modules)

捷徑輔助器,它會傳回 `ActionController::Base` 中包含的所有模組,唯獨傳入作為引數的模組除外

class MyBaseController < ActionController::Metal
  ActionController::Base.without_modules(:ParamsWrapper, :Streaming).each do |left|
    include left
  end
end

這樣就能更靈活地控制要排除哪些模組,而且更容易建立一個裸的控制器類別,而無需手動列出需要的模組。

# File actionpack/lib/action_controller/base.rb, line 222
def self.without_modules(*modules)
  modules = modules.map do |m|
    m.is_a?(Symbol) ? ActionController.const_get(m) : m
  end

  MODULES - modules
end