v7.1.3.2
更多資訊請參閱 rubyonrails.org: 更多 Ruby on Rails

使用 Rails 製作僅限 API 的應用程式

在本指南中,您將學習

1 什麼是 API 應用程式?

傳統上,當人們說他們將 Rails 用作「API」時,他們的意思是提供一個可透過程式存取的 API,搭配他們的網路應用程式。例如,GitHub 提供了 API,您可以從您自己的自訂用戶端使用。

隨著客戶端框架的出現,越來越多的開發人員使用 Rails 來建置後端,並在他們的網路應用程式和其他的原生應用程式之間共用。

例如,Twitter 在其網路應用程式中使用其 公開 API,而該應用程式建置為使用 JSON 資源的靜態網站。

許多開發人員並非使用 Rails 透過表單和連結產生與伺服器通訊的 HTML,而是將其網路應用程式視為僅透過 HTML 傳送的 API 應用程式,並使用 JavaScript 使用 JSON API。

本指南涵蓋建置 Rails 應用程式的內容,該應用程式會提供 JSON 資源給 API 應用程式,包括用戶端架構。

2 為何使用 Rails 建置 JSON API?

許多人在思考使用 Rails 建置 JSON API 時,第一個問題是:「使用 Rails 產生 JSON 是不是太過頭了?我是不是應該只使用類似 Sinatra 的東西?」

對於非常簡單的 API,這可能是對的。然而,即使在非常強調 HTML 的應用程式中,應用程式的邏輯大部分都存在於檢視層之外。

大多數人使用 Rails 的原因是,它提供了一組預設值,讓開發人員可以快速上手,而不用做出許多微不足道的決定。

讓我們來看看 Rails 提供的一些現成功能,這些功能仍然適用於 API 應用程式。

在中間層處理

  • 重新載入:Rails 應用程式支援透明重新載入。即使應用程式變大,而且對每個要求重新啟動伺服器變得不可行,這仍然有效。
  • 開發模式:Rails 應用程式提供智慧型預設值,用於開發,讓開發變得愉快,同時不影響生產時間效能。
  • 測試模式:同上開發模式。
  • 記錄:Rails 應用程式記錄每個要求,其詳細程度適當於目前模式。Rails 記錄在開發中包含有關要求環境、資料庫查詢和基本效能資訊。
  • 安全性:Rails 偵測並阻止 IP 欺騙攻擊,並以 計時攻擊意識的方式處理密碼簽章。不知道什麼是 IP 欺騙攻擊或計時攻擊嗎?沒錯。
  • 參數剖析:想要以 JSON 而非 URL 編碼字串指定參數嗎?沒問題。Rails 會為你解碼 JSON,並在 params 中提供給你。想要使用巢狀 URL 編碼參數嗎?這也可以。
  • 條件式 GET:Rails 處理條件式 GET (ETagLast-Modified) 處理請求標頭,並傳回正確的回應標頭和狀態碼。你只需要在控制器中使用 stale? 檢查,Rails 便會為你處理所有 HTTP 詳細資料。
  • HEAD 請求:Rails 會透明地將 HEAD 請求轉換成 GET 請求,並在傳出時只傳回標頭。這使得 HEAD 在所有 Rails API 中都能可靠地運作。

雖然你顯然可以在現有的 Rack 中介軟體中建立這些,但此清單顯示了預設的 Rails 中介軟體堆疊提供了許多價值,即使你「只是產生 JSON」。

在 Action Pack 層中處理

  • 資源導向路由:如果你正在建構 RESTful JSON API,你會想要使用 Rails 路由器。從 HTTP 到控制器的乾淨且慣例對應表示不必花時間思考如何根據 HTTP 對 API 建模。
  • URL 產生:路由的另一面是 URL 產生。一個好的基於 HTTP 的 API 包含 URL(請參閱 GitHub Gist API 以取得範例)。
  • 標頭和重新導向回應:head :no_contentredirect_to user_url(current_user) 會派上用場。當然,你可以手動新增回應標頭,但為什麼要這樣做呢?
  • 快取:Rails 提供頁面、動作和片段快取。在建構巢狀 JSON 物件時,片段快取特別有用。
  • 基本、摘要和權杖驗證:Rails 內建支援三種 HTTP 驗證。
  • 儀器化:Rails 有個儀器化 API,會觸發註冊處理常式,以處理各種事件,例如動作處理、傳送檔案或資料、重新導向和資料庫查詢。每個事件的載荷都附帶相關資訊(對於動作處理事件,載荷包括控制器、動作、參數、要求格式、要求方法和要求的完整路徑)。
  • 產生器:通常很方便產生一個資源,並在一個指令中為你建立模型、控制器、測試程式碼和路由,以便進一步調整。遷移和其他作業也一樣。
  • 外掛程式:許多第三方程式庫都支援 Rails,可減少或消除設定和黏合程式庫與 Web 架構的成本。這包括覆寫預設產生器、新增 Rake 作業和尊重 Rails 選擇(例如記錄器和快取後端)等事項。

