March 19, 2015

Grape自定义参数类型

Grape的params函数是非常方便用于定义API参数的工具. 这次做到一个接口要传入GeoJSON格式的参数. 与世界在接口上直接写了:

params do
  requires :location, type: GeoJSONPoint, desc: "地点"
end
class GeoJSONPoint
  attr_accessor :type, :coordinates
  
  def initialize(coordinates=[])
    @coordinates = coordinates
  end
  
  # always a point
  def type
    "Point"
  end
end

不过这样显然是不行的, 一定会出400 Bad Request. Grape毕竟不知道GeoJSONPoint这个类是怎么转换的. 读了读Grape的代码, 发现Grape是用 Virtus 这个gem来转换参数=>对象的.
于是这个GeoJSONPoint的类就需要用Virtus的方式来定义. 于是代码变成了

class GeoJSONPoint
  include Virtus.model
  
  attribute :type, String
  attribute :coordinates, Array
  
  def initialize(coordinates=[])
    @coordinates = coordinates
  end
  
  # always a point
  def type
    "Point"
  end
end

先用Virtus的方法试一下, 结果OK:

coercer = Virtus::Attribute.build(GeoJSONPoint)
point = coercer.coerce({type: "Point", coordinates: [190, 50]})
=> #<GeoJSONPoint:0x007ffdf3a61618 @coordinates={:type=>"Point", :coordinates=>[190, 50]}>

再到RestClient里发个请求看看:

{"location" : { 
  "type" : "Point", 
  "coordinates" : [190, 50] 
}}

结果:

{
  "application":{"name":"TestServer","status":"ok"},
  "request":{
    "headers":{
     "Version":"HTTP/1.1",
     "Host":"localhost:3000",
     "Connection":"Keep-Alive",
     "User-Agent":"Apache-HttpClient/4.3.5 (java 1.5)",
     "Accept-Encoding":"gzip,deflate"
   },
   "params":{
     "location":{
       "type":"Point",
       "coordinates":[190,50]}}},
 "env":"127.0.0.1"
}

Grape能够成功把GeoJSONPoint转换出来.