Grape Gem 教程:如何在 Ruby 中構建類似 REST 的 API
已發表: 2022-03-11作為 Ruby On Rails 開發人員,我們經常需要使用 API 端點擴展我們的應用程序,以支持大量 JavaScript 的富 Internet 客戶端或原生 iPhone 和 Android 應用程序。 在某些情況下,應用程序的唯一目的是通過 JSON API 為 iPhone/Android 應用程序提供服務。
在本教程中,我將演示如何使用 Grape(一種用於 Ruby 的類似 REST 的 API 微框架)在 Rails 中為 JSON API 構建後端支持。 Grape 旨在作為可安裝的機架引擎運行,以補充我們的 Web 應用程序,而不會干擾它們。
用例
我們將在本教程中關注的用例是一個能夠捕獲和查看結對編程會話的應用程序。 應用程序本身將使用 ObjectiveC 為 iOS 編寫,並且需要與後端服務通信以存儲和檢索數據。 我們在本教程中的重點是創建支持 JSON API 的強大且安全的後端服務。
API 將支持以下方法:
- 登錄系統
- 查詢結對編程會話評論
注意:除了提供查詢結對編程會話評論的能力之外,真正的 API 還需要提供一種工具來提交結對編程評論以包含在數據庫中。 由於通過 API 支持這一點超出了本教程的範圍,我們將簡單地假設數據庫已經填充了一組結對編程評論的示例。
關鍵技術要求包括:
- 每個 API 調用都必須返回有效的 JSON
- 每個失敗的 API 調用都必須記錄足夠的上下文和信息,以便隨後可重現,並在必要時進行調試
此外,由於我們的應用程序需要為外部客戶端提供服務,因此我們需要關注安全性。 為此:
- 每個請求都應僅限於我們跟踪的一小部分開發人員
- 所有請求(登錄/註冊除外)都需要經過身份驗證
測試驅動開發和 RSpec
我們將使用測試驅動開發 (TDD) 作為我們的軟件開發方法,以幫助確保我們 API 的確定性行為。
出於測試目的,我們將使用 RSpec,這是 RubyOnRails 社區中眾所周知的測試框架。 因此,我將在本文中提到“規範”而不是“測試”。
全面的測試方法包括“陽性”和“陰性”測試。 負面規範將指定,例如,如果某些參數丟失或不正確,API 端點的行為方式。 正面規範涵蓋了正確調用 API 的情況。
入門
讓我們為我們的後端 API 打下基礎。 首先,我們需要創建一個新的 Rails 應用程序:
rails new toptal_grape_blog
接下來,我們將通過將rspec-rails
添加到我們的 gemfile 中來安裝 RSpec:
group :development, :test do gem 'rspec-rails', '~> 3.2' end
然後從我們的命令行中,我們需要運行:
rails generate rspec:install
我們還可以將一些現有的開源軟件用於我們的測試框架。 具體來說:
- Devise - 基於 Warden 的靈活的 Rails 身份驗證解決方案
- factory_girl_rails - 為 factory_girl 提供 Rails 集成,這是一個用於將 Ruby 對象設置為測試數據的庫
第 1 步:將這些添加到我們的 gemfile 中:
... gem 'devise' ... group :development, :test do ... gem 'factory_girl_rails', '~> 4.5' ... end
第 2 步:生成用戶模型,初始化devise
gem,並將其添加到用戶模型中(這使用戶類能夠用於身份驗證)。
rails g model user rails generate devise:install rails generate devise user
第 3 步:在我們的rails_helper.rb
文件中包含factory_girl
語法方法,以便在我們的規範中使用用戶創建的縮寫版本:
RSpec.configure do |config| config.include FactoryGirl::Syntax::Methods
第 4 步:將葡萄 gem 添加到我們的 DSL 並安裝它:
gem 'grape' bundle
用戶登錄用例和規範
我們的後端需要支持基本的登錄功能。 讓我們為login_spec
創建框架,假設有效的登錄請求包含註冊的電子郵件地址和密碼對:
require 'rails_helper' describe '/api/login' do context 'negative tests' do context 'missing params' do context 'password' do end context 'email' do end end context 'invalid params' do context 'incorrect password' do end context 'with a non-existent login' do end end end context 'positive tests' do context 'valid params' do end end end
如果缺少任何一個參數,客戶端應收到 HTTP 返回狀態代碼 400(即錯誤請求),以及“缺少電子郵件”或“缺少密碼”的錯誤消息。
對於我們的測試,我們將創建一個有效用戶並將用戶的電子郵件和密碼設置為此測試套件的原始參數。 然後我們將通過省略密碼/電子郵件或覆蓋它來為每個特定規範自定義此參數哈希。
讓我們在規範的開頭創建用戶和參數哈希。 我們將把這段代碼放在描述塊之後:
describe '/api/login' do let(:email) { user.email } let(:password) { user.password } let!(:user) { create :user } let(:original_params) { { email: email, password: password } } let(:params) { original_params } ...
然後我們可以擴展我們的“缺少參數”/“密碼”上下文,如下所示:
let(:params) { original_params.except(:password) } it_behaves_like '400' it_behaves_like 'json result' it_behaves_like 'contains error msg', 'password is missing'
但是,我們可以使用與期望相同的共享示例,而不是在“電子郵件”和“密碼”上下文中重複期望。 為此,我們需要在rails_helper.rb
文件中取消註釋這一行:
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
然後我們需要將 3 個 RSpec 共享示例添加到spec/support/shared.rb
:
RSpec.shared_examples 'json result' do specify 'returns JSON' do api_call params expect { JSON.parse(response.body) }.not_to raise_error end end RSpec.shared_examples '400' do specify 'returns 400' do api_call params expect(response.status).to eq(400) end end RSpec.shared_examples 'contains error msg' do |msg| specify "error msg is #{msg}" do api_call params json = JSON.parse(response.body) expect(json['error_msg']).to eq(msg) end end
這些共享示例調用了api_call
方法,這使我們能夠在規範中只定義一次 API 端點(符合 DRY 原則)。 我們定義這個方法如下:
describe '/api/login' do ... def api_call *params post "/api/login", *params end ...
我們還需要為我們的用戶定制工廠:
FactoryGirl.define do factory :user do password "Passw0rd" password_confirmation { |u| u.password } sequence(:email) { |n| "test#{n}@example.com" } end end
最後,在運行我們的規範之前,我們需要運行我們的遷移:
rake db:migrate
但是請記住,此時規範仍然會失敗,因為我們還沒有實現我們的 API 端點。 那是下一個。
實現登錄 API 端點
首先,我們將為登錄 API ( app/api/login.rb
) 編寫一個空框架:
class Login < Grape::API format :json desc 'End-points for the Login' namespace :login do desc 'Login via email and password' params do requires :email, type: String, desc: 'email' requires :password, type: String, desc: 'password' end post do end end end
接下來,我們將編寫一個聚合器類來聚合 API 端點( app/api/api.rb
):
class API < Grape::API prefix 'api' mount Login end
好的,現在我們可以在路由中掛載我們的 API:
Rails.application.routes.draw do ... mount API => '/' ... end
現在讓我們添加代碼來檢查缺少的參數。 我們可以通過從Grape::Exceptions::ValidationErrors
中救援來將該代碼添加到api.rb
中。
rescue_from Grape::Exceptions::ValidationErrors do |e| rack_response({ status: e.status, error_msg: e.message, }.to_json, 400) end
對於無效密碼,我們會檢查http響應碼是否為401,表示未經授權的訪問。 讓我們將其添加到“不正確的密碼”上下文中:
let(:params) { original_params.merge(password: 'invalid') } it_behaves_like '401' it_behaves_like 'json result' it_behaves_like 'contains error msg', 'Bad Authentication Parameters'
然後將相同的邏輯添加到“不存在登錄”上下文中。
然後,我們在login.rb
中實現處理無效身份驗證嘗試的邏輯,如下所示:
post do user = User.find_by_email params[:email] if user.present? && user.valid_password?(params[:password]) else error_msg = 'Bad Authentication Parameters' error!({ 'error_msg' => error_msg }, 401) end end
此時登錄 api 的所有負面規範都會正常運行,但我們仍然需要支持登錄 api 的正面規範。 我們的正面規範將期望端點返回一個帶有有效 JSON 和有效令牌的 HTTP 響應代碼 200(即成功):
it_behaves_like '200' it_behaves_like 'json result' specify 'returns the token as part of the response' do api_call params expect(JSON.parse(response.body)['token']).to be_present end
讓我們還將響應代碼 200 的期望添加到spec/support/shared.rb
:
RSpec.shared_examples '200' do specify 'returns 200' do api_call params expect(response.status).to eq(200) end end
如果登錄成功,我們將返回第一個有效的 authentication_token 以及用戶的電子郵件,格式如下:
{'email':<the_email_of_the_user>, 'token':<the users first valid token>}
如果還沒有這樣的令牌,那麼我們將為當前用戶創建一個:
... if user.present? && user.valid_password?(params[:password]) token = user.authentication_tokens.valid.first || AuthenticationToken.generate(user) status 200 else ...
為了讓它工作,我們需要一個屬於用戶的AuthenticationToken
類。 我們將生成這個模型,然後運行相應的遷移:
rails g model authentication_token token user:references expires_at:datetime rake db:migrate
我們還需要在我們的用戶模型中添加相應的關聯:
class User < ActiveRecord::Base has_many :authentication_tokens end
然後我們將有效範圍添加到AuthenticationToken
類:
class AuthenticationToken < ActiveRecord::Base belongs_to :user validates :token, presence: true scope :valid, -> { where{ (expires_at == nil) | (expires_at > Time.zone.now) } } end
請注意,我們在where
語句中使用了 Ruby 語法。 這是通過我們使用squeel
gem 實現的,它支持在 activerecord 查詢中支持 Ruby 語法。
對於經過驗證的用戶,我們將創建一個實體,我們將其稱為“具有令牌實體的用戶”,利用grape-entity
gem 的特性。
讓我們為我們的實體編寫規範並將其放入user_with_token_entity_spec.rb
文件中:
require 'rails_helper' describe Entities::UserWithTokenEntity do describe 'fields' do subject(:subject) { Entities::UserWithTokenEntity } specify { expect(subject).to represent(:email)} let!(:token) { create :authentication_token } specify 'presents the first available token' do json = Entities::UserWithTokenEntity.new(token.user).as_json expect(json[:token]).to be_present end end end
接下來讓我們將實體添加到user_entity.rb
:
module Entities class UserEntity < Grape::Entity expose :email end end
最後,向user_with_token_entity.rb
添加另一個類:
module Entities class UserWithTokenEntity < UserEntity expose :token do |user, options| user.authentication_tokens.valid.first.token end end end
由於我們不希望令牌無限期地保持有效,我們讓它們在一天后過期:
FactoryGirl.define do factory :authentication_token do token "MyString" expires_at 1.day.from_now user end end
完成這一切後,我們現在可以使用我們新編寫的UserWithTokenEntity
返回預期的 JSON 格式:
... user = User.find_by_email params[:email] if user.present? && user.valid_password?(params[:password]) token = user.authentication_tokens.valid.first || AuthenticationToken.generate(user) status 200 present token.user, with: Entities::UserWithTokenEntity else ...
涼爽的。 現在我們所有的規範都通過了,基本登錄 api 端點的功能要求都得到了支持。
結對編程會話審查 API 端點:入門
我們的後端將需要允許已登錄的授權開發人員查詢結對編程會話評論。
我們的新 API 端點將掛載到/api/pair_programming_session
並將返回屬於項目的評論。 讓我們首先為這個規範編寫一個基本框架:
require 'rails_helper' describe '/api' do describe '/pair_programming_session' do def api_call *params get '/api/pair_programming_sessions', *params end context 'invalid params' do end context 'valid params' do end end end
我們還將編寫一個相應的空 API 端點( app/api/pair_programming_sessions.rb
):
class PairProgrammingSessions < Grape::API format :json desc 'End-points for the PairProgrammingSessions' namespace :pair_programming_sessions do desc 'Retrieve the pairprogramming sessions' params do requires :token, type: String, desc: 'email' end get do end end end
然後讓我們掛載我們的新 api ( app/api/api.rb
):
... mount Login mount PairProgrammingSessions end
讓我們根據需求一一擴展規範和 API 端點。
結對編程會話審查 API 端點:驗證
我們最重要的非功能性安全要求之一是將 API 訪問限制為我們跟踪的一小部分開發人員,因此讓我們指定:
... def api_call *params get '/api/pair_programming_sessions', *params end let(:token) { create :authentication_token } let(:original_params) { { token: token.token} } let(:params) { original_params } it_behaves_like 'restricted for developers' context 'invalid params' do ...
然後我們將在我們的shared.rb
中創建一個shared_example
來確認請求來自我們的一位註冊開發者:
RSpec.shared_examples 'restricted for developers' do context 'without developer key' do specify 'should be an unauthorized call' do api_call params expect(response.status).to eq(401) end specify 'error code is 1001' do api_call params json = JSON.parse(response.body) expect(json['error_code']).to eq(ErrorCodes::DEVELOPER_KEY_MISSING) end end end
我們還需要創建一個ErrorCodes
類(在app/models/error_codes.rb
中):