當然,Rails 啟動程序也會黏合所有註冊的元件。例如,Rails 啟動程序會在設定 Active Record 時使用你的 config/database.yml 檔案。

簡而言之:你可能沒有想過即使移除檢視層,Rails 的哪些部分仍然適用,但答案是大部分。

3 基本設定

如果你要建立一個 Rails 應用程式,它首先會是一個 API 伺服器,你可以從 Rails 的一個較有限的子集開始,並根據需要新增功能。

3.1 建立一個新應用程式

你可以產生一個新的 api Rails 應用程式

$ rails new my_api --api

這將為你執行三項主要工作

  • 設定你的應用程式,從比一般更有限的一組中間件開始。具體來說,預設情況下,它不會包含任何主要用於瀏覽器應用程式的中間件(例如 cookie 支援)。
  • ApplicationController 繼承自 ActionController::API,而不是 ActionController::Base。與中間件一樣,這將省略任何 Action Controller 模組,這些模組提供主要由瀏覽器應用程式使用的功能。
  • 設定產生器,當你產生一個新資源時,略過產生檢視、輔助程式和資產。

3.2 產生新資源

讓我們建立一個新群組資源,看看我們新建立的 API 如何處理產生新資源。每個群組都會有一個名稱。

$ bin/rails g scaffold Group name:string

在我們可以使用我們的架構程式碼之前,我們需要更新我們的資料庫結構。

$ bin/rails db:migrate

現在如果我們開啟我們的 GroupsController,我們應該會注意到,使用 API Rails 應用程式時,我們只會呈現 JSON 資料。在索引動作中,我們查詢 Group.all 並將其指定給稱為 @groups 的執行個體變數。將其傳遞給 render 並使用 :json 選項,將自動將群組呈現為 JSON。

# app/controllers/groups_controller.rb
class GroupsController < ApplicationController
  before_action :set_group, only: %i[ show update destroy ]

  # GET /groups
  def index
    @groups = Group.all

    render json: @groups
  end

  # GET /groups/1
  def show
    render json: @group
  end

  # POST /groups
  def create
    @group = Group.new(group_params)

    if @group.save
      render json: @group, status: :created, location: @group
    else
      render json: @group.errors, status: :unprocessable_entity
    end
  end

  # PATCH/PUT /groups/1
  def update
    if @group.update(group_params)
      render json: @group
    else
      render json: @group.errors, status: :unprocessable_entity
    end
  end

  # DELETE /groups/1
  def destroy
    @group.destroy
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_group
      @group = Group.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def group_params
      params.require(:group).permit(:name)
    end
end

最後,我們可以從 Rails 主控台將一些群組新增到我們的資料庫

irb> Group.create(name: "Rails Founders")
irb> Group.create(name: "Rails Contributors")

在應用程式中有一些資料後,我們可以啟動伺服器並瀏覽 https://127.0.0.1:3000/groups.json 來查看我們的 JSON 資料。

[
{"id":1, "name":"Rails Founders", "created_at": ...},
{"id":2, "name":"Rails Contributors", "created_at": ...}
]

3.3 變更現有應用程式

如果你想將現有應用程式轉換成 API 應用程式,請閱讀下列步驟。

config/application.rb 中,在 Application 類別定義的最上方新增下列程式碼列

config.api_only = true

config/environments/development.rb 中,設定 config.debug_exception_response_format 來設定在開發模式發生錯誤時,回應中使用的格式。

若要呈現包含偵錯資訊的 HTML 頁面,請使用值 :default

config.debug_exception_response_format = :default

若要呈現偵錯資訊並保留回應格式,請使用值 :api

config.debug_exception_response_format = :api

config.api_only 設定為 true 時,預設會將 config.debug_exception_response_format 設定為 :api

最後,在 app/controllers/application_controller.rb 內,取代

class ApplicationController < ActionController::Base
end

執行

class ApplicationController < ActionController::API
end

4 選擇中間件

