天天看點

Grape教程-params

參數

請求參數可以通過params擷取,params是一個hash對象,包括GET、POST、PUT參數,以及路徑字元串中的任何命名參數:

get :public_timeline do
  Status.order(params[:sort_by])
end
           

Parameters are automatically populated from the request body on POST and PUT for form input, JSON and XML content-types.

請求:

curl -d '{"text": "140 characters"}' 'http://localhost:9292/statuses' -H Content-Type:application/json -v
           

Grape中:

post '/statuses' do
  Status.create!(text: params[:text])
end
           

多部分的POSTs和PUTs也是支援的。

curl --form image_file='@image.jpg;type=image/jpg' http://localhost:9292/upload
           
post 'upload' do
  # file in params[:image_file]
end
           

在以下任何一個或兩個有沖突時:

  • 路徑字元串參數
  • GET/POST/PUT參數
  • POST/PUT請求正文的内容

    路徑字元串中的将生效。

聲明

Grape隻允許通路在params塊中聲明的變量,它過濾掉傳遞過來但是不允許通路的變量,請看下面的API:

format :json

post 'users/signup' do
  { 'declared_params' => declared(params) }
end
           

如果我們沒有指定任何參數,declared會傳回一個空的Hashie::Mash執行個體。

curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "last_name": "last name"}}'
           

應答:

{
  "declared_params": {}
}
           

一旦我們添加了參數requirements,Grape僅傳回聲明的參數:

format :json

params do
  requires :user, type: Hash do
    requires :first_name, type: String
    requires :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是Hashie::Mash執行個體,允許你使用.通路參數:

declared(params).user == declared(params)['user']
           

The #declared method is not available to before filters, as those are evaluated prior to parameter coercion.

包含父命名空間

預設的,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"
  },
}
           

Include missing

預設的,declared(params)包含值為nil的參數,如果你隻想傳回值為非nil的參數,你可以使用include_missing 選項。該選項預設為true,看下下面的API:

format :json

params do
  requires :first_name, type: String
  optional :last_name, type: String
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": {
    "first_name": "first name",
    "last_name": null
  }
}
           

在嵌套的hash中也會生效:

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"}}}'
           
{
  "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時,值為nil的變量也會傳回。

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"}
   }
 }
}
           

參數校驗和強轉

你可以通過一個params block為參數定義校驗選項:

params do
  requires :id, type: Integer
  optional :text, type: String, regexp: /\A[a-z]+\z/
  group :media do
    requires :url
  end
  optional :audio 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
           

如果指定了參數類型,在參數強轉後,會對強轉後的參數進行隐式的校驗,以保證轉換後的類型是聲明的類型。(When a type is specified an implicit validation is done after the coercion to ensure the output type is the one declared.)

可選參數可以提供一個預設值:

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
           

注意,預設值要滿足所有的校驗條件,如果沒有顯示提供值,下面的例子總是會失敗:

params do
  optional :color, type: String, default: 'blue', values: ['red', 'green']
end
           

支援的參數類型

  • Integer
  • Float
  • BigDecimal
  • Numeric
  • Date
  • DateTime
  • Time
  • Boolean
  • String
  • Symbol
  • Rack::Multipart::UploadedFile (alias File)
  • JSON

自定義類型和轉換

除了上面列出的類型,如果提供了顯示的轉換方法parse,任何類都可以當做類型使用。如果提供的是類方法,Grape會自動使用,該方法入參必須是一個字元串,并且傳回一個正确的類型,或者抛出異常表示該值無效:

class Color
  attr_reader :value
  def initialize(color)
    @value = color
  end

  def self.parse(value)
    fail 'Invalid color' unless %w(blue red green).include?(value)
    new(value)
  end
end

# ...

params do
  requires :color, type: Color, default: Color.new('blue')
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(:decode)
  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
           

下面的例子為corece_with提供了一個lambda表達式,該表達式接受一個string類型參數,傳回一個整形數組,以比對Array[Integer]類型:

params do
  requires :values, type: Array[Integer], coerce_with: ->(val) { val.split(/\s+/).map(&:to_i) }
end
           

MultipatFile參數

Grape利用Rack::Request内置的對MultipatFile參數的支援,這種類型的參數可以被聲明為type: File:

