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
(ETag
和Last-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_content
和redirect_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::Rendering 和 ActionController::ApiRendering |
基本渲染支援。 |
ActionController::Renderers::All |
支援 render :json 和相關函式。 |
ActionController::ConditionalGet |
支援 stale? 。 |
ActionController::BasicImplicitRender |
如果沒有明確的回應,請務必傳回一個空的回應。 |
ActionController::StrongParameters |
支援參數過濾與 Active Model 大量指派結合使用。 |
ActionController::DataStreaming |
支援 send_file 和 send_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
:支援l
和t
本地化和翻譯方法。- 支援基本、摘要或權杖 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 文件進行任何討論。