API 應用程式預設會附帶下列中間件

  • ActionDispatch::HostAuthorization
  • Rack::Sendfile
  • ActionDispatch::Static
  • ActionDispatch::Executor
  • ActionDispatch::ServerTiming
  • ActiveSupport::Cache::Strategy::LocalCache::Middleware
  • Rack::Runtime
  • ActionDispatch::RequestId
  • ActionDispatch::RemoteIp
  • Rails::Rack::Logger
  • ActionDispatch::ShowExceptions
  • ActionDispatch::DebugExceptions
  • ActionDispatch::ActionableExceptions
  • ActionDispatch::Reloader
  • ActionDispatch::Callbacks
  • ActiveRecord::Migration::CheckPending
  • Rack::Head
  • Rack::ConditionalGet
  • Rack::ETag

有關這些 middleware 的更多資訊,請參閱 Rack 指南中的內部 middleware區段。

其他外掛程式,包括 Active Record,可能會新增其他 middleware。一般來說,這些 middleware 與您建置的應用程式類型無關,而且在僅限 API 的 Rails 應用程式中很有意義。

您可以透過下列方式取得應用程式中所有 middleware 的清單

$ bin/rails middleware

4.1 使用 Rack::Cache

當與 Rails 搭配使用時,Rack::Cache 會使用 Rails 快取儲存區作為其實體和 meta 儲存區。這表示如果您使用 memcache 作為 Rails 應用程式,內建的 HTTP 快取就會使用 memcache。

若要使用 Rack::Cache,您首先需要將 rack-cache 寶石新增到 Gemfile,並將 config.action_dispatch.rack_cache 設為 true。若要啟用其功能,您會希望在控制器中使用 stale?。以下是 stale? 使用範例。

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

  if stale?(last_modified: @post.updated_at)
    render json: @post
  end
end

呼叫 stale? 會將要求中的 If-Modified-Since 標頭與 @post.updated_at 進行比較。如果標頭比上次修改的時間新,這個動作會傳回「304 未修改」回應。否則,它會呈現回應並在其中包含 Last-Modified 標頭。

一般來說,這個機制會在每個客戶端基礎上使用。Rack::Cache 讓我們可以在客戶端之間共用這個快取機制。我們可以在呼叫 stale? 時啟用跨客戶端快取

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

  if stale?(last_modified: @post.updated_at, public: true)
    render json: @post
  end
end

這表示 Rack::Cache 會將 URL 的 Last-Modified 值儲存在 Rails 快取中,並將 If-Modified-Since 標頭新增到任何後續針對相同 URL 的入站要求。

將其視為使用 HTTP 語意的頁面快取。

4.2 使用 Rack::Sendfile

當您在 Rails 控制器內使用 send_file 方法時,它會設定 X-Sendfile 標頭。Rack::Sendfile 負責實際傳送檔案。

如果您的前端伺服器支援加速檔案傳送,Rack::Sendfile 會將實際的檔案傳送工作卸載到前端伺服器。

您可以使用 config.action_dispatch.x_sendfile_header 在適當的環境設定檔中設定前端伺服器用於此目的的標頭名稱。

您可以在 Rack::Sendfile 文件 中進一步了解如何將 Rack::Sendfile 與熱門前端搭配使用。

以下是這些伺服器在設定支援加速檔案傳送後,此標頭的一些值

# Apache and lighttpd
config.action_dispatch.x_sendfile_header = "X-Sendfile"

# Nginx
config.action_dispatch.x_sendfile_header = "X-Accel-Redirect"

請務必按照 Rack::Sendfile 文件中的說明設定您的伺服器以支援這些選項。

4.3 使用 ActionDispatch::Request

ActionDispatch::Request#params 會從 JSON 格式的用戶端取得參數,並在 params 內讓您的控制器使用。

要使用此功能,您的用戶端需要提出包含 JSON 編碼參數的請求,並將 Content-Type 指定為 application/json

以下是在 jQuery 中的範例

jQuery.ajax({
  type: 'POST',
  url: '/people',
  dataType: 'json',
  contentType: 'application/json',
  data: JSON.stringify({ person: { firstName: "Yehuda", lastName: "Katz" } }),
  success: function(json) { }
});

ActionDispatch::Request 會看到 Content-Type,而您的參數會是

{ person: { firstName: "Yehuda", lastName: "Katz" } }

4.4 使用 Session 中介軟體

下列用於會話管理的中介軟體會從 API 應用程式中排除,因為它們通常不需要會話。如果您的某個 API 用戶端是瀏覽器,您可能想新增其中一個

  • ActionDispatch::Session::CacheStore
  • ActionDispatch::Session::CookieStore
  • ActionDispatch::Session::MemCacheStore

