什么是Grape
Grape是Ruby中的一個類REST API框架,被設計用於運行在Rack上或彌補已有的web應用框架(比如Rails或者Sinatra),Grape提供了一個簡單的DSL用於方便的開發RESTful APIs。Grape支持common conventions,包括多種格式,子域/前綴限制,內容協商,版本控制等。
安裝
通過gem安裝:
gem install grape
基本用法
你的應用一般要繼承自Grape::API,下面的例子暫時了Grape中常用的一些特性:
#config.ru 內容
require "grape"
module Twitter
class API < Grape::API
version 'v1', using: :header, vendor: 'twitter'
format :json
prefix :api
helpers do
def current_user
@current_user ||= User.authorize!(env)
end
def authenticate!
error!('401 Unauthorized', 401) unless current_user
end
end
resource :statuses do
desc 'Return a public timeline.'
get :public_timeline do
Status.limit(20)
end
desc 'Return a personal timeline.'
get :home_timeline do
authenticate!
current_user.statuses.limit(20)
end
desc 'Return a status.'
params do
requires :id, type: Integer, desc: 'Status id.'
end
route_param :id do
get do
Status.find(params[:id])
end
end
desc 'Create a status.'
params do
requires :status, type: String, desc: 'Your status.'
end
post do
authenticate!
Status.create!({
user: current_user,
text: params[:status]
})
end
desc 'Update a status.'
params do
requires :id, type: String, desc: 'Status ID.'
requires :status, type: String, desc: 'Your status.'
end
put ':id' do
authenticate!
current_user.statuses.find(params[:id]).update({
user: current_user,
text: params[:status]
})
end
desc 'Delete a status.'
params do
requires :id, type: String, desc: 'Status ID.'
end
delete ':id' do
authenticate!
current_user.statuses.find(params[:id]).destroy
end
end
end
end
run Twitter::API
安裝
Rack
上面的例子創建了一個簡單的Rack應用,將上面的內容放在config.ru文件中,通過如下命令啟動:
rackup -o 0.0.0.0
在我的環境中直接執行rackup之后,在其他機器上無法訪問
現在可以訪問如下路徑:
GET /api/statuses/public_timeline
GET /api/statuses/home_timeline
GET /api/statuses/:id
POST /api/statuses
PUT /api/statuses/:id
DELETE /api/statuses/:id
所有的GET選項,Grape也會自動應答HEAD/OPTIONS選項,但是其他路徑,只會應答OPTIONS選項。
脫離Rails使用ActiveRecord
如果你想在Grape中使用ActiveRecord,你要保證ActiveRecord的連接池處理正確。保證這一點的最簡單的方式是在安裝你的Grape應用前使用ActiveRecord的ConnectionManagement中間件:
use ActiveRecord::ConnectionAdapters::ConnectionManagement
run Twitter::API
結合Sinatra(或者其他框架)
如果使用Grape的同時想結合使用其他Rack框架,比如Sinatra,你可以很簡單的使用Rack::Cascade:
# Example config.ru
require 'sinatra'
require 'grape'
class API < Grape::API
get :hello do
{ hello: 'world' }
end
end
class Web < Sinatra::Base
get '/' do
'Hello world.'
end
end
use Rack::Session::Cookie
run Rack::Cascade.new [API, Web]
Rails
把所有的API文件都放置到app/api路徑下,Rails期望一個子文件夾能夠匹配一個Ruby模塊,一個文件名能夠匹配一個類名。在我們的例子中,Twitter::API的路徑和文件名應該是app/api/twitter/api.rb。
修改application.rb:
config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]
修改config/routes:
mount Twitter::API => '/'
另外,如果使用的Rails是4.0+,並且使用了ActiveRecord默認的model層,你可能會想使用 hashie-forbidden_attributes gem包。該gem去使能model層strong_paras的安全特性,允許你使用Grape自己的參數校驗。
# Gemfile
gem 'hashie-forbidden_attributes'
Modules
在一個應用內部你可以mount多個其他應用的API實現,它們不需要是不同的版本,但是可能是同一接口的組件:
class Twitter::API < Grape::API
mount Twitter::APIv1
mount Twitter::APIv2
end
你也可以掛在到一個路徑上,就好像在被mount的API內部使用了prefix一樣:
class Twitter::API < Grape::API
mount Twitter::APIv1 => '/v1'
end
版本控制
版本信息可以通過四種方式提供:
:path、:header、:accept_version_header、:param
默認的方式是:path。
path
version 'v1', using: :path
通過這種方式,URL中必須提供版本信息:
curl http://localhost:9292/v1/statuses/public_timeline
Header
version 'v1', using: :header, vendor: 'twitter'
現在,Grape僅支持如下格式的版本控制信息:
vnd.vendor-and-or-resource-v1234+format
最后的-和+之間的所有信息都會被解釋為版本信息(上面的版本信息就是v1234)。
通過這種方式,用戶必須通過HTTP的Accept頭傳遞版本信息:
curl -H Accept:application/vnd.twitter-v1+json http://localhost:9292/statuses/public_timeline
默認的,如果沒有提供Accept頭信息,將會使用第一個匹配的版本,這種行為和Rails中的路徑相似。為了規避這個默認的行為,可以使用:strict選項,該選項被設置為true時,如果沒有提供正確的Accept頭信息,會返回一個406錯誤。
當提供了一個無效的Accept頭時,如果:cascade選項設置為false,會返回一個406錯誤。否則,如果沒有其他的路徑匹配,Rack會返回一個404錯誤。
Accept-Version Header
version 'v1', using: :accept_version_header
使用該方式時,用戶必須在HTTP頭中提供Accept-Version信息:
curl -H "Accept-Version:v1" http://localhost:9292/statuses/public_timeline
默認的,如果沒有提供Accept頭信息,將會使用第一個匹配的版本,這種行為和Rails中的路徑相似。為了規避這個默認的行為,可以使用:strict選項,該選項被設置為true時,如果沒有提供正確的Accept頭信息,會返回一個406錯誤。
當提供了一個無效的Accept頭時,如果:cascade選項設置為false,會返回一個406錯誤。否則,如果沒有其他的路徑匹配,Rack會返回一個404錯誤。
Param
version 'v1', using: :param
使用這種方式,用於必須在URL字符串或者請求的body中提供版本信息:
curl http://localhost:9292/statuses/public_timeline?apiver=v1
參數名稱默認是“apiver”,你也可以通過:parameter 選項指定:
version 'v1', using: :param, parameter: 'v'
curl http://localhost:9292/statuses/public_timeline?v=v1
描述方法
你可以為API方法和名稱空間添加一個描述:
desc 'Returns your public timeline.' do
detail 'more details'
params API::Entities::Status.documentation
success API::Entities::Entity
failure [[401, 'Unauthorized', 'Entities::Error']]
named 'My named route'
headers XAuthToken: {
description: 'Valdates your identity',
required: true
},
XOptionalHeader: {
description: 'Not really needed',
required: false
}
end
get :public_timeline do
Status.limit(20)
end
- detail
更詳細的描述。 - params
直接從一個Entity定義參數。 - success: (former entity) The Entity to be used to present by default this route
- failure: (former http_codes) A definition of the used failure HTTP Codes and Entities
- named: A helper to give a route a name and find it with this name in the documentation Hash
- headers: A definition of the used Headers