params do
  requires :avatar, type: File
end
post '/' do
  # Parameter will be wrapped using Hashie:
  params.avatar.filename # => 'avatar.png'
  params.avatar.type     # => 'image/png'
  params.avatar.tempfile # => #<File>
end
           

JSON類型

Grape支援JSON格式的複雜類型參數,使用type: JSON聲明,JSON對象和數組都可以被接受,在兩種情況下,内置的校驗規則對所有的對象(JSON對象或JSON數組中的所有對象)都适用。(Grape supports complex parameters given as JSON-formatted strings using the special type: JSON declaration. JSON objects and arrays of objects are accepted equally, with nested validation rules applied to all objects in either case):

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
           

For stricter control over the type of JSON structure which may be supplied, use type: Array, coerce_with: JSON or type: Hash, coerce_with: JSON.

允許多種類型

多類型參數可以使用types來聲明,而不是使用type:

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或者optinal提供block可以使用嵌套參數:

params do
  requires :id, type: Integer
  optional :text, type: String, regexp: /\A[a-z]+\z/
  group :media do
    requires :url
  end
  optional :audio 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[:media][:url]

params[:id]

,提供

params[:audio]

時才需要提供

params[:audio][:format]

。提供block時,group、requires、optional接受值可以是Array或者Hash的type(預設為Array)。Depending on the value, the nested parameters will be treated either as values of a hash or as values of hashes in an 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
           

在上面的例子中,Grape會使用blank?方法檢查是否提供了shelf_id參數。

give也接受一個自定義的Proc,下面的例子,description參數隻有在category 的值是“foo”的時候才需要提供:

params do
  optional :category
  given category: ->(val) { val == 'foo' } do
    requires :description
  end
end
           

内置的驗證方法

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選項,參數可以被限制為一組特定的值。

Default values are eagerly evaluated. Above :non_random_number will evaluate to the same number for each call to the endpoint of this params block. To have the default evaluate lazily with each request use a lambda, like :random_number above.

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選項提供一個範圍參數:

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
           

注意,range的起始值類型必須和: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對象,在每個請求中進行求值:

params do
  requires :hashtag, type: String, values: -> { Hashtag.all.map(&:tag) }
end
           

也可以通過except選項來限制不能包含哪些值,except接受相同類型的參數作為值(Procs, ranges,等):

params do
  requires :browsers, values: { except: [ 'ie6', 'ie7', 'ie8' ] }
end
           

values和except可以結合使用,用以指定哪些值能接受,哪些不能接受。可以分别為except和value自定義不同的錯誤消息,用于指定值落在了except中或者不在value中:

params do
  requires :number, type: Integer, values: { value: 1..20 except: [4,13], except_message: 'includes unsafe numbers', message: 'is outside the range of numbers allowed' }
end
           

正規表達式

通過:regexp選項,可以将參數限制為和正規表達式比對,如果不比對将會傳回一個錯誤,正規表達式選項對requires和optinal參數都有效。

params do
  requires :email, regexp: /.+@.+/
end
           

當參數不包含值時,校驗是能通過的。為了保證參數包含值,可以使用alow_blank: false。

params do
  requires :email, allow_blank: false, regexp: /.+@.+/
end
           

互斥

參數可以通過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可以指定正好有一個參數被提供:

params do
  optional :beer
  optional :wine
  exactly_one_of :beer, :wine
end
           

至少有一個

at_least_one_of選項保證至少有一個參數被提供:

params do
  optional :beer
  optional :wine
  optional :juice
  at_least_one_of :beer, :wine, :juice
end
           

都提供或都不提供

可以通過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 do
    optional :meat
    optional :fish
    optional :rice
    at_least_one_of :meat, :fish, :rice
  end
  group :drink do
    optional :beer
    optional :wine
    optional :juice
    exactly_one_of :beer, :wine, :juice
  end
  optional :dessert do
    optional :cake
    optional :icecream
    mutually_exclusive :cake, :icecream
  end
  optional :recipe 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, and segment。你可以選擇一個你喜歡的。

通過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
           

You can also define a route parameter type by passing to route_param's options.

namespace :arithmetic do
  route_param :n, type: Integer do
    desc 'Returns in power'
    get 'power' do
      params[:n] ** params[:n]
    end
  end
end