重新加入這些的訣竅在於,預設情況下,它們在加入時會傳遞 session_options(包括會話金鑰),因此您不能只加入一個 session_store.rb 初始化程式,加入 use ActionDispatch::Session::CookieStore,並讓會話像往常一樣運作。(說清楚一點:會話可能會運作,但您的會話選項將會被忽略 - 亦即會話金鑰會預設為 _session_id

您必須在建立中間件之前(例如 config/application.rb)設定相關選項,而不是使用初始化程式,並將它們傳遞給您偏好的中間件,如下所示

# This also configures session_options for use below
config.session_store :cookie_store, key: '_interslice_session'

# Required for all session management (regardless of session_store)
config.middleware.use ActionDispatch::Cookies

config.middleware.use config.session_store, config.session_options

4.5 其他中間件

Rails 附帶許多其他您可能想在 API 應用程式中使用的中間件,特別是如果您的其中一個 API 用戶端是瀏覽器

  • Rack::MethodOverride
  • ActionDispatch::Cookies
  • ActionDispatch::Flash

任何這些中間件都可以透過以下方式加入

config.middleware.use Rack::MethodOverride

4.6 移出中間件

如果您不想使用 API 專用中間件組中預設包含的中間件,您可以使用以下方式將其移除

config.middleware.delete ::Rack::Sendfile

請記住,移除這些中間件會移除 Action Controller 中某些功能的支援。

5 選擇控制器模組

API 應用程式(使用 ActionController::API)預設附帶以下控制器模組

ActionController::UrlFor url_for 和類似的輔助程式可用。
ActionController::Redirecting 支援 redirect_to
AbstractController::RenderingActionController::ApiRendering 基本渲染支援。
ActionController::Renderers::All 支援 render :json 和相關函式。
ActionController::ConditionalGet 支援 stale?
ActionController::BasicImplicitRender 如果沒有明確的回應,請務必傳回一個空的回應。
ActionController::StrongParameters 支援參數過濾與 Active Model 大量指派結合使用。
ActionController::DataStreaming 支援 send_filesend_data
AbstractController::Callbacks 支援 before_action 和類似的輔助程式。
ActionController::Rescue 支援 rescue_from
ActionController::Instrumentation 支援 Action Controller 定義的儀器掛鉤(請參閱 儀器指南 以取得更多相關資訊)。
ActionController::ParamsWrapper 將參數雜湊包裝到巢狀雜湊中,這樣您就不必指定根元素來傳送 POST 要求,例如。
ActionController::Head 支援傳回沒有內容、只有標頭的回應。

其他外掛程式可能會新增其他模組。您可以在 Rails 主控台中取得包含在 ActionController::API 中的所有模組清單

irb> ActionController::API.ancestors - ActionController::Metal.ancestors
=> [ActionController::API,
    ActiveRecord::Railties::ControllerRuntime,
    ActionDispatch::Routing::RouteSet::MountedHelpers,
    ActionController::ParamsWrapper,
    ... ,
    AbstractController::Rendering,
    ActionView::ViewPaths]

5.1 新增其他模組

所有 Action Controller 模組都知道它們的相依模組,因此您可以放心地將任何模組包含到您的控制器中,所有相依項也會包含和設定。

您可能想要新增的一些常見模組

  • AbstractController::Translation:支援 lt 本地化和翻譯方法。
  • 支援基本、摘要或權杖 HTTP 驗證
    • ActionController::HttpAuthentication::Basic::ControllerMethods
    • ActionController::HttpAuthentication::Digest::ControllerMethods
    • ActionController::HttpAuthentication::Token::ControllerMethods
  • ActionView::Layouts:支援在呈現時使用版面配置。
  • ActionController::MimeResponds:支援 respond_to
  • ActionController::Cookies:支援 cookies,其中包括支援已簽署和已加密的 cookie。這需要 cookie 中介軟體。
  • ActionController::Caching:支援 API 控制器檢視快取。請注意,您需要手動在控制器中指定快取儲存,如下所示

    class ApplicationController < ActionController::API
      include ::ActionController::Caching
      self.cache_store = :mem_cache_store
    end
    

    Rails 不會 自動傳遞此組態。

新增模組的最佳位置是在您的 ApplicationController 中,但您也可以將模組新增到個別控制器中。

回饋

歡迎您協助改善本指南的品質。

如果您發現任何錯字或事實錯誤,請協助我們修正。首先,您可以閱讀我們的 文件貢獻 部分。

您也可能會發現不完整或過時的內容。請協助新增任何遺漏的 main 文件。請務必先查看 Edge Guides,確認問題是否已在 main 分支中修正。查看 Ruby on Rails 指南指南,了解風格和慣例。

如果您發現需要修正的地方,但無法自行修補,請 開啟問題

最後但並非最不重要的一點,歡迎在 官方 Ruby on Rails 論壇 針對 Ruby on Rails 文件進行任何討論。