module ErrorCodes DEVELOPER_KEY_MISSING = 1001 end
由於我們希望我們的 API 在未來擴展,我們將實現一個authorization_helper
可以在應用程序中的所有 API 端點重用,以限制僅註冊開發人員的訪問:
class PairProgrammingSessions < Grape::API helpers ApiHelpers::AuthenticationHelper before { restrict_access_to_developers }
我們將在ApiHelpers::AuthenticationHerlper
模塊 ( app/api/api_helpers/authentication_helper.rb
) 中定義方法restrict_access_to_developers
。 此方法將簡單地檢查標題下的密鑰Authorization
是否包含有效的ApiKey
。 (每個想要訪問 API 的開發人員都需要一個有效的ApiKey
。這可以由系統管理員提供,也可以通過一些自動註冊過程提供,但該機制超出了本文的範圍。)
module ApiHelpers module AuthenticationHelper def restrict_access_to_developers header_token = headers['Authorization'] key = ApiKey.where{ token == my{ header_token } } Rails.logger.info "API call: #{headers}\tWith params: #{params.inspect}" if ENV['DEBUG'] if key.blank? error_code = ErrorCodes::DEVELOPER_KEY_MISSING error_msg = 'please aquire a developer key' error!({ :error_msg => error_msg, :error_code => error_code }, 401) # LogAudit.new({env:env}).execute end end end end
然後我們需要生成 ApiKey 模型並運行遷移: rails g model api_key token rake db:migrate
完成後,在我們的spec/api/pair_programming_spec.rb
,我們可以檢查用戶是否已通過身份驗證:
... it_behaves_like 'restricted for developers' it_behaves_like 'unauthenticated' ...
讓我們還定義一個可以在所有規範中重用的未經身份unauthenticated
的共享示例( spec/support/shared.rb
):
RSpec.shared_examples 'unauthenticated' do context 'unauthenticated' do specify 'returns 401 without token' do api_call params.except(:token), developer_header expect(response.status).to eq(401) end specify 'returns JSON' do api_call params.except(:token), developer_header json = JSON.parse(response.body) end end end
這個共享示例需要開發者標頭中的令牌,所以讓我們將其添加到我們的規範( spec/api/pair_programming_spec.rb
)中:
... describe '/api' do let(:api_key) { create :api_key } let(:developer_header) { {'Authorization' => api_key.token} } ...
現在,在我們的app/api/pair_programming_session.rb
中,讓我們嘗試對用戶進行身份驗證:
... class PairProgrammingSessions < Grape::API helpers ApiHelpers::AuthenticationHelper before { restrict_access_to_developers } before { authenticate! } ...
讓我們實現authenticate!
AuthenticationHelper
( app/api/api_helpers/authentication_helper.rb
) 中的方法:
... module ApiHelpers module AuthenticationHelper TOKEN_PARAM_NAME = :token def token_value_from_request(token_param = TOKEN_PARAM_NAME) params[token_param] end def current_user token = AuthenticationToken.find_by_token(token_value_from_request) return nil unless token.present? @current_user ||= token.user end def signed_in? !!current_user end def authenticate! unless signed_in? AuditLog.create data: 'unauthenticated user access' error!({ :error_msg => "authentication_error", :error_code => ErrorCodes::BAD_AUTHENTICATION_PARAMS }, 401) end end ...
(請注意,我們需要將錯誤代碼BAD_AUTHENTICATION_PARAMS
添加到我們的ErrorCodes
類中。)
接下來,讓我們來看看如果開發人員使用無效令牌調用 API 會發生什麼。 在這種情況下,返回代碼將是 401,表示“未經授權的訪問”。 結果應該是 JSON 並且應該創建一個可審計的。 所以我們將它添加到spec/api/pair_programming_spec.rb
:
... context 'invalid params' do context 'incorrect token' do let(:params) { original_params.merge(token: 'invalid') } it_behaves_like '401' it_behaves_like 'json result' it_behaves_like 'auditable created' it_behaves_like 'contains error msg', 'authentication_error' it_behaves_like 'contains error code', ErrorCodes::BAD_AUTHENTICATION_PARAMS end end ...
我們將在spec/support/shared.rb
中添加“auditable created”、“contains error code”和“contains error msg”共享示例:
... RSpec.shared_examples 'contains error code' do |code| specify "error code is #{code}" do api_call params, developer_header json = JSON.parse(response.body) expect(json['error_code']).to eq(code) end end RSpec.shared_examples 'contains error msg' do |msg| specify "error msg is #{msg}" do api_call params, developer_header json = JSON.parse(response.body) expect(json['error_msg']).to eq(msg) end end RSpec.shared_examples 'auditable created' do specify 'creates an api call audit' do expect do api_call params, developer_header end.to change{ AuditLog.count }.by(1) end end ...
我們還需要創建一個 audit_log 模型:
rails g model audit_log backtrace data user:references rake db:migrate
結對編程會話審查 API 端點:返回結果
對於經過身份驗證和授權的用戶,對此 API 端點的調用應返回一組按項目分組的結對編程會話評論。 讓我們相應地修改我們的spec/api/pair_programming_spec.rb
:
... context 'valid params' do it_behaves_like '200' it_behaves_like 'json result' end ...
這指定使用有效api_key
和有效參數提交的請求返回 HTTP 代碼 200(即成功),並且結果以有效 JSON 的形式返回。
我們將查詢並以 JSON 格式返回任何參與者都是當前用戶的結對編程會話 ( app/api/pair_programming_sessions.rb
):
... get do sessions = PairProgrammingSession.where{(host_user == my{current_user}) | (visitor_user == my{current_user})} sessions = sessions.includes(:project, :host_user, :visitor_user, reviews: [:code_samples, :user] ) present sessions, with: Entities::PairProgrammingSessionsEntity end ...
結對編程會話在數據庫中建模如下:
- 項目和結對編程會話之間的一對多關係
- 結對編程會話和評論之間的一對多關係
- 評論和代碼示例之間的一對多關係
讓我們相應地生成模型,然後運行遷移:
rails g model project name rails g model pair_programming_session project:references host_user:references visitor_user:references rails g model review pair_programming_session:references user:references comment rails g model code_sample review:references code:text rake db:migrate
然後我們需要修改我們的PairProgrammingSession
和Review
類以包含has_many
關聯:
class Review < ActiveRecord::Base belongs_to :pair_programming_session belongs_to :user has_many :code_samples end class PairProgrammingSession < ActiveRecord::Base belongs_to :project belongs_to :host_user, class_name: :User belongs_to :visitor_user, class_name: 'User' has_many :reviews end
注意:在正常情況下,我會先為它們編寫規範來生成這些類,但由於這超出了本文的範圍,我將跳過這一步。
現在我們需要編寫那些將我們的模型轉換為它們的 JSON 表示的類(在葡萄術語中稱為葡萄實體)。 為簡單起見,我們將在模型和葡萄實體之間使用一對一的映射。
我們首先公開CodeSampleEntity
中的code
字段(在api/entities/code_sample_entity.rb
):
module Entities class CodeSampleEntity < Grape::Entity expose :code end end
然後我們通過重用已經定義的UserEntity
和code_samples
來公開user
和相關的CodeSampleEntity
:
module Entities class ReviewEntity < Grape::Entity expose :user, using: UserEntity expose :code_samples, using: CodeSampleEntity end end
我們還公開了ProjectEntity
的name
字段:
module Entities class ProjectEntity < Grape::Entity expose :name end end
最後,我們將實體組裝成一個新的PairProgrammingSessionsEntity
,我們在其中公開project
、 host_user
、 visitor_user
和reviews
:
module Entities class PairProgrammingSessionsEntity < Grape::Entity expose :project, using: ProjectEntity expose :host_user, using: UserEntity expose :visitor_user, using: UserEntity expose :reviews, using: ReviewEntity end end
這樣,我們的 API 就完全實現了!
生成測試數據
出於測試目的,我們將在db/seeds.rb
中創建一些示例數據。 該文件應包含使用其默認值為數據庫播種所需的所有記錄創建。 然後可以使用rake db:seed
加載數據(或在調用db:setup
時使用 db 創建)。 這是一個可能包括的示例:
user_1 = User.create email: '[email protected]', password: 'password', password_confirmation: 'password' user_2 = User.create email: '[email protected]', password: 'password', password_confirmation: 'password' user_3 = User.create email: '[email protected]', password: 'password', password_confirmation: 'password' ApiKey.create token: '12345654321' project_1 = Project.create name: 'Time Sheets' project_2 = Project.create name: 'Toptal Blog' project_3 = Project.create name: 'Hobby Project' session_1 = PairProgrammingSession.create project: project_1, host_user: user_1, visitor_user: user_2 session_2 = PairProgrammingSession.create project: project_2, host_user: user_1, visitor_user: user_3 session_3 = PairProgrammingSession.create project: project_3, host_user: user_2, visitor_user: user_3 review_1 = session_1.reviews.create user: user_1, comment: 'Please DRY a bit your code' review_2 = session_1.reviews.create user: user_1, comment: 'Please DRY a bit your specs' review_3 = session_2.reviews.create user: user_1, comment: 'Please DRY your view templates' review_4 = session_2.reviews.create user: user_1, comment: 'Please clean your N+1 queries' review_1.code_samples.create code: 'Lorem Ipsum' review_1.code_samples.create code: 'Do not abuse the single responsibility principle' review_2.code_samples.create code: 'Use some shared examples' review_2.code_samples.create code: 'Use at the beginning of specs'
現在我們的應用程序已經可以使用了,我們可以啟動我們的 Rails 服務器了。
測試 API
我們將使用 Swagger 對我們的 API 進行一些基於瀏覽器的手動測試。 我們需要一些設置步驟才能使用 Swagger。
首先,我們需要在 gemfile 中添加幾個 gem:
... gem 'grape-swagger' gem 'grape-swagger-ui' ...
然後我們運行bundle
來安裝這些 gem。
我們還需要將這些添加到資產到我們的資產管道中(在config/initializers/assets.rb
):
Rails.application.config.assets.precompile += %w( swagger_ui.js ) Rails.application.config.assets.precompile += %w( swagger_ui.css )
最後,在app/api/api.rb
我們需要掛載 swagger 生成器:
... add_swagger_documentation end ...
現在我們可以通過訪問http://localhost:3000/api/swagger
來利用 Swagger 的漂亮 UI 來探索我們的 API。
Swagger 以一種很好探索的方式展示了我們的 API 端點。 如果我們點擊一個端點,Swagger 會列出它的操作。 如果我們單擊一個操作,Swagger 會顯示其必需和可選參數及其數據類型。
在我們繼續之前,還有一個細節:由於我們使用有效的api_key
限制了 API 開發人員的使用,我們將無法直接從瀏覽器訪問 API 端點,因為服務器將需要 HTTP 標頭中的有效api_key
。 我們可以通過使用 Modify Headers for Google Chrome 插件在 Google Chrome 中完成此操作以進行測試。 這個插件將使我們能夠編輯 HTTP 標頭並添加一個有效的api_key
(我們將使用我們的數據庫種子文件中包含的api_key
的虛擬12345654321
)。
好的,現在我們準備好測試了!
為了調用pair_programming_sessions
API 端點,我們首先需要登錄。我們將使用數據庫種子文件中的電子郵件和密碼組合,並通過 Swagger 將其提交到登錄 API 端點,如下所示。
正如您在上面看到的,屬於該用戶的令牌被返回,表明登錄 API 正在按預期正常工作。 我們現在可以使用該令牌成功執行GET /api/pair_programming_sessions.json
操作。
如圖所示,結果作為格式正確的分層 JSON 對象返回。 請注意,JSON 結構反映了兩個嵌套的一對多關聯,因為項目有多個評論,而一個評論有多個代碼示例。 如果我們不以這種方式返回結構,那麼我們 API 的調用者將需要單獨請求每個項目的評論,這需要向我們的 API 端點提交 N 個查詢。 通過這種結構,我們因此解決了 N+1 查詢性能問題。
包起來
如本文所示,您的 API 的全面規範有助於確保實施的 API 正確且充分地解決預期(和非預期!)用例。
雖然本教程中介紹的示例 API 相當基礎,但我們展示的方法和技術可以作為使用 Grape gem 的任意複雜度的更複雜 API 的基礎。 本教程希望證明 Grape 是一個有用且靈活的 gem,可以幫助您在 Rails 應用程序中實現 JSON API。 享受!