

포도는 Ruby의 휴식과 같은 API 프레임 워크입니다. Restful API를 쉽게 개발할 수있는 간단한 DSL을 제공하여 Rails에서 실행되거나 Rails 및 Sinatra와 같은 기존 웹 응용 프로그램 프레임 워크를 보완하도록 설계되었습니다. 여러 형식, 하위 도메인/접두사 제한, 컨텐츠 협상, 버전화 등을 포함하여 일반적인 규칙에 대한 내장 지원이 있습니다.
포도의 다음 릴리스에 대한 문서를 읽고 있는데, 이는 2.3.0이어야합니다. 현재 안정적인 릴리스는 2.2.0입니다.
TideLift 구독의 일부로 제공됩니다.
포도 관리자는 TideLift와 협력하여 상업적 지원 및 유지 보수를 제공하고 있습니다. 포도 관리자에게 비용을 지불하면서 시간을 절약하고 위험을 줄이며 코드 건강을 향상시킵니다. 자세한 내용은 여기를 클릭하십시오.
루비 2.7 또는 새로운 것이 필요합니다.
포도는 보석으로 제공되어 실행을 설치할 수 있습니다.
bundle add grape
포도 API는 Grape::API 에 의해 생성되는 랙 애플리케이션입니다. 아래는 트위터 API의 일부를 재현하는 맥락에서 포도의 일반적인 특징 중 일부를 보여주는 간단한 예입니다.
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 포도의 감가 상각기는 애플리케이션의 감가 상각기에 다음과 같이 자동으로 추가됩니다 :grape 의 구성을 적용 할 수 있습니다.
기본적으로 포도는 첫 번째 경로에서 경로를 컴파일하면 compile! 방법.
Twitter :: API . compile! config.ru (Rackup을 사용하는 경우), application.rb (레일을 사용하는 경우) 또는 서버를로드하는 파일에 추가 할 수 있습니다.
위의 샘플은 rackup 있는 Rackup config.ru 파일에서 실행할 수있는 랙 응용 프로그램을 만듭니다.
run Twitter :: API(사전로드를 사용하면 사용할 수 있습니다)
Twitter :: API . compile!
run Twitter :: API다음 경로에 응답 할 것입니다.
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에 대한 머리와 옵션에 자동으로 응답하며 다른 모든 경로에 대한 옵션 만 있습니다.
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 [ Web , API ] Rack::Cascade 사용하여 앱을로드하는 순서가 중요합니다. 포도에서 포도 적용은 포도에서 사용자 정의 404 오류를 올리려면 마지막이어야합니다 (예 : error!('Not Found',404) ). 포도 응용 프로그램이 마지막이 아니고 404 또는 405 응답을 반환하는 경우 Cascade는 다음 앱을 시도하기위한 신호로이를 사용합니다. 이것은 잘못된 앱에서 잘못된 404 페이지를 보여주는 바람직하지 않은 행동으로 이어질 수 있습니다.
API 파일을 app/api 에 배치하십시오. Rails는 Ruby 모듈의 이름과 클래스 이름과 일치하는 파일 이름과 일치하는 하위 디렉토리를 기대합니다. 이 예에서는 Twitter::API 의 파일 이름 위치 및 디렉토리는 app/api/twitter/api.rb 여야합니다.
config/routes 수정 :
mount Twitter :: API => '/' Rails의 기본 자동 로더는 Zeitwerk 입니다. 기본적으로 API 대신 Api 로 api 전달합니다. 예제를 작동 시키려면 config/initializers/inflections.rb 하단의 줄을 무너 뜨리고 API 약어로 추가해야합니다.
ActiveSupport :: Inflector . inflections ( :en ) do | inflect |
inflect . acronym 'API'
end여러 API 구현을 다른 API 구현 내에 마운트 할 수 있습니다. 이것들은 다른 버전 일 필요는 없지만 동일한 API의 구성 요소 일 수 있습니다.
class Twitter :: API < Grape :: API
mount Twitter :: APIv1
mount Twitter :: APIv2
end 마운트 API 자체 내에서 prefix 사용하는 것과 유사한 경로에 장착 할 수도 있습니다.
class Twitter :: API < Grape :: API
mount Twitter :: APIv1 => '/v1'
end before/after/rescue_from 의 선언은 mount 전후에 배치 할 수 있습니다. 어쨌든 그들은 상속받을 것입니다.
class Twitter :: API < Grape :: API
before do
header 'X-Base-Header' , 'will be defined for all APIs that are mounted below'
end
rescue_from :all do
error! ( { "error" => "Internal Server Error" } , 500 )
end
mount Twitter :: Users
mount Twitter :: Search
after do
clean_cache!
end
rescue_from ZeroDivisionError do
error! ( { "error" => "Not found" } , 404 )
end
end 두 개의 다른 위치에 동일한 엔드 포인트를 장착 할 수 있습니다.
class Voting :: API < Grape :: API
namespace 'votes' do
get do
# Your logic
end
post do
# Your logic
end
end
end
class Post :: API < Grape :: API
mount Voting :: API
end
class Comment :: API < Grape :: API
mount Voting :: API
end 게시물 및 댓글 엔드 포인트가 /posts 및 /comments 에 장착되어 있다고 가정하면 이제 get /posts/votes , post /posts/votes , get /comments/votes 및 post /comments/votes 수행 할 수 있어야합니다.
리 마운트 가능한 엔드 포인트를 구성하여 장착 위치에 따라 행동 방식을 변경할 수 있습니다.
class Voting :: API < Grape :: API
namespace 'votes' do
desc "Vote for your #{ configuration [ :votable ] } "
get do
# Your logic
end
end
end
class Post :: API < Grape :: API
mount Voting :: API , with : { votable : 'posts' }
end
class Comment :: API < Grape :: API
mount Voting :: API , with : { votable : 'comments' }
end mount 의 첫 번째 매개 변수로 해시를 전달하는 경우 매개 변수 주위에 명시 적으로 () 를 넣어야합니다.
# good
mount ( { :: Some :: Api => '/some/api' } , with : { condition : true } )
# bad
mount :: Some :: Api => '/some/api' , with : { condition : true } 클래스에서 configuration 에 액세스 할 수 있습니다 (동적 속성으로 사용하려면) 내부 블록 (예 : 네임 스페이스)
configuration 에 주어진 논리가 발생하려면 given 도우미를 사용할 수 있습니다.
class ConditionalEndpoint :: API < Grape :: API
given configuration [ :some_setting ] do
get 'mount_this_endpoint_conditionally' do
configuration [ :configurable_response ]
end
end
end 엔드 포인트가 장착 될 때마다 로직 블록을 실행하려면 ( configuration 해시에 액세스 할 수 있음)
class ConditionalEndpoint :: API < Grape :: API
mounted do
YourLogger . info "This API was mounted at: #{ Time . now } "
get configuration [ :endpoint_name ] do
configuration [ :configurable_response ]
end
end
end configuration 이미 해시로 평가되는 표현식으로 mounted 하여 더 복잡한 결과를 달성 할 수 있습니다.
class ExpressionEndpointAPI < Grape :: API
get ( mounted { configuration [ :route_name ] || 'default_name' } ) do
# some logic
end
end class BasicAPI < Grape :: API
desc 'Statuses index' do
params : ( configuration [ :entity ] || API :: Entities :: Status ) . documentation
end
params do
requires :all , using : ( configuration [ :entity ] || API :: Entities :: Status ) . documentation
end
get '/statuses' do
statuses = Status . all
type = current_user . admin? ? :full : :default
present statuses , with : ( configuration [ :entity ] || API :: Entities :: Status ) , type : type
end
end
class V1 < Grape :: API
version 'v1'
mount BasicAPI , with : { entity : mounted { configuration [ :entity ] || API :: Entities :: Status } }
end
class V2 < Grape :: API
version 'v2'
mount BasicAPI , with : { entity : mounted { configuration [ :entity ] || API :: Entities :: V2 :: Status } }
end 제공되는 각 버전에 대해 별도의 포도 Grape::API 클래스를 설정 한 다음 1 차 Grape::API 클래스에 통합하여 다양한 버전의 API를 제공 할 수 있습니다. 최신 버전이 이전 버전에 장착되어 있는지 확인하십시오. 버전 설정에 대한 기본 접근 방식은 특정 버전을 찾을 수없는 경우 요청을 후속 랙 미들웨어로 지시합니다.
require 'v1'
require 'v2'
require 'v3'
class App < Grape :: API
mount V3
mount V2
mount V1
end이전 API 버전을 다시 작성하지 않고 동일한 엔드 포인트를 유지하려면 이전 API 버전 내에서 여러 버전을 표시 할 수 있습니다.
class V1 < Grape :: API
version 'v1' , 'v2' , 'v3'
get '/foo' do
# your code for GET /foo
end
get '/other' do
# your code for GET /other
end
end
class V2 < Grape :: API
version 'v2' , 'v3'
get '/var' do
# your code for GET /var
end
end
class V3 < Grape :: API
version 'v3'
get '/foo' do
# your new code for GET /foo
end
end제공된 예제를 사용하여 후속 엔드 포인트는 다양한 버전에서 액세스 할 수 있습니다.
GET /v1/foo
GET /v1/other
GET /v2/foo # => Same behavior as v1
GET /v2/other # => Same behavior as v1
GET /v2/var # => New endpoint not available in v1
GET /v3/foo # => Different behavior to v1 and v2
GET /v3/other # => Same behavior as v1 and v2
GET /v3/var # => Same behavior as v2 클라이언트가 API의 엔드 포인트 :header 도달 할 수 :accept_version_header 4 가지 전략이 :param :path 기본 전략은 다음과 같습니다 :path .
version 'v1' , using : :path이 버전 작성 전략을 사용하여 클라이언트는 원하는 버전을 URL에서 전달해야합니다.
curl http://localhost:9292/v1/statuses/public_timeline
version 'v1' , using : :header , vendor : 'twitter'현재 Grape는 다음 형식의 버전 미디어 유형 만 지원합니다.
vnd.vendor-and-or-resource-v1234+format
기본적으로 - 및 + 간의 모든 토큰은 버전으로 해석됩니다.
이 버전 작성 전략을 사용하여 클라이언트는 HTTP Accept 헤드에서 원하는 버전을 전달해야합니다.
curl -H Accept:application/vnd.twitter-v1+json http://localhost:9292/statuses/public_timeline
기본적으로 첫 번째 일치 버전은 Accept 헤더가 제공되지 않을 때 사용됩니다. 이 동작은 레일의 라우팅과 유사합니다. 이 기본 동작을 우회하기 위해 :strict 옵션을 사용할 수 있습니다. 이 옵션이 true 로 설정되면 올바른 Accept 헤더가 제공되지 않으면 406 Not Acceptable 오류가 반환됩니다.
유효하지 않은 Accept 헤더가 제공되면 :cascade 옵션이 false 로 설정된 경우 406 Not Acceptable 오류가 반환됩니다. 그렇지 않으면 다른 경로가 일치하지 않으면 404 Not Found 오류가 랙으로 반환됩니다.
포도는 수락 헤더에 포함 된 상대적 품질 선호도를 평가하고 생략하면 1.0의 품질로 기본값을 평가합니다. 다음 예에서는 XML과 JSON을 지원하는 포도 API가 순서대로 JSON을 반환합니다.
curl -H "Accept: text/xml;q=0.8, application/json;q=0.9" localhost:1234/resource
version 'v1' , using : :accept_version_header 이 버전 작성 전략을 사용하여 클라이언트는 HTTP Accept-Version 헤더에서 원하는 버전을 전달해야합니다.
curl -H "Accept-Version:v1" http://localhost:9292/statuses/public_timeline
기본적으로 첫 번째 일치 버전은 Accept-Version 헤더가 제공되지 않을 때 사용됩니다. 이 동작은 레일의 라우팅과 유사합니다. 이 기본 동작을 우회하기 위해 :strict 옵션을 사용할 수 있습니다. 이 옵션이 true 로 설정되면 올바른 Accept 헤더가 제공되지 않으면 406 Not Acceptable 오류가 반환되고 :cascade 옵션이 false 로 설정됩니다. 그렇지 않으면 다른 경로가 일치하지 않으면 404 Not Found 오류가 랙으로 반환됩니다.
version 'v1' , using : :param이 버전 작성 전략을 사용하여 클라이언트는 원하는 버전을 URL 쿼리 문자열 또는 요청 본문에서 요청 매개 변수로 전달해야합니다.
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 메소드 및 네임 스페이스에 설명을 추가 할 수 있습니다. 이 설명은 포도-윙거가 Swagger 준수 문서를 생성하는 데 사용됩니다.
참고 : 설명 블록은 문서에만 해당되며 API 동작에 영향을 미치지 않습니다.
desc 'Returns your public timeline.' do
summary 'summary'
detail 'more details'
params API :: Entities :: Status . documentation
success API :: Entities :: Entity
failure [ [ 401 , 'Unauthorized' , 'Entities::Error' ] ]
default { code : 500 , message : 'InvalidRequest' , model : Entities :: Error }
named 'My named route'
headers XAuthToken : {
description : 'Validates your identity' ,
required : true
} ,
XOptionalHeader : {
description : 'Not really needed' ,
required : false
}
hidden false
deprecated false
is_array true
nickname 'nickname'
produces [ 'application/json' ]
consumes [ 'application/json' ]
tags [ 'tag1' , 'tag2' ]
end
get :public_timeline do
Status . limit ( 20 )
enddetail :보다 강화 된 설명params : Entity 에서 직접 매개 변수를 정의하십시오success : (이전 엔터티)이 경로에 대한 성공 대응을 제시하는 데 사용되는 Entity .failure : (이전 http_codes) 중고 실패 HTTP 코드 및 엔티티의 정의.default :이 경로의 기본 응답을 제시하는 데 사용되는 정의 및 Entity .named : 라우트를 이름을 지정하고 문서 에서이 이름으로 찾아 보는 도우미 해시headers : 중고 헤더의 정의 Grape.configure 사용하여로드 타임에 글로벌 설정을 설정하십시오. 현재 구성 가능한 설정은 다음과 같습니다.
param_builder : 매개 변수 빌더를 설정합니다. 기본값은 Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder 로 설정합니다.설정 값을 변경하려면로드 시간 동안 어느 시점에서 다음 코드가 실행되도록하십시오.
Grape . configure do | config |
config . setting = value
end 예를 들어, param_builder 의 경우 다음 코드가 이니셜 라이저에서 실행될 수 있습니다.
Grape . configure do | config |
config . param_builder = Grape :: Extensions :: Hashie :: Mash :: ParamBuilder
end단일 API를 구성 할 수도 있습니다.
API . configure do | config |
config [ key ] = value
end 마운트 구성 인 것처럼 configuration API 내부에서 사용할 수 있습니다.
요청 매개 변수는 params HASH 객체를 통해 사용할 수 있습니다. 여기에는 GET , POST 및 PUT 매개 변수와 함께 경로 문자열에 지정할 수있는 이름의 매개 변수가 포함됩니다.
get :public_timeline do
Status . order ( params [ :sort_by ] )
end 매개 변수는 POST 요청 본문에서 자동으로 채워져 있으며 양식 입력, JSON 및 XML 컨텐츠 유형을 위해 PUT .
요청 :
curl -d '{"text": "140 characters"}' 'http://localhost:9292/statuses' -H Content-Type:application/json -v
포도 종말점 :
post '/statuses' do
Status . create! ( text : params [ :text ] )
end멀티 파트 게시물 및 풋도 지원됩니다.
요청 :
curl --form image_file='@image.jpg;type=image/jpg' http://localhost:9292/upload
포도 종말점 :
post 'upload' do
# file in params[:image_file]
end중 하나 간의 충돌의 경우 :
GET POST 하고 PUTPOST 에 요청 본문의 내용 PUT경로 문자열 매개 변수가 우선합니다.
기본적으로 매개 변수는 ActiveSupport::HashWithIndifferentAccess 로 사용할 수 있습니다. 예를 들어 전체 API의 Ruby Hash 또는 Hashie::Mash 로 변경할 수 있습니다.
class API < Grape :: API
include Grape :: Extensions :: Hashie :: Mash :: ParamBuilder
params do
optional :color , type : String
end
get do
params . color # instead of params[:color]
end 클래스는 다음과 같이 build_with 사용하여 개별 매개 변수 블록에서 재정의 할 수 있습니다.
params do
build_with Grape :: Extensions :: Hash :: ParamBuilder
optional :color , type : String
end 또는 구성 Grape.configure.param_builder 와 함께 전 세계적으로.
위의 예에서, params["color"] params 일반 Hash 이기 때문에 nil 반환합니다.
사용 가능한 매개 변수 빌더는 Grape::Extensions::Hash::ParamBuilder , Grape::Extensions::ActiveSupport::HashWithIndifferentAccess::ParamBuilder and Grape::Extensions::Hashie::Mash::ParamBuilder .
포도를 사용하면 params 블록에서 선언 된 매개 변수 만 액세스 할 수 있습니다. 그것은 :
다음 API 엔드 포인트를 고려하십시오.
format :json
post 'users/signup' do
{ 'declared_params' => declared ( params ) }
end 매개 변수를 지정하지 않으면 declared 경우 빈 해시를 반환합니다.
요구
curl -X POST -H " Content-Type: application/json " localhost:9292/users/signup -d ' {"user": {"first_name":"first name", "last_name": "last name"}} '응답
{
"declared_params" : {}
}
매개 변수 요구 사항을 추가하면 포도가 선언 된 매개 변수 만 반환하기 시작합니다.
format :json
params do
optional :user , type : Hash do
optional :first_name , type : String
optional :last_name , type : String
end
end
post 'users/signup' do
{ 'declared_params' => declared ( params ) }
end요구
curl -X POST -H " Content-Type: application/json " localhost:9292/users/signup -d ' {"user": {"first_name":"first name", "last_name": "last name", "random": "never shown"}} '응답
{
"declared_params" : {
"user" : {
"first_name" : " first name " ,
"last_name" : " last name "
}
}
} 유형 Hash 또는 Array 로 선언 된 누락 된 매개 변수가 포함됩니다.
format :json
params do
optional :user , type : Hash do
optional :first_name , type : String
optional :last_name , type : String
end
optional :widgets , type : Array
end
post 'users/signup' do
{ 'declared_params' => declared ( params ) }
end요구
curl -X POST -H " Content-Type: application/json " localhost:9292/users/signup -d ' {} '응답
{
"declared_params" : {
"user" : {
"first_name" : null ,
"last_name" : null
},
"widgets" : []
}
} 반환 된 해시는 ActiveSupport::HashWithIndifferentAccess 입니다.
#declared 방법은 파라미터 강요 전에 평가되므로 필터 before 에 사용할 수 없습니다.
기본적으로 declared(params) 에는 모든 상위 네임 스페이스에서 정의 된 매개 변수가 포함되어 있습니다. 현재 네임 스페이스에서 매개 변수 만 반환하려면 include_parent_namespaces 옵션을 false 로 설정할 수 있습니다.
format :json
namespace :parent do
params do
requires :parent_name , type : String
end
namespace ':parent_name' do
params do
requires :child_name , type : String
end
get ':child_name' do
{
'without_parent_namespaces' => declared ( params , include_parent_namespaces : false ) ,
'with_parent_namespaces' => declared ( params , include_parent_namespaces : true ) ,
}
end
end
end요구
curl -X GET -H " Content-Type: application/json " localhost:9292/parent/foo/bar응답
{
"without_parent_namespaces" : {
"child_name" : " bar "
},
"with_parent_namespaces" : {
"parent_name" : " foo " ,
"child_name" : " bar "
},
} 기본적으로 declared(params) 에는 nil 값이있는 매개 변수가 포함됩니다. nil 아닌 매개 변수 만 반환하려면 include_missing 옵션을 사용할 수 있습니다. 기본적으로 include_missing 이 true 로 설정됩니다. 다음 API를 고려하십시오.
format :json
params do
requires :user , type : Hash do
requires :first_name , type : String
optional :last_name , type : String
end
end
post 'users/signup' do
{ 'declared_params' => declared ( params , include_missing : false ) }
end요구
curl -X POST -H " Content-Type: application/json " localhost:9292/users/signup -d ' {"user": {"first_name":"first name", "random": "never shown"}} 'include_missing에 대한 응답 : false
{
"declared_params" : {
"user" : {
"first_name" : " first name "
}
}
}include_missing : true의 응답
{
"declared_params" : {
"user" : {
"first_name" : " first name " ,
"last_name" : null
}
}
}중첩 된 해시에서도 작동합니다.
format :json
params do
requires :user , type : Hash do
requires :first_name , type : String
optional :last_name , type : String
requires :address , type : Hash do
requires :city , type : String
optional :region , type : String
end
end
end
post 'users/signup' do
{ 'declared_params' => declared ( params , include_missing : false ) }
end요구
curl -X POST -H " Content-Type: application/json " localhost:9292/users/signup -d ' {"user": {"first_name":"first name", "random": "never shown", "address": { "city": "SF"}}} 'include_missing에 대한 응답 : false
{
"declared_params" : {
"user" : {
"first_name" : " first name " ,
"address" : {
"city" : " SF "
}
}
}
}include_missing : true의 응답
{
"declared_params" : {
"user" : {
"first_name" : " first name " ,
"last_name" : null ,
"address" : {
"city" : " Zurich " ,
"region" : null
}
}
}
} nil 값이있는 속성은 누락 된 것으로 간주되지 않으며 포함 include_missing false 로 설정되면 반환됩니다.
요구
curl -X POST -H " Content-Type: application/json " localhost:9292/users/signup -d ' {"user": {"first_name":"first name", "last_name": null, "address": { "city": "SF"}}} 'include_missing에 대한 응답 : false
{
"declared_params" : {
"user" : {
"first_name" : " first name " ,
"last_name" : null ,
"address" : { "city" : " SF " }
}
}
} 기본적으로 declared(params) given 모든 매개 변수를 평가하지 않고 반환하지 않습니다. evaluate_given 사용하여 given 모든 블록을 평가하고 given 조건을 만족하는 매개 변수 만 반환하십시오. 다음 API를 고려하십시오.
format :json
params do
optional :child_id , type : Integer
given :child_id do
requires :father_id , type : Integer
end
end
post 'child' do
{ 'declared_params' => declared ( params , evaluate_given : true ) }
end요구
curl -X POST -H " Content-Type: application/json " localhost:9292/child -d ' {"father_id": 1} 'Evaluate_given : False를 사용한 응답
{
"declared_params" : {
"child_id" : null ,
"father_id" : 1
}
}Evaluate_given : True에 대한 응답
{
"declared_params" : {
"child_id" : null
}
}중첩 된 해시에서도 작동합니다.
format :json
params do
requires :child , type : Hash do
optional :child_id , type : Integer
given :child_id do
requires :father_id , type : Integer
end
end
end
post 'child' do
{ 'declared_params' => declared ( params , evaluate_given : true ) }
end요구
curl -X POST -H " Content-Type: application/json " localhost:9292/child -d ' {"child": {"father_id": 1}} 'Evaluate_given : False를 사용한 응답
{
"declared_params" : {
"child" : {
"child_id" : null ,
"father_id" : 1
}
}
}Evaluate_given : True에 대한 응답
{
"declared_params" : {
"child" : {
"child_id" : null
}
}
} route_param 사용은 동일한 이름으로 정의 된 일반 매개 변수보다 더 높은 우선 순위를 갖습니다.
params do
requires :foo , type : String
end
route_param :foo do
get do
{ value : params [ :foo ] }
end
end요구
curl -X POST -H " Content-Type: application/json " localhost:9292/bar -d ' {"foo": "baz"} '응답
{
"value" : " bar "
} params 블록을 사용하여 매개 변수의 유효성 검사 및 강요 옵션을 정의 할 수 있습니다.
params do
requires :id , type : Integer
optional :text , type : String , regexp : / A [a-z]+ z /
group :media , type : Hash do
requires :url
end
optional :audio , type : Hash do
requires :format , type : Symbol , values : [ :mp3 , :wav , :aac , :ogg ] , default : :mp3
end
mutually_exclusive :media , :audio
end
put ':id' do
# params[:id] is an Integer
end유형이 지정되면 출력 유형이 선언 된 것인지 확인하기 위해 강요 후 암시 적 검증이 수행됩니다.
선택적 매개 변수는 기본값을 가질 수 있습니다.
params do
optional :color , type : String , default : 'blue'
optional :random_number , type : Integer , default : -> { Random . rand ( 1 .. 100 ) }
optional :non_random_number , type : Integer , default : Random . rand ( 1 .. 100 )
end 기본값이 간절히 평가됩니다. 위 :non_random_number 이 params 블록의 종말점에 대한 각 호출에 대해 동일한 숫자로 평가됩니다. 각 요청에 따라 기본값을 게으르게 평가하려면 다음과 같은 :random_number 를 사용하십시오.
기본값은 지정된 모든 유효성 검사 옵션으로 전달됩니다. 다음 예제는 항상 실패합니다 :color 명시 적으로 제공되지 않습니다.
params do
optional :color , type : String , default : 'blue' , values : [ 'red' , 'green' ]
end올바른 구현은 기본값이 모든 유효성 검사를 통과하도록하는 것입니다.
params do
optional :color , type : String , default : 'blue' , values : [ 'blue' , 'red' , 'green' ]
end 하나의 매개 변수 값을 다른 매개 변수의 기본값으로 사용할 수 있습니다. 이 경우 primary_color 매개 변수가 제공되지 않으면 color 값과 동일한 값을 갖습니다. 둘 다 제공되지 않으면 둘 다 blue 값을 가질 것입니다.
params do
optional :color , type : String , default : 'blue'
optional :primary_color , type : String , default : -> ( params ) { params [ :color ] }
end다음은 포도로 상자에서 지원되는 모든 유효한 유형입니다.
File )Ruby 2.4와 이전 버전간에 동작이 다릅니다. Ruby 2.4에서는 숫자로 구성된 값이 정수로 변환되지만 이전 버전에서는 Fixnum으로 취급됩니다.
params do
requires :integers , type : Hash do
requires :int , coerce : Integer
end
end
get '/int' do
params [ :integers ] [ :int ] . class
end
. ..
get '/int' integers : { int : '45' }
#=> Integer in ruby 2.4
#=> Fixnum in earlier ruby versions 위에 나열된 지원 유형의 기본 세트 외에도 명시 적 강요 방법이 제공되는 한 모든 클래스는 유형으로 사용할 수 있습니다. 유형이 클래스 수준 parse 방법을 구현하면 포도가 자동으로 사용됩니다. 이 방법은 하나의 문자열 인수를 가져 와서 올바른 유형의 인스턴스를 반환하거나 Grape::Types::InvalidValue 의 인스턴스를 반환해야합니다.
class Color
attr_reader :value
def initialize ( color )
@value = color
end
def self . parse ( value )
return new ( value ) if %w[ blue red green ] . include? ( value )
Grape :: Types :: InvalidValue . new ( 'Unsupported color' )
end
end
params do
requires :color , type : Color , default : Color . new ( 'blue' )
requires :more_colors , type : Array [ Color ] # Collections work
optional :unique_colors , type : Set [ Color ] # Duplicates discarded
end
get '/stuff' do
# params[:color] is already a Color.
params [ :color ] . value
end 또는 coerce_with 사용하여 임의의 유형의 매개 변수에 맞춤 강요 방법이 제공 될 수 있습니다. 그 우선 순서대로 구문 parse 또는 call 방법을 구현하는 클래스 또는 객체가 주어질 수 있습니다. 메소드는 단일 문자열 매개 변수를 수용해야하며 반환 값은 주어진 type 과 일치해야합니다.
params do
requires :passwd , type : String , coerce_with : Base64 . method ( :decode64 )
requires :loud_color , type : Color , coerce_with : -> ( c ) { Color . parse ( c . downcase ) }
requires :obj , type : Hash , coerce_with : JSON do
requires :words , type : Array [ String ] , coerce_with : -> ( val ) { val . split ( / s +/ ) }
optional :time , type : Time , coerce_with : Chronic
end
end nil 값은 사용자 정의 강요 메소드를 호출하지만 누락 된 매개 변수는 그렇지 않습니다.
람다 (구문 parse 방법이있는 클래스도 사용될 수 있음)와 함께 coerce_with 와 함께 사용의 예는 문자열을 구문 분석하고 Array[Integer] type 과 일치하는 정수 배열을 반환합니다.
params do
requires :values , type : Array [ Integer ] , coerce_with : -> ( val ) { val . split ( / s +/ ) . map ( & :to_i ) }
end 포도는 강제 값이 주어진 type 일치한다고 주장하며, 그렇지 않은 경우 요청을 거부 할 것입니다. 이 동작을 무시하기 위해 사용자 정의 유형이 parsed? 단일 인수를 수락하고 값이 유형 유효성 검증을 전달하는 경우 true 를 반환 해야하는 방법.
class SecureUri
def self . parse ( value )
URI . parse value
end
def self . parsed? ( value )
value . is_a? URI :: HTTPS
end
end
params do
requires :secure_uri , type : SecureUri
end 포도는 Rack::Request 의 Multipart 파일 매개 변수에 대한 내장 지원을 사용합니다. 이러한 매개 변수는 type: File 로 선언 할 수 있습니다.
params do
requires :avatar , type : File
end
post '/' do
params [ :avatar ] [ :filename ] # => 'avatar.png'
params [ :avatar ] [ :type ] # => 'image/png'
params [ :avatar ] [ :tempfile ] # => #<File>
endJSON 유형 포도는 특수 type: JSON 선언을 사용하여 JSON 형식 스트링으로 제공되는 복잡한 매개 변수를 지원합니다. JSON 객체 및 객체 배열은 동일하게 허용되며, 중첩 유효성 검사 규칙은 다음과 같이 모든 객체에 적용됩니다.
params do
requires :json , type : JSON do
requires :int , type : Integer , values : [ 1 , 2 , 3 ]
end
end
get '/' do
params [ :json ] . inspect
end
client . get ( '/' , json : '{"int":1}' ) # => "{:int=>1}"
client . get ( '/' , json : '[{"int":"1"}]' ) # => "[{:int=>1}]"
client . get ( '/' , json : '{"int":4}' ) # => HTTP 400
client . get ( '/' , json : '[{"int":4}]' ) # => HTTP 400 또한 type: Array[JSON] 사용하여 매개 변수를 객체 배열로 명시 적으로 표시합니다. 단일 객체가 제공되면 포장됩니다.
params do
requires :json , type : Array [ JSON ] do
requires :int , type : Integer
end
end
get '/' do
params [ :json ] . each { | obj | ... } # always works
end 공급 될 수있는 JSON 구조의 유형에 대한 엄격한 제어를 위해 type: Array, coerce_with: JSON 또는 type: Hash, coerce_with: JSON .
변형 유형 매개 변수는 type 아닌 types 옵션을 사용하여 선언 할 수 있습니다.
params do
requires :status_code , types : [ Integer , String , Array [ Integer , String ] ]
end
get '/' do
params [ :status_code ] . inspect
end
client . get ( '/' , status_code : 'OK_GOOD' ) # => "OK_GOOD"
client . get ( '/' , status_code : 300 ) # => 300
client . get ( '/' , status_code : %w( 404 NOT FOUND ) ) # => [404, "NOT", "FOUND"] 특별한 경우, 변형 멤버 유형 컬렉션은 여러 멤버가있는 Set 또는 Array 전달하여 다음을 type 할 수 있습니다.
params do
requires :status_codes , type : Array [ Integer , String ]
end
get '/' do
params [ :status_codes ] . inspect
end
client . get ( '/' , status_codes : %w( 1 two ) ) # => [1, "two"] 매개 변수는 group 사용하여 중첩하거나 블록으로 requires 또는 optional 호출하여 중첩 될 수 있습니다. 위의 예에서는 params[:id] 와 함께 params[:media][:url] 이 필요하다는 것을 의미합니다. params[:audio][:format] params[:audio] 있는 경우에만 필요합니다. 블록, group , requires 및 optional 사용하면 Array 또는 Hash 될 수있는 추가 옵션 type 및 Array 의 기본값을 허용합니다. 값에 따라 중첩 된 매개 변수는 해시의 값으로 또는 배열에서 해시 값으로 처리됩니다.
params do
optional :preferences , type : Array do
requires :key
requires :value
end
requires :name , type : Hash do
requires :first_name
requires :last_name
end
end 일부 매개 변수가 다른 매개 변수가 제공되는 경우에만 관련이 있다고 가정합니다. 포도는 다음과 같이 매개 변수 블록의 given 방법을 통해이 관계를 표현할 수 있습니다.
params do
optional :shelf_id , type : Integer
given :shelf_id do
requires :bin_id , type : Integer
end
end 위의 예에서 포도는 blank? shelf_id 매개 변수가 있는지 확인하십시오.
또한 사용자 정의 코드가 포함 된 Proc given 합니다. 아래에서는 category 값이 foo 경우에만 매개 변수 description 필요합니다.
params do
optional :category
given category : -> ( val ) { val == 'foo' } do
requires :description
end
end매개 변수 이름을 바꿀 수 있습니다.
params do
optional :category , as : :type
given type : -> ( val ) { val == 'foo' } do
requires :description
end
end 참고 : given Param은 이름이 바뀌어야합니다. 예에서는 type 아닌 category 이어야합니다.
매개 변수 옵션을 그룹화 할 수 있습니다. 여러 매개 변수에 대한 일반적인 검증 또는 유형을 추출하려는 경우 유용 할 수 있습니다. 이 그룹 내에서 개별 매개 변수는 공통 설정을 확장하거나 선택적으로 무시할 수 있으므로 필요한 경우 매개 변수 별 규칙을 적용하면서 그룹 수준에서 기본값을 유지할 수 있습니다.
아래의 예는 매개 변수가 공통 옵션을 공유하는 일반적인 사례를 나타냅니다.
params do
requires :first_name , type : String , regexp : /w+/ , desc : 'First name' , documentation : { in : 'body' }
optional :middle_name , type : String , regexp : /w+/ , desc : 'Middle name' , documentation : { in : 'body' , x : { nullable : true } }
requires :last_name , type : String , regexp : /w+/ , desc : 'Last name' , documentation : { in : 'body' }
end 포도는 매개 변수 블록의 with 를 통해 동일한 논리를 다음과 같이 제시 할 수 있습니다.
params do
with ( type : String , regexp : /w+/ , documentation : { in : 'body' } ) do
requires :first_name , desc : 'First name'
optional :middle_name , desc : 'Middle name' , documentation : { x : { nullable : true } }
requires :last_name , desc : 'Last name'
end
end중첩 된 '블록'을 사용하여 레이어로 설정을 구성 할 수 있습니다. 각 레이어는 그 위의 레이어 설정을 사용, 추가 또는 변경할 수 있습니다. 이를 통해 복잡한 매개 변수를 구성하고 일관성있게 유지하면서 특정 사용자 정의를 만들 수 있습니다.
params do
with ( documentation : { in : 'body' } ) do # Applies documentation to all nested parameters
with ( type : String , regexp : / w +/ ) do # Applies type and validation to names
requires :first_name , desc : 'First name'
requires :last_name , desc : 'Last name'
end
optional :age , type : Integer , desc : 'Age' , documentation : { x : { nullable : true } } # Specific settings for 'age'
end
end 기존 API를 리팩토링 할 때 유용 할 수있는 as 사용하여 매개 변수 이름을 바꿀 수 있습니다.
resource :users do
params do
requires :email_address , as : :email
requires :password
end
post do
User . create! ( declared ( params ) ) # User takes email and password
end
end declared(params) 호출 할 때 값이 핵심 as 전달됩니다.
allow_blank 매개 변수는 allow_blank 로 정의되어 값이 포함되어 있는지 확인할 수 있습니다. 기본적으로 값에 관계없이 요청에 매개 변수가 전송 되었음을 확인 requires . allow_blank: false , 빈 값 또는 공백 값만 유효하지 않습니다.
allow_blank requires 및 optional 과 결합 할 수 있습니다. 매개 변수가 필요한 경우 값을 포함해야합니다. 선택 사항이라면 요청에 보낼 수는 없지만 전송중인 경우 빈 문자열이 아닌 값이 없어야합니다.
params do
requires :username , allow_blank : false
optional :first_name , allow_blank : false
end values 매개 변수는 :values 옵션을 사용하여 특정 값 세트로 제한 될 수 있습니다.
params do
requires :status , type : Symbol , values : [ :not_started , :processing , :done ]
optional :numbers , type : Array [ Integer ] , default : 1 , values : [ 1 , 2 , 3 , 5 , 8 ]
end :values 옵션에 범위를 공급하면 매개 변수가 해당 범위에 포함 된 (또는 매개 변수)가 포함되도록합니다 ( Range#include? ).
params do
requires :latitude , type : Float , values : - 90.0 ..+ 90.0
requires :longitude , type : Float , values : - 180.0 ..+ 180.0
optional :letters , type : Array [ String ] , values : 'a' .. 'z'
end참고 끝없는 범위는 ActivesUpport> = 6.0으로 지원되지만 유형을 제공해야합니다.
params do
requires :minimum , type : Integer , values : 10 ..
optional :maximum , type : Integer , values : .. 10
end 두 범위 엔드 포인트 모두 #kind_of? 귀하의 :type 옵션 ( :type 옵션을 제공하지 않으면 범위의 첫 번째 엔드 포인트 클래스와 동일하도록 기부됩니다). 따라서 다음은 유효하지 않습니다.
params do
requires :invalid1 , type : Float , values : 0 .. 10 # 0.kind_of?(Float) => false
optional :invalid2 , values : 0 .. 10.0 # 10.0.kind_of?(0.class) => false
end :values 옵션은 또한 Proc 와 함께 제공 될 수 있으며 각 요청에 따라 게으르게 평가할 수 있습니다. Proc에 Arity Zero가있는 경우 (즉, 인수가 필요하지 않음) 목록 또는 범위를 반환하여 매개 변수를 검증하는 데 사용될 것으로 예상됩니다.
예를 들어, 상태 모델이 주어지면 이전에 해시 HashTag 모델에서 정의한 해시 태그로 제한 할 수 있습니다.
params do
requires :hashtag , type : String , values : -> { Hashtag . all . map ( & :tag ) }
end대안 적으로, Arity One을 가진 Proc (즉, 하나의 인수를 취하는)를 사용하여 각 매개 변수 값을 명시 적으로 검증 할 수 있습니다. 이 경우 Proc는 매개 변수 값이 유효 한 경우 Truthy 값을 반환 할 것으로 예상됩니다. Proc가 허위 가치를 반환하거나 표준 오류를 올리는 경우 매개 변수는 유효하지 않은 것으로 간주됩니다.
params do
requires :number , type : Integer , values : -> ( v ) { v . even? && v < 25 }
endProcs는 단일 사례에 편리하지만 검증이 두 번 이상 사용되는 경우 사용자 정의 유효성 검사기 사용을 고려하십시오.
allow_blank 유효성 검사기는 다음을 사용하는 동안 적용됩니다 :values 다음과 같습니다. 다음 예제에서 :allow_blank 는 :allow_blank true :state 방지하지 않습니다.
params do
requires :state , type : Symbol , values : [ :active , :inactive ]
end except_values 매개 변수는 :except_values 과 같은 특정 값 세트를 갖지 못하도록 제한 할 수 있습니다.
except_values Validator는 배열, 범위 또는 Proc를 수락한다는 점에서 values 유효성 검사기와 유사하게 동작합니다. 그러나 values Validator와 달리, except_values Arity Zero가있는 Procs 만 허용합니다.
params do
requires :browser , except_values : [ 'ie6' , 'ie7' , 'ie8' ]
requires :port , except_values : { value : 0 .. 1024 , message : 'is not allowed' }
requires :hashtag , except_values : -> { Hashtag . FORBIDDEN_LIST }
end same_as 매개 변수 값이 일치하도록하기 위해 same_as 옵션이 제공 될 수 있습니다.
params do
requires :password
requires :password_confirmation , same_as : :password
end length #length 메서드를 지원하는 유형의 매개 변수는 다음과 :length 옵션으로 특정 길이를 가질 수 있습니다.
유효성 검사기는 다음을 수락합니다 :min 또는 :max 또는 두 옵션 또는 전용 :is .
params do
requires :code , type : String , length : { is : 2 }
requires :str , type : String , length : { min : 3 }
requires :list , type : [ Integer ] , length : { min : 3 , max : 5 }
requires :hash , type : Hash , length : { max : 5 }
end regexp 매개 변수는 :regexp 옵션과 특정 정규 표현식과 일치하도록 제한 될 수 있습니다. 값이 정규식과 일치하지 않으면 오류가 반환됩니다. requires 사항과 optional 매개 변수 모두에 해당됩니다.
params do
requires :email , regexp : /.+@.+/
end 매개 변수에 값없이 전송 된 경우 유효성 검사기가 전달됩니다. 매개 변수에 값이 포함되도록하려면 allow_blank: false 사용하십시오.
params do
requires :email , allow_blank : false , regexp : /.+@.+/
end mutually_exclusive 매개 변수는 mutually_exclusive 로 정의하여 요청에 동시에 존재하지 않도록합니다.
params do
optional :beer
optional :wine
mutually_exclusive :beer , :wine
end여러 세트를 정의 할 수 있습니다.
params do
optional :beer
optional :wine
mutually_exclusive :beer , :wine
optional :scotch
optional :aquavit
mutually_exclusive :scotch , :aquavit
end경고 : 필요한 매개 변수로 상호 배타적 세트를 정의하지 마십시오. 두 개의 상호 배타적 인 필요한 매개 변수는 매개 변수가 결코 유효하지 않으므로 엔드 포인트를 쓸모가 없다는 것을 의미합니다. 옵션 매개 변수와 상호 배타적으로 필요한 하나의 필요한 매개 변수는 후자가 유효하지 않다는 것을 의미합니다.
exactly_one_of매개 변수는 '정확한 _one_of'로 정의 될 수 있으므로 정확히 하나의 매개 변수를 선택할 수 있습니다.
params do
optional :beer
optional :wine
exactly_one_of :beer , :wine
end 사용 :default mutually_exclusive 를 사용하면 여러 매개 변수가 항상 기본값을 갖고 Grape::Exceptions::Validation .
at_least_one_of매개 변수는 'at_least_one_of'로 정의 될 수 있으므로 하나 이상의 매개 변수가 선택되도록합니다.
params do
optional :beer
optional :wine
optional :juice
at_least_one_of :beer , :wine , :juice
end all_or_none_of매개 변수는 'all_or_none_of'로 정의 될 수 있으므로 매개 변수가 모두 선택되도록합니다.
params do
optional :beer
optional :wine
optional :juice
all_or_none_of :beer , :wine , :juice
end mutually_exclusive , exactly_one_of , at_least_one_of , all_or_none_of이 모든 방법은 모든 중첩 레벨에서 사용할 수 있습니다.
params do
requires :food , type : Hash do
optional :meat
optional :fish
optional :rice
at_least_one_of :meat , :fish , :rice
end
group :drink , type : Hash do
optional :beer
optional :wine
optional :juice
exactly_one_of :beer , :wine , :juice
end
optional :dessert , type : Hash do
optional :cake
optional :icecream
mutually_exclusive :cake , :icecream
end
optional :recipe , type : Hash do
optional :oil
optional :meat
all_or_none_of :oil , :meat
end
end네임 스페이스는 매개 변수 정의를 허용하고 네임 스페이스 내의 모든 메소드에 적용됩니다.
namespace :statuses do
params do
requires :user_id , type : Integer , desc : 'A user ID.'
end
namespace ':user_id' do
desc "Retrieve a user's status."
params do
requires :status_id , type : Integer , desc : 'A status ID.'
end
get ':status_id' do
User . find ( params [ :user_id ] ) . statuses . find ( params [ :status_id ] )
end
end
end namespace 방법에는 group , resource , resources 및 segment 포함한 여러 별칭이 있습니다. API에 가장 적합한 것을 사용하십시오.
route_param 사용하여 경로 매개 변수를 네임 스페이스로 편리하게 정의 할 수 있습니다.
namespace :statuses do
route_param :id do
desc 'Returns all replies for a status.'
get 'replies' do
Status . find ( params [ :id ] ) . replies
end
desc 'Returns a status.'
get do
Status . find ( params [ :id ] )
end
end
end route_param 의 옵션으로 전달하여 Route 매개 변수 유형을 정의 할 수도 있습니다.
namespace :arithmetic do
route_param :n , type : Integer do
desc 'Returns in power'
get 'power' do
params [ :n ] ** params [ :n ]
end
end
end class AlphaNumeric < Grape :: Validations :: Validators :: Base
def validate_param! ( attr_name , params )
unless params [ attr_name ] =~ / A [[:alnum:]]+ z /
raise Grape :: Exceptions :: Validation . new params : [ @scope . full_name ( attr_name ) ] , message : 'must consist of alpha-numeric characters'
end
end
end params do
requires :text , alpha_numeric : true
end매개 변수를 사용하는 사용자 정의 클래스를 만들 수도 있습니다.
class Length < Grape :: Validations :: Validators :: Base
def validate_param! ( attr_name , params )
unless params [ attr_name ] . length <= @option
raise Grape :: Exceptions :: Validation . new params : [ @scope . full_name ( attr_name ) ] , message : "must be at the most #{ @option } characters long"
end
end
end params do
requires :text , length : 140
end요청을 사용하여 속성을 검증하는 사용자 정의 유효성 검사를 만들 수도 있습니다. 예를 들어 관리자 만 사용할 수있는 매개 변수를 원한다면 다음을 수행 할 수 있습니다.
class Admin < Grape :: Validations :: Validators :: Base
def validate ( request )
# return if the param we are checking was not in request
# @attrs is a list containing the attribute we are currently validating
# in our sample case this method once will get called with
# @attrs being [:admin_field] and once with @attrs being [:admin_false_field]
return unless request . params . key? ( @attrs . first )
# check if admin flag is set to true
return unless @option
# check if user is admin or not
# as an example get a token from request and check if it's admin or not
raise Grape :: Exceptions :: Validation . new params : @attrs , message : 'Can not set admin-only field.' unless request . headers [ 'X-Access-Token' ] == 'admin'
end
end엔드 포인트 정의에서 다음과 같이 사용하십시오.
params do
optional :admin_field , type : String , admin : true
optional :non_admin_field , type : String
optional :admin_false_field , type : String , admin : false
end모든 유효성 검사에는 유효성 검사기의 자체 인스턴스가 있으므로 유효성 검사기가 상태를 가질 수 있습니다.
유효성 검사 및 강요 오류가 수집되고 유형 Grape::Exceptions::ValidationErrors 제기됩니다. 예외가 끝나면 400의 상태와 오류 메시지로 응답합니다. 유효성 검사 오류는 매개 변수 이름으로 그룹화되며 Grape::Exceptions::ValidationErrors#errors 통해 액세스 할 수 있습니다.
Grape::Exceptions::ValidationErrors 다음 예에서 "맥주, 와인은 상호 배타적입니다"와 같은 인간적으로 읽을 수있는 끈입니다.
params do
optional :beer
optional :wine
optional :juice
exactly_one_of :beer , :wine , :juice
end Grape::Exceptions::ValidationErrors 구출하고 사용자 정의 응답으로 응답하거나 개별 매개 변수와 해당 오류 메시지를 분리하는 JSON API에 대해 잘 형성 된 JSON으로 응답을 전환 할 수 있습니다. 다음 rescue_from 예제는 [{"params":["beer","wine"],"messages":["are mutually exclusive"]}] 생성합니다.
format :json
subject . rescue_from Grape :: Exceptions :: ValidationErrors do | e |
error! e , 400
end Grape::Exceptions::ValidationErrors#full_messages 유효성 검사 메시지를 배열로 반환합니다. Grape::Exceptions::ValidationErrors#message 메시지를 하나의 문자열로 결합합니다.
유효성 검사 메시지 배열로 응답하려면 Grape::Exceptions::ValidationErrors#full_messages .
format :json
subject . rescue_from Grape :: Exceptions :: ValidationErrors do | e |
error! ( { messages : e . full_messages } , 400 )
end 포도는 기본적으로 발견 된 모든 검증 및 강요 오류를 반환합니다. 특정 매개 변수가 유효하지 않은 경우 모든 후속 유효성 검사 검사를 건너 뛰려면 fail_fast: true 사용하십시오.
다음 예는 다음과 같은 경우를 확인하지 않습니다 :wine 와인이 발견되지 않는 한 :beer .
params do
required :beer , fail_fast : true
required :wine
end 빈 매개 변수의 결과는 단일 Grape::Exceptions::ValidationErrors 오류입니다.
마찬가지로, 다음과 같은 경우 정규 발현 테스트는 수행되지 않습니다 :blah 는 다음 예에서 비어 있습니다.
params do
required :blah , allow_blank : false , regexp : /blah/ , fail_fast : true
end포도는 매개 변수 관련 오류 메시지에 대해 i18N을 지원하지만 기본 로케일의 번역이 제공되지 않은 경우 영어로 떨어집니다. 메시지 키는 en.yml을 참조하십시오.
앱이 사용 가능한 로케일 만 시행하고 EN이 사용 가능한 로케일에 포함되지 않으면 포도는 영어로 돌아갈 수 없으며 오류 메시지의 번역 키를 반환합니다. 이 동작을 피하려면 기본 로케일에 대한 번역을 제공하거나 다음을 추가하십시오.
포도는 매개 변수 관련 및 코어 관련 오류 메시지에 대한 사용자 정의 검증 메시지를 지원합니다.
presence , allow_blank , values , regexp params do
requires :name , values : { value : 1 .. 10 , message : 'not in range from 1 to 10' } , allow_blank : { value : false , message : 'cannot be blank' } , regexp : { value : /^[a-z]+$/ , message : 'format is invalid' } , message : 'is required'
end same_as params do
requires :password
requires :password_confirmation , same_as : { value : :password , message : 'not match' }
end length params do
requires :code , type : String , length : { is : 2 , message : 'code is expected to be exactly 2 characters long' }
requires :str , type : String , length : { min : 5 , message : 'str is expected to be atleast 5 characters long' }
requires :list , type : [ Integer ] , length : { min : 2 , max : 3 , message : 'list is expected to have between 2 and 3 elements' }
end all_or_none_of params do
optional :beer
optional :wine
optional :juice
all_or_none_of :beer , :wine , :juice , message : "all params are required or none is required"
end mutually_exclusive params do
optional :beer
optional :wine
optional :juice
mutually_exclusive :beer , :wine , :juice , message : "are mutually exclusive cannot pass both params"
end exactly_one_of params do
optional :beer
optional :wine
optional :juice
exactly_one_of :beer , :wine , :juice , message : { exactly_one : "are missing, exactly one parameter is required" , mutual_exclusion : "are mutually exclusive, exactly one parameter is required" }
end at_least_one_of params do
optional :beer
optional :wine
optional :juice
at_least_one_of :beer , :wine , :juice , message : "are missing, please specify at least one param"
end Coerce params do
requires :int , type : { value : Integer , message : "type cast is invalid" }
end With Lambdas params do
requires :name , values : { value : -> { ( 1 .. 10 ) . to_a } , message : 'not in range from 1 to 10' }
end Pass symbols for i18n translations사용자 정의 유효성 검사 메시지에 대한 i18n 번역을 원한다면 기호를 전달할 수 있습니다.
params do
requires :name , message : :name_required
end # en.yml
en :
grape :
errors :
format : ! '%{attributes} %{message}'
messages :
name_required : 'must be present' 속성 이름을 무시할 수도 있습니다.
# en.yml
en :
grape :
errors :
format : ! '%{attributes} %{message}'
messages :
name_required : 'must be present'
attributes :
name : 'Oops! Name''죄송합니다! 이름이 있어야합니다 '
보간 %{option1}: %{value1} is incompatible with %{option2}: %{value2} 때문에 기본값에 대한 사용자 정의 메시지 옵션을 설정할 수 없습니다. incompatible_option_values
params do
requires :name , values : { value : -> { ( 1 .. 10 ) . to_a } , message : 'not in range from 1 to 10' } , default : 5
enddry-validation 또는 dry-schema 사용 위에서 설명한 params DSL의 대안으로 스키마 또는 dry-validation 계약을 사용하여 엔드 포인트의 매개 변수를 설명 할 수 있습니다. 응용 프로그램의 다른 일부에서 이미 위를 사용하는 경우 특히 유용 할 수 있습니다. 그렇지 않은 경우 Gemfile 에 dry-validation 또는 dry-schema 추가해야합니다.
그런 다음 이전에 정의 된 계약 또는 스키마로 contract 호출하십시오.
CreateOrdersSchema = Dry :: Schema . Params do
required ( :orders ) . array ( :hash ) do
required ( :name ) . filled ( :string )
optional ( :volume ) . maybe ( :integer , lt? : 9 )
end
end
# ...
contract CreateOrdersSchema또는 스키마 정의 구문을 사용하여 블록을 사용하여 :
contract do
required ( :orders ) . array ( :hash ) do
required ( :name ) . filled ( :string )
optional ( :volume ) . maybe ( :integer , lt? : 9 )
end
end 후자는 강요 스키마 ( Dry::Schema.Params )를 정의합니다. 전자의 접근법을 사용하는 경우 입력이 강요가 필요한지 여부를 결정하는 것은 귀하에게 달려 있습니다.
params 및 contract 선언은 동일한 API에서 함께 사용할 수 있으며 예를 들어 엔드 포인트에 대한 중첩 네임 스페이스의 다른 부분을 설명합니다.
요청 헤더는 headers 헬퍼 또는 env 에서 원래 양식으로 사용할 수 있습니다.
get do
error! ( 'Unauthorized' , 401 ) unless headers [ 'Secret-Password' ] == 'swordfish'
end get do
error! ( 'Unauthorized' , 401 ) unless env [ 'HTTP_SECRET_PASSWORD' ] == 'swordfish'
end 위의 예는 다음과 같이 요청되었을 수 있습니다.
curl -H " secret_PassWord: swordfish " ...헤더 이름은 귀하를 위해 정규화되었습니다.
header 헬퍼 이름은 Rack 3을 사용하는 경우 다운 secret-password 케밥 케이스로 강요됩니다.header 헬퍼 이름은 Rack <3을 사용하는 경우 Secret-PassWord 로 자본화 된 케밥 케이스로 강요됩니다.env 컬렉션에서 그들은 모든 대문자, 뱀 케이스에 나타나고 'http_'로 HTTP_SECRET_PASSWORD 로 접두사에 나타납니다.헤더 이름은 클라이언트가 전송하는 내용에 관계없이 RFC2616 섹션 4.2에 정의 된 HTTP 표준에 따라 정규화되었습니다.
API 내부에 header 있는 응답 헤더를 설정할 수 있습니다.
header 'X-Robots-Tag' , 'noindex' error! , 추가 헤더를 인수로 전달하십시오. 추가 헤더는 error! 부르다.
error! 'Unauthorized' , 401 , 'X-Error-Detail' => 'Invalid token.' 경로를 정의하려면 route 방법 또는 HTTP 동사의 속기를 사용할 수 있습니다. 다음으로 설정된 경로를 수락하는 경로를 정의하려면 :any . 결장으로 표시되는 경로의 일부는 경로 매개 변수로 해석됩니다.
route :get , 'status' do
end
# is the same as
get 'status' do
end
# is the same as
get :status do
end
# is NOT the same as
get ':status' do # this makes params[:status] available
end
# This will make both params[:status_id] and params[:id] available
get 'statuses/:status_id/reviews/:id' do
end 내부의 모든 경로를 접두사 한 네임 스페이스를 선언하려면 namespace 메소드를 사용하십시오. group , resource , resources 및 segment 이 방법의 별칭입니다. 내부의 모든 엔드 포인트는 부모 컨텍스트와 네임 스페이스 컨텍스트에서 수행 된 구성을 공유합니다.
route_param 메소드는 매개 변수 경로 세그먼트를 정의하는 편리한 방법입니다. 유형을 정의하면이 매개 변수에 대한 유효성 검사가 추가됩니다.
route_param :id , type : Integer do
get 'status' do
end
end
# is the same as
namespace ':id' do
params do
requires :id , type : Integer
end
get 'status' do
end
end선택적으로 네임 스페이스 또는 엔드 포인트에서 정규 표현식을 사용하여 명명 된 경로 매개 변수의 요구 사항을 정의 할 수 있습니다. 경로는 모든 요구 사항이 충족되는 경우에만 일치합니다.
get ':id' , requirements : { id : /[0-9]*/ } do
Status . find ( params [ :id ] )
end
namespace :outer , requirements : { id : /[0-9]*/ } do
get :id do
end
get ':id/edit' do
end
end 블록 또는 모듈 배열을 제공하여 엔드 포인트가 helpers Macro와 함께 사용할 수있는 도우미 방법을 정의 할 수 있습니다.
module StatusHelpers
def user_info ( user )
" #{ user } has statused #{ user . statuses } status(s)"
end
end
module HttpCodesHelpers
def unauthorized
401
end
end
class API < Grape :: API
# define helpers with a block
helpers do
def current_user
User . find ( params [ :user_id ] )
end
end
# or mix in an array of modules
helpers StatusHelpers , HttpCodesHelpers
before do
error! ( 'Access Denied' , unauthorized ) unless current_user
end
get 'info' do
# helpers available in your endpoint and filters
user_info ( current_user )
end
end helpers 사용하여 재사용 가능한 params 를 정의 할 수 있습니다.
class API < Grape :: API
helpers do
params :pagination do
optional :page , type : Integer
optional :per_page , type : Integer
end
end
desc 'Get collection'
params do
use :pagination # aliases: includes, use_scope
end
get do
Collection . page ( params [ :page ] ) . per ( params [ :per_page ] )
end
end 공유 도우미를 사용하여 재사용 가능한 params 정의 할 수도 있습니다.
module SharedParams
extend Grape :: API :: Helpers
params :period do
optional :start_date
optional :end_date
end
params :pagination do
optional :page , type : Integer
optional :per_page , type : Integer
end
end
class API < Grape :: API
helpers SharedParams
desc 'Get collection.'
params do
use :period , :pagination
end
get do
Collection
. from ( params [ :start_date ] )
. to ( params [ :end_date ] )
. page ( params [ :page ] )
. per ( params [ :per_page ] )
end
end 도우미는 기본값을 설정하는 데 도움이되는 블록을 지원합니다. 다음 API는 asc 또는 desc 순서로 id 또는 created_at 로 정렬 된 컬렉션을 반환 할 수 있습니다.
module SharedParams
extend Grape :: API :: Helpers
params :order do | options |
optional :order_by , type : Symbol , values : options [ :order_by ] , default : options [ :default_order_by ]
optional :order , type : Symbol , values : %i( asc desc ) , default : options [ :default_order ]
end
end
class API < Grape :: API
helpers SharedParams
desc 'Get a sorted collection.'
params do
use :order , order_by : %i( id created_at ) , default_order_by : :created_at , default_order : :asc
end
get do
Collection . send ( params [ :order ] , params [ :order_by ] )
end
end 엔드 포인트 내부에 경로를 생성하는 방법이 필요한 경우 포도 가운데 헬퍼 보석을 참조하십시오.
documentation 해시를 사용하여 추가 문서를 params 에 첨부 할 수 있습니다.
params do
optional :first_name , type : String , documentation : { example : 'Jim' }
requires :last_name , type : String , documentation : { example : 'Smith' }
end문서가 필요하지 않은 경우 (예 : 내부 API 인 경우) 문서를 비활성화 할 수 있습니다.
class API < Grape :: API
do_not_document!
# endpoints...
end이 경우 포도는 RAM에 영원히 유지되는 문서와 관련된 물체를 만들지 않습니다.
cookies 방법을 사용하여 쿠키를 설정, 받기 및 삭제할 수 있습니다.
class API < Grape :: API
get 'status_count' do
cookies [ :status_count ] ||= 0
cookies [ :status_count ] += 1
{ status_count : cookies [ :status_count ] }
end
delete 'status_count' do
{ status_count : cookies . delete ( :status_count ) }
end
end해시 기반 구문을 사용하여 둘 이상의 값을 설정하십시오.
cookies [ :status_count ] = {
value : 0 ,
expires : Time . tomorrow ,
domain : '.twitter.com' ,
path : '/'
}
cookies [ :status_count ] [ :value ] += 1 delete 로 쿠키를 삭제하십시오.
cookies . delete :status_count선택적 경로를 지정합니다.
cookies . delete :status_count , path : '/' 기본적으로 포도는 POST 레퍼스트의 경우 2010, 컨텐츠를 반환하지 않는 DELETE -요청의 경우 204, 다른 모든 요청에 대해서는 200 개의 상태 코드를 반환합니다. status 사용하여 실제 HTTP 상태 코드를 쿼리하고 설정할 수 있습니다.
post do
status 202
if status == 200
# do some thing
end
endYou can also use one of status codes symbols that are provided by Rack utils
post do
status :no_content
end You can redirect to a new url temporarily (302) or permanently (301).
redirect '/statuses' redirect '/statuses' , permanent : true You can recognize the endpoint matched with given path.
This API returns an instance of Grape::Endpoint .
class API < Grape :: API
get '/statuses' do
end
end
API . recognize_path '/statuses' Since version 2.1.0 , the recognize_path method takes into account the parameters type to determine which endpoint should match with given path.
class Books < Grape :: API
resource :books do
route_param :id , type : Integer do
# GET /books/:id
get do
#...
end
end
resource :share do
# POST /books/share
post do
# ....
end
end
end
end
API . recognize_path '/books/1' # => /books/:id
API . recognize_path '/books/share' # => /books/share
API . recognize_path '/books/other' # => nil When you add a GET route for a resource, a route for the HEAD method will also be added automatically. You can disable this behavior with do_not_route_head! .
class API < Grape :: API
do_not_route_head!
get '/example' do
# only responds to GET
end
end When you add a route for a resource, a route for the OPTIONS method will also be added. The response to an OPTIONS request will include an "Allow" header listing the supported methods. If the resource has before and after callbacks they will be executed, but no other callbacks will run.
class API < Grape :: API
get '/rt_count' do
{ rt_count : current_user . rt_count }
end
params do
requires :value , type : Integer , desc : 'Value to add to the rt count.'
end
put '/rt_count' do
current_user . rt_count += params [ :value ] . to_i
{ rt_count : current_user . rt_count }
end
end curl -v -X OPTIONS http://localhost:3000/rt_count
> OPTIONS /rt_count HTTP/1.1
>
< HTTP/1.1 204 No Content
< Allow: OPTIONS, GET, PUT You can disable this behavior with do_not_route_options! .
If a request for a resource is made with an unsupported HTTP method, an HTTP 405 (Method Not Allowed) response will be returned. If the resource has before callbacks they will be executed, but no other callbacks will run.
curl -X DELETE -v http://localhost:3000/rt_count/
> DELETE /rt_count/ HTTP/1.1
> Host: localhost:3000
>
< HTTP/1.1 405 Method Not Allowed
< Allow: OPTIONS, GET, PUT You can abort the execution of an API method by raising errors with error! .
error! 'Access Denied' , 401 Anything that responds to #to_s can be given as a first argument to error! .
error! :not_found , 404You can also return JSON formatted objects by raising error! and passing a hash instead of a message.
error! ( { error : 'unexpected error' , detail : 'missing widget' } , 500 ) You can set additional headers for the response. They will be merged with headers set before error! 부르다.
error! ( 'Something went wrong' , 500 , 'X-Error-Detail' => 'Invalid token.' )You can present documented errors with a Grape entity using the the grape-entity gem.
module API
class Error < Grape :: Entity
expose :code
expose :message
end
end The following example specifies the entity to use in the http_codes definition.
desc 'My Route' do
failure [ [ 408 , 'Unauthorized' , API :: Error ] ]
end
error! ( { message : 'Unauthorized' } , 408 )The following example specifies the presented entity explicitly in the error message.
desc 'My Route' do
failure [ [ 408 , 'Unauthorized' ] ]
end
error! ( { message : 'Unauthorized' , with : API :: Error } , 408 ) By default Grape returns a 500 status code from error! . You can change this with default_error_status .
class API < Grape :: API
default_error_status 400
get '/example' do
error! 'This should have http status code 400'
end
endFor Grape to handle all the 404s for your API, it can be useful to use a catch-all. In its simplest form, it can be like:
route :any , '*path' do
error! # or something else
endIt is very crucial to define this endpoint at the very end of your API , as it literally accepts every request.
Grape can be told to rescue all StandardError exceptions and return them in the API format.
class Twitter :: API < Grape :: API
rescue_from :all
end This mimics default rescue behaviour when an exception type is not provided. Any other exception should be rescued explicitly, see below.
Grape can also rescue from all exceptions and still use the built-in exception handing. This will give the same behavior as rescue_from :all with the addition that Grape will use the exception handling defined by all Exception classes that inherit Grape::Exceptions::Base .
The intent of this setting is to provide a simple way to cover the most common exceptions and return any unexpected exceptions in the API format.
class Twitter :: API < Grape :: API
rescue_from :grape_exceptions
end If you want to customize the shape of grape exceptions returned to the user, to match your :all handler for example, you can pass a block to rescue_from :grape_exceptions .
rescue_from :grape_exceptions do | e |
error! ( e , e . status )
endYou can also rescue specific exceptions.
class Twitter :: API < Grape :: API
rescue_from ArgumentError , UserDefinedError
end In this case UserDefinedError must be inherited from StandardError .
Notice that you could combine these two approaches (rescuing custom errors takes precedence). For example, it's useful for handling all exceptions except Grape validation errors.
class Twitter :: API < Grape :: API
rescue_from Grape :: Exceptions :: ValidationErrors do | e |
error! ( e , 400 )
end
rescue_from :all
endThe error format will match the request format. See "Content-Types" below.
Custom error formatters for existing and additional types can be defined with a proc.
class Twitter :: API < Grape :: API
error_formatter :txt , -> ( message , backtrace , options , env , original_exception ) {
"error: #{ message } from #{ backtrace } "
}
endYou can also use a module or class.
module CustomFormatter
def self . call ( message , backtrace , options , env , original_exception )
{ message : message , backtrace : backtrace }
end
end
class Twitter :: API < Grape :: API
error_formatter :custom , CustomFormatter
end You can rescue all exceptions with a code block. The error! wrapper automatically sets the default error code and content-type.
class Twitter :: API < Grape :: API
rescue_from :all do | e |
error! ( "rescued from #{ e . class . name } " )
end
endOptionally, you can set the format, status code and headers.
class Twitter :: API < Grape :: API
format :json
rescue_from :all do | e |
error! ( { error : 'Server error.' } , 500 , { 'Content-Type' => 'text/error' } )
end
endYou can also rescue all exceptions with a code block and handle the Rack response at the lowest level.
class Twitter :: API < Grape :: API
rescue_from :all do | e |
Rack :: Response . new ( [ e . message ] , 500 , { 'Content-type' => 'text/error' } )
end
endOr rescue specific exceptions.
class Twitter :: API < Grape :: API
rescue_from ArgumentError do | e |
error! ( "ArgumentError: #{ e . message } " )
end
rescue_from NoMethodError do | e |
error! ( "NoMethodError: #{ e . message } " )
end
end By default, rescue_from will rescue the exceptions listed and all their subclasses.
Assume you have the following exception classes defined.
module APIErrors
class ParentError < StandardError ; end
class ChildError < ParentError ; end
end Then the following rescue_from clause will rescue exceptions of type APIErrors::ParentError and its subclasses (in this case APIErrors::ChildError ).
rescue_from APIErrors :: ParentError do | e |
error! ( {
error : " #{ e . class } error" ,
message : e . message
} , e . status )
end To only rescue the base exception class, set rescue_subclasses: false . The code below will rescue exceptions of type RuntimeError but not its subclasses.
rescue_from RuntimeError , rescue_subclasses : false do | e |
error! ( {
status : e . status ,
message : e . message ,
errors : e . errors
} , e . status )
end Helpers are also available inside rescue_from .
class Twitter :: API < Grape :: API
format :json
helpers do
def server_error!
error! ( { error : 'Server error.' } , 500 , { 'Content-Type' => 'text/error' } )
end
end
rescue_from :all do | e |
server_error!
end
end The rescue_from handler must return a Rack::Response object, call error! , or raise an exception (either the original exception or another custom one). The exception raised in rescue_from will be handled outside Grape. For example, if you mount Grape in Rails, the exception will be handle by Rails Action Controller.
Alternately, use the with option in rescue_from to specify a method or a proc .
class Twitter :: API < Grape :: API
format :json
helpers do
def server_error!
error! ( { error : 'Server error.' } , 500 , { 'Content-Type' => 'text/error' } )
end
end
rescue_from :all , with : :server_error!
rescue_from ArgumentError , with : -> { Rack :: Response . new ( 'rescued with a method' , 400 ) }
end Inside the rescue_from block, the environment of the original controller method( .self receiver) is accessible through the #context method.
class Twitter :: API < Grape :: API
rescue_from :all do | e |
user_id = context . params [ :user_id ]
error! ( "error for #{ user_id } " )
end
end You could put rescue_from clauses inside a namespace and they will take precedence over ones defined in the root scope:
class Twitter :: API < Grape :: API
rescue_from ArgumentError do | e |
error! ( "outer" )
end
namespace :statuses do
rescue_from ArgumentError do | e |
error! ( "inner" )
end
get do
raise ArgumentError . new
end
end
end Here 'inner' will be result of handling occurred ArgumentError .
Grape::Exceptions::InvalidVersionHeader , which is raised when the version in the request header doesn't match the currently evaluated version for the endpoint, will never be rescued from a rescue_from block (even a rescue_from :all ) This is because Grape relies on Rack to catch that error and try the next versioned-route for cases where there exist identical Grape endpoints with different versions.
Any exception that is not subclass of StandardError should be rescued explicitly. Usually it is not a case for an application logic as such errors point to problems in Ruby runtime. This is following standard recommendations for exceptions handling.
Grape::API provides a logger method which by default will return an instance of the Logger class from Ruby's standard library.
To log messages from within an endpoint, you need to define a helper to make the logger available in the endpoint context.
class API < Grape :: API
helpers do
def logger
API . logger
end
end
post '/statuses' do
logger . info " #{ current_user } has statused"
end
endTo change the logger level.
class API < Grape :: API
self . logger . level = Logger :: INFO
endYou can also set your own logger.
class MyLogger
def warning ( message )
puts "this is a warning: #{ message } "
end
end
class API < Grape :: API
logger MyLogger . new
helpers do
def logger
API . logger
end
end
get '/statuses' do
logger . warning " #{ current_user } has statused"
end
endFor similar to Rails request logging try the grape_logging or grape-middleware-logger gems.
Your API can declare which content-types to support by using content_type . If you do not specify any, Grape will support XML , JSON , BINARY , and TXT content-types. The default format is :txt ; you can change this with default_format . Essentially, the two APIs below are equivalent.
class Twitter :: API < Grape :: API
# no content_type declarations, so Grape uses the defaults
end
class Twitter :: API < Grape :: API
# the following declarations are equivalent to the defaults
content_type :xml , 'application/xml'
content_type :json , 'application/json'
content_type :binary , 'application/octet-stream'
content_type :txt , 'text/plain'
default_format :txt
end If you declare any content_type whatsoever, the Grape defaults will be overridden. For example, the following API will only support the :xml and :rss content-types, but not :txt , :json , or :binary . Importantly, this means the :txt default format is not supported! So, make sure to set a new default_format .
class Twitter :: API < Grape :: API
content_type :xml , 'application/xml'
content_type :rss , 'application/xml+rss'
default_format :xml
end Serialization takes place automatically. For example, you do not have to call to_json in each JSON API endpoint implementation. The response format (and thus the automatic serialization) is determined in the following order:
format parameter in the query string, if specified.format option, if specified.Accept header.default_format option.:txt .For example, consider the following API.
class MultipleFormatAPI < Grape :: API
content_type :xml , 'application/xml'
content_type :json , 'application/json'
default_format :json
get :hello do
{ hello : 'world' }
end
endGET /hello (with an Accept: */* header) does not have an extension or a format parameter, so it will respond with JSON (the default format).GET /hello.xml has a recognized extension, so it will respond with XML.GET /hello?format=xml has a recognized format parameter, so it will respond with XML.GET /hello.xml?format=json has a recognized extension (which takes precedence over the format parameter), so it will respond with XML.GET /hello.xls (with an Accept: */* header) has an extension, but that extension is not recognized, so it will respond with JSON (the default format).GET /hello.xls with an Accept: application/xml header has an unrecognized extension, but the Accept header corresponds to a recognized format, so it will respond with XML.GET /hello.xls with an Accept: text/plain header has an unrecognized extension and an unrecognized Accept header, so it will respond with JSON (the default format). You can override this process explicitly by calling api_format in the API itself. For example, the following API will let you upload arbitrary files and return their contents as an attachment with the correct MIME type.
class Twitter :: API < Grape :: API
post 'attachment' do
filename = params [ :file ] [ :filename ]
content_type MIME :: Types . type_for ( filename ) [ 0 ] . to_s
api_format :binary # there's no formatter for :binary, data will be returned "as is"
header 'Content-Disposition' , "attachment; filename*=UTF-8'' #{ CGI . escape ( filename ) } "
params [ :file ] [ :tempfile ] . read
end
end You can have your API only respond to a single format with format . If you use this, the API will not respond to file extensions other than specified in format . For example, consider the following API.
class SingleFormatAPI < Grape :: API
format :json
get :hello do
{ hello : 'world' }
end
endGET /hello will respond with JSON.GET /hello.json will respond with JSON.GET /hello.xml , GET /hello.foobar , or any other extension will respond with an HTTP 404 error code.GET /hello?format=xml will respond with an HTTP 406 error code, because the XML format specified by the request parameter is not supported.GET /hello with an Accept: application/xml header will still respond with JSON, since it could not negotiate a recognized content-type from the headers and JSON is the effective default. The formats apply to parsing, too. The following API will only respond to the JSON content-type and will not parse any other input than application/json , application/x-www-form-urlencoded , multipart/form-data , multipart/related and multipart/mixed . All other requests will fail with an HTTP 406 error code.
class Twitter :: API < Grape :: API
format :json
end When the content-type is omitted, Grape will return a 406 error code unless default_format is specified. The following API will try to parse any data without a content-type using a JSON parser.
class Twitter :: API < Grape :: API
format :json
default_format :json
end If you combine format with rescue_from :all , errors will be rendered using the same format. If you do not want this behavior, set the default error formatter with default_error_formatter .
class Twitter :: API < Grape :: API
format :json
content_type :txt , 'text/plain'
default_error_formatter :txt
endCustom formatters for existing and additional types can be defined with a proc.
class Twitter :: API < Grape :: API
content_type :xls , 'application/vnd.ms-excel'
formatter :xls , -> ( object , env ) { object . to_xls }
endYou can also use a module or class.
module XlsFormatter
def self . call ( object , env )
object . to_xls
end
end
class Twitter :: API < Grape :: API
content_type :xls , 'application/vnd.ms-excel'
formatter :xls , XlsFormatter
endBuilt-in formatters are the following.
:json : use object's to_json when available, otherwise call MultiJson.dump:xml : use object's to_xml when available, usually via MultiXml:txt : use object's to_txt when available, otherwise to_s:serializable_hash : use object's serializable_hash when available, otherwise fallback to :json:binary : data will be returned "as is"If a body is present in a request to an API, with a Content-Type header value that is of an unsupported type a "415 Unsupported Media Type" error code will be returned by Grape.
Response statuses that indicate no content as defined by Rack here will bypass serialization and the body entity - though there should be none - will not be modified.
Grape supports JSONP via Rack::JSONP, part of the rack-contrib gem. Add rack-contrib to your Gemfile .
require 'rack/contrib'
class API < Grape :: API
use Rack :: JSONP
format :json
get '/' do
'Hello World'
end
end Grape supports CORS via Rack::CORS, part of the rack-cors gem. Add rack-cors to your Gemfile , then use the middleware in your config.ru file.
require 'rack/cors'
use Rack :: Cors do
allow do
origins '*'
resource '*' , headers : :any , methods : :get
end
end
run Twitter :: API Content-type is set by the formatter. You can override the content-type of the response at runtime by setting the Content-Type header.
class API < Grape :: API
get '/home_timeline_js' do
content_type 'application/javascript'
"var statuses = ...;"
end
end Grape accepts and parses input data sent with the POST and PUT methods as described in the Parameters section above. It also supports custom data formats. You must declare additional content-types via content_type and optionally supply a parser via parser unless a parser is already available within Grape to enable a custom format. Such a parser can be a function or a class.
With a parser, parsed data is available "as-is" in env['api.request.body'] . Without a parser, data is available "as-is" and in env['api.request.input'] .
The following example is a trivial parser that will assign any input with the "text/custom" content-type to :value . The parameter will be available via params[:value] inside the API call.
module CustomParser
def self . call ( object , env )
{ value : object . to_s }
end
end content_type :txt , 'text/plain'
content_type :custom , 'text/custom'
parser :custom , CustomParser
put 'value' do
params [ :value ]
endYou can invoke the above API as follows.
curl -X PUT -d 'data' 'http://localhost:9292/value' -H Content-Type:text/custom -v
You can disable parsing for a content-type with nil . For example, parser :json, nil will disable JSON parsing altogether. The request data is then available as-is in env['api.request.body'] .
Grape uses JSON and ActiveSupport::XmlMini for JSON and XML parsing by default. It also detects and supports multi_json and multi_xml. Adding those gems to your Gemfile and requiring them will enable them and allow you to swap the JSON and XML back-ends.
Grape supports a range of ways to present your data with some help from a generic present method, which accepts two arguments: the object to be presented and the options associated with it. The options hash may include :with , which defines the entity to expose.
Add the grape-entity gem to your Gemfile. Please refer to the grape-entity documentation for more details.
The following example exposes statuses.
module API
module Entities
class Status < Grape :: Entity
expose :user_name
expose :text , documentation : { type : 'string' , desc : 'Status update text.' }
expose :ip , if : { type : :full }
expose :user_type , :user_id , if : -> ( status , options ) { status . user . public? }
expose :digest do | status , options |
Digest :: MD5 . hexdigest ( status . txt )
end
expose :replies , using : API :: Status , as : :replies
end
end
class Statuses < Grape :: API
version 'v1'
desc 'Statuses index' do
params : API :: Entities :: Status . documentation
end
get '/statuses' do
statuses = Status . all
type = current_user . admin? ? :full : :default
present statuses , with : API :: Entities :: Status , type : type
end
end
end You can use entity documentation directly in the params block with using: Entity.documentation .
module API
class Statuses < Grape :: API
version 'v1'
desc 'Create a status'
params do
requires :all , except : [ :ip ] , using : API :: Entities :: Status . documentation . except ( :id )
end
post '/status' do
Status . create! params
end
end
endYou can present with multiple entities using an optional Symbol argument.
get '/statuses' do
statuses = Status . all . page ( 1 ) . per ( 20 )
present :total_page , 10
present :per_page , 20
present :statuses , statuses , with : API :: Entities :: Status
endThe response will be
{
total_page: 10,
per_page: 20,
statuses: []
}
In addition to separately organizing entities, it may be useful to put them as namespaced classes underneath the model they represent.
class Status
def entity
Entity . new ( self )
end
class Entity < Grape :: Entity
expose :text , :user_id
end
end If you organize your entities this way, Grape will automatically detect the Entity class and use it to present your models. In this example, if you added present Status.new to your endpoint, Grape will automatically detect that there is a Status::Entity class and use that as the representative entity. This can still be overridden by using the :with option or an explicit represents call.
You can present hash with Grape::Presenters::Presenter to keep things consistent.
get '/users' do
present { id : 10 , name : :dgz } , with : Grape :: Presenters :: Presenter
endThe response will be
{
id : 10 ,
name : 'dgz'
}It has the same result with
get '/users' do
present :id , 10
present :name , :dgz
end You can use Roar to render HAL or Collection+JSON with the help of grape-roar, which defines a custom JSON formatter and enables presenting entities with Grape's present keyword.
You can use Rabl templates with the help of the grape-rabl gem, which defines a custom Grape Rabl formatter.
You can use Active Model Serializers serializers with the help of the grape-active_model_serializers gem, which defines a custom Grape AMS formatter.
In general, use the binary format to send raw data.
class API < Grape :: API
get '/file' do
content_type 'application/octet-stream'
File . binread 'file.bin'
end
end You can set the response body explicitly with body .
class API < Grape :: API
get '/' do
content_type 'text/plain'
body 'Hello World'
# return value ignored
end
end Use body false to return 204 No Content without any data or content-type.
If you want to empty the body with an HTTP status code other than 204 No Content , you can override the status code after specifying body false as follows
class API < Grape :: API
get '/' do
body false
status 304
end
end You can also set the response to a file with sendfile . This works with the Rack::Sendfile middleware to optimally send the file through your web server software.
class API < Grape :: API
get '/' do
sendfile '/path/to/file'
end
end To stream a file in chunks use stream
class API < Grape :: API
get '/' do
stream '/path/to/file'
end
end If you want to stream non-file data use the stream method and a Stream object. This is an object that responds to each and yields for each chunk to send to the client. Each chunk will be sent as it is yielded instead of waiting for all of the content to be available.
class MyStream
def each
yield 'part 1'
yield 'part 2'
yield 'part 3'
end
end
class API < Grape :: API
get '/' do
stream MyStream . new
end
end Grape has built-in Basic authentication (the given block is executed in the context of the current Endpoint ). Authentication applies to the current namespace and any children, but not parents.
http_basic do | username , password |
# verify user's password here
# IMPORTANT: make sure you use a comparison method which isn't prone to a timing attack
end Grape can use custom Middleware for authentication. How to implement these Middleware have a look at Rack::Auth::Basic or similar implementations.
For registering a Middleware you need the following options:
label - the name for your authenticator to use it laterMiddlewareClass - the MiddlewareClass to use for authenticationoption_lookup_proc - A Proc with one Argument to lookup the options at runtime (return value is an Array as Parameter for the Middleware).예:
Grape :: Middleware :: Auth :: Strategies . add ( :my_auth , AuthMiddleware , -> ( options ) { [ options [ :realm ] ] } )
auth :my_auth , { realm : 'Test Api' } do | credentials |
# lookup the user's password here
{ 'user1' => 'password1' } [ username ]
endUse Doorkeeper, warden-oauth2 or rack-oauth2 for OAuth2 support.
You can access the controller params, headers, and helpers through the context with the #context method inside any auth middleware inherited from Grape::Middleware::Auth::Base .
Grape routes can be reflected at runtime. This can notably be useful for generating documentation.
Grape exposes arrays of API versions and compiled routes. Each route contains a prefix , version , namespace , method and params . You can add custom route settings to the route metadata with route_setting .
class TwitterAPI < Grape :: API
version 'v1'
desc 'Includes custom settings.'
route_setting :custom , key : 'value'
get do
end
endExamine the routes at runtime.
TwitterAPI :: versions # yields [ 'v1', 'v2' ]
TwitterAPI :: routes # yields an array of Grape::Route objects
TwitterAPI :: routes [ 0 ] . version # => 'v1'
TwitterAPI :: routes [ 0 ] . description # => 'Includes custom settings.'
TwitterAPI :: routes [ 0 ] . settings [ :custom ] # => { key: 'value' } Note that Route#route_xyz methods have been deprecated since 0.15.0 and removed since 2.0.1.
Please use Route#xyz instead.
Note that difference of Route#options and Route#settings .
The options can be referred from your route, it should be set by specifing key and value on verb methods such as get , post and put . The settings can also be referred from your route, but it should be set by specifing key and value on route_setting .
It's possible to retrieve the information about the current route from within an API call with route .
class MyAPI < Grape :: API
desc 'Returns a description of a parameter.'
params do
requires :id , type : Integer , desc : 'Identity.'
end
get 'params/:id' do
route . params [ params [ :id ] ] # yields the parameter description
end
end The current endpoint responding to the request is self within the API block or env['api.endpoint'] elsewhere. The endpoint has some interesting properties, such as source which gives you access to the original code block of the API implementation. This can be particularly useful for building a logger middleware.
class ApiLogger < Grape :: Middleware :: Base
def before
file = env [ 'api.endpoint' ] . source . source_location [ 0 ]
line = env [ 'api.endpoint' ] . source . source_location [ 1 ]
logger . debug "[api] #{ file } : #{ line } "
end
end Blocks can be executed before or after every API call, using before , after , before_validation and after_validation . If the API fails the after call will not be triggered, if you need code to execute for sure use the finally .
Before and after callbacks execute in the following order:
beforebefore_validationafter_validation (upon successful validation)after (upon successful validation and API call)finally (always)Steps 4, 5 and 6 only happen if validation succeeds.
If a request for a resource is made with an unsupported HTTP method (returning HTTP 405) only before callbacks will be executed. The remaining callbacks will be bypassed.
If a request for a resource is made that triggers the built-in OPTIONS handler, only before and after callbacks will be executed. The remaining callbacks will be bypassed.
For example, using a simple before block to set a header.
before do
header 'X-Robots-Tag' , 'noindex'
end You can ensure a block of code runs after every request (including failures) with finally :
finally do
# this code will run after every request (successful or failed)
endNamespaces
Callbacks apply to each API call within and below the current namespace:
class MyAPI < Grape :: API
get '/' do
"root - #{ @blah } "
end
namespace :foo do
before do
@blah = 'blah'
end
get '/' do
"root - foo - #{ @blah } "
end
namespace :bar do
get '/' do
"root - foo - bar - #{ @blah } "
end
end
end
endThe behavior is then:
GET / # 'root - '
GET /foo # 'root - foo - blah'
GET /foo/bar # 'root - foo - bar - blah' Params on a namespace (or whichever alias you are using) will also be available when using before_validation or after_validation :
class MyAPI < Grape :: API
params do
requires :blah , type : Integer
end
resource ':blah' do
after_validation do
# if we reach this point validations will have passed
@blah = declared ( params , include_missing : false ) [ :blah ]
end
get '/' do
@blah . class
end
end
endThe behavior is then:
GET /123 # 'Integer'
GET /foo # 400 error - 'blah is invalid'버전 작성
When a callback is defined within a version block, it's only called for the routes defined in that block.
class Test < Grape :: API
resource :foo do
version 'v1' , :using => :path do
before do
@output ||= 'v1-'
end
get '/' do
@output += 'hello'
end
end
version 'v2' , :using => :path do
before do
@output ||= 'v2-'
end
get '/' do
@output += 'hello'
end
end
end
endThe behavior is then:
GET /foo/v1 # 'v1-hello'
GET /foo/v2 # 'v2-hello'Altering Responses
Using present in any callback allows you to add data to a response:
class MyAPI < Grape :: API
format :json
after_validation do
present :name , params [ :name ] if params [ :name ]
end
get '/greeting' do
present :greeting , 'Hello!'
end
endThe behavior is then:
GET /greeting # {"greeting":"Hello!"}
GET /greeting ? name=Alan # {"name":"Alan","greeting":"Hello!"} Instead of altering a response, you can also terminate and rewrite it from any callback using error! , including after . This will cause all subsequent steps in the process to not be called. This includes the actual api call and any callbacks
Grape by default anchors all request paths, which means that the request URL should match from start to end to match, otherwise a 404 Not Found is returned. However, this is sometimes not what you want, because it is not always known upfront what can be expected from the call. This is because Rack-mount by default anchors requests to match from the start to the end, or not at all. Rails solves this problem by using a anchor: false option in your routes. In Grape this option can be used as well when a method is defined.
For instance when your API needs to get part of an URL, for instance:
class TwitterAPI < Grape :: API
namespace :statuses do
get '/(*:status)' , anchor : false do
end
end
end This will match all paths starting with '/statuses/'. There is one caveat though: the params[:status] parameter only holds the first part of the request url. Luckily this can be circumvented by using the described above syntax for path specification and using the PATH_INFO Rack environment variable, using env['PATH_INFO'] . This will hold everything that comes after the '/statuses/' part.
You can use instance variables to pass information across the various stages of a request. An instance variable set within a before validator is accessible within the endpoint's code and can also be utilized within the rescue_from handler.
class TwitterAPI < Grape :: API
before do
@var = 1
end
get '/' do
puts @var # => 1
raise
end
rescue_from :all do
puts @var # => 1
end
endThe values of instance variables cannot be shared among various endpoints within the same API. This limitation arises due to Grape generating a new instance for each request made. Consequently, instance variables set within an endpoint during one request differ from those set during a subsequent request, as they exist within separate instances.
class TwitterAPI < Grape :: API
get '/first' do
@var = 1
puts @var # => 1
end
get '/second' do
puts @var # => nil
end
end You can make a custom middleware by using Grape::Middleware::Base . It's inherited from some grape official middlewares in fact.
For example, you can write a middleware to log application exception.
class LoggingError < Grape :: Middleware :: Base
def after
return unless @app_response && @app_response [ 0 ] == 500
env [ 'rack.logger' ] . error ( "Raised error on #{ env [ 'PATH_INFO' ] } " )
end
endYour middleware can overwrite application response as follows, except error case.
class Overwriter < Grape :: Middleware :: Base
def after
[ 200 , { 'Content-Type' => 'text/plain' } , [ 'Overwritten.' ] ]
end
end You can add your custom middleware with use , that push the middleware onto the stack, and you can also control where the middleware is inserted using insert , insert_before and insert_after .
class CustomOverwriter < Grape :: Middleware :: Base
def after
[ 200 , { 'Content-Type' => 'text/plain' } , [ @options [ :message ] ] ]
end
end
class API < Grape :: API
use Overwriter
insert_before Overwriter , CustomOverwriter , message : 'Overwritten again.'
insert 0 , CustomOverwriter , message : 'Overwrites all other middleware.'
get '/' do
end
end You can access the controller params, headers, and helpers through the context with the #context method inside any middleware inherited from Grape::Middleware::Base .
Note that when you're using Grape mounted on Rails you don't have to use Rails middleware because it's already included into your middleware stack. You only have to implement the helpers to access the specific env variable.
If you are using a custom application that is inherited from Rails::Application and need to insert a new middleware among the ones initiated via Rails, you will need to register it manually in your custom application class.
class Company :: Application < Rails :: Application
config . middleware . insert_before ( Rack :: Attack , Middleware :: ApiLogger )
end By default you can access remote IP with request.ip . This is the remote IP address implemented by Rack. Sometimes it is desirable to get the remote IP Rails-style with ActionDispatch::RemoteIp .
Add gem 'actionpack' to your Gemfile and require 'action_dispatch/middleware/remote_ip.rb' . Use the middleware in your API and expose a client_ip helper. See this documentation for additional options.
class API < Grape :: API
use ActionDispatch :: RemoteIp
helpers do
def client_ip
env [ 'action_dispatch.remote_ip' ] . to_s
end
end
get :remote_ip do
{ ip : client_ip }
end
end Use rack-test and define your API as app .
You can test a Grape API with RSpec by making HTTP requests and examining the response.
describe Twitter :: API do
include Rack :: Test :: Methods
def app
Twitter :: API
end
context 'GET /api/statuses/public_timeline' do
it 'returns an empty array of statuses' do
get '/api/statuses/public_timeline'
expect ( last_response . status ) . to eq ( 200 )
expect ( JSON . parse ( last_response . body ) ) . to eq [ ]
end
end
context 'GET /api/statuses/:id' do
it 'returns a status by id' do
status = Status . create!
get "/api/statuses/ #{ status . id } "
expect ( last_response . body ) . to eq status . to_json
end
end
endThere's no standard way of sending arrays of objects via an HTTP GET, so POST JSON data and specify the correct content-type.
describe Twitter :: API do
context 'POST /api/statuses' do
it 'creates many statuses' do
statuses = [ { text : '...' } , { text : '...' } ]
post '/api/statuses' , statuses . to_json , 'CONTENT_TYPE' => 'application/json'
expect ( last_response . body ) . to eq 201
end
end
end You can test with other RSpec-based frameworks, including Airborne, which uses rack-test to make requests.
require 'airborne'
Airborne . configure do | config |
config . rack_app = Twitter :: API
end
describe Twitter :: API do
context 'GET /api/statuses/:id' do
it 'returns a status by id' do
status = Status . create!
get "/api/statuses/ #{ status . id } "
expect_json ( status . as_json )
end
end
end require 'test_helper'
class Twitter :: APITest < MiniTest :: Test
include Rack :: Test :: Methods
def app
Twitter :: API
end
def test_get_api_statuses_public_timeline_returns_an_empty_array_of_statuses
get '/api/statuses/public_timeline'
assert last_response . ok?
assert_equal [ ] , JSON . parse ( last_response . body )
end
def test_get_api_statuses_id_returns_a_status_by_id
status = Status . create!
get "/api/statuses/ #{ status . id } "
assert_equal status . to_json , last_response . body
end
end describe Twitter :: API do
context 'GET /api/statuses/public_timeline' do
it 'returns an empty array of statuses' do
get '/api/statuses/public_timeline'
expect ( response . status ) . to eq ( 200 )
expect ( JSON . parse ( response . body ) ) . to eq [ ]
end
end
context 'GET /api/statuses/:id' do
it 'returns a status by id' do
status = Status . create!
get "/api/statuses/ #{ status . id } "
expect ( response . body ) . to eq status . to_json
end
end
end In Rails, HTTP request tests would go into the spec/requests group. You may want your API code to go into app/api - you can match that layout under spec by adding the following in spec/rails_helper.rb .
RSpec . configure do | config |
config . include RSpec :: Rails :: RequestExampleGroup , type : :request , file_path : /spec / api/
end class Twitter :: APITest < ActiveSupport :: TestCase
include Rack :: Test :: Methods
def app
Rails . application
end
test 'GET /api/statuses/public_timeline returns an empty array of statuses' do
get '/api/statuses/public_timeline'
assert last_response . ok?
assert_equal [ ] , JSON . parse ( last_response . body )
end
test 'GET /api/statuses/:id returns a status by id' do
status = Status . create!
get "/api/statuses/ #{ status . id } "
assert_equal status . to_json , last_response . body
end
end Because helpers are mixed in based on the context when an endpoint is defined, it can be difficult to stub or mock them for testing. The Grape::Endpoint.before_each method can help by allowing you to define behavior on the endpoint that will run before every request.
describe 'an endpoint that needs helpers stubbed' do
before do
Grape :: Endpoint . before_each do | endpoint |
allow ( endpoint ) . to receive ( :helper_name ) . and_return ( 'desired_value' )
end
end
after do
Grape :: Endpoint . before_each nil
end
it 'stubs the helper' do
end
end Use grape-reload.
Add API paths to config/application.rb .
# Auto-load API and its subdirectories
config . paths . add File . join ( 'app' , 'api' ) , glob : File . join ( '**' , '*.rb' )
config . autoload_paths += Dir [ Rails . root . join ( 'app' , 'api' , '*' ) ] Create config/initializers/reload_api.rb .
if Rails . env . development?
ActiveSupport :: Dependencies . explicitly_unloadable_constants << 'Twitter::API'
api_files = Dir [ Rails . root . join ( 'app' , 'api' , '**' , '*.rb' ) ]
api_reloader = ActiveSupport :: FileUpdateChecker . new ( api_files ) do
Rails . application . reload_routes!
end
ActionDispatch :: Callbacks . to_prepare do
api_reloader . execute_if_updated
end
endFor Rails >= 5.1.4, change this:
ActionDispatch :: Callbacks . to_prepare do
api_reloader . execute_if_updated
endto this:
ActiveSupport :: Reloader . to_prepare do
api_reloader . execute_if_updated
endSee StackOverflow #3282655 for more information.
Grape has built-in support for ActiveSupport::Notifications which provides simple hook points to instrument key parts of your application.
The following are currently supported:
The main execution of an endpoint, includes filters and rendering.
The execution of the main content block of the endpoint.
The execution of validators.
Serialization or template rendering.
Grape::Formatter::Json )See the ActiveSupport::Notifications documentation for information on how to subscribe to these events.
Grape integrates with following third-party tools:
Grape is work of hundreds of contributors. You're encouraged to submit pull requests, propose features and discuss issues.
See CONTRIBUTING.
See SECURITY for details.
MIT License. See LICENSE for details.
Copyright (c) 2010-2020 Michael Bleigh, Intridea Inc. and Contributors.