grape入门

1.Grape是运行在rack或与rails/sinatra配合使用的一种restful风格的ruby微框架,通过提供简单的DSL(领域特定语言)简化APIs开发.它内置支持mutiple formats(),subdomain/prefix restriction, versioning等通用约束(ruby约束高于配置).详见http://intridea.github.io/grape/.

2.安装Grape

gem install grape

或者编辑Gemfile.

gem "grape"

然后

bundle install

3.基础用法

Grape APIs和Rack应用继承自Grape::API.

下面展示一段用Grape写的简单的twitter 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

关于上面代码的简单解释:

将API文件放在/app/api文件下,并且修改/config/application.rb文件:

config.paths.add "app/api", glob: "**/*.rb"
config.autoload_paths += Dir["#{Rails.root}/app/api/*"]

并且修改路由文件/config/routes.rb:

mount   Twitter::API  => '/'

3.调用API

使用mount方法:

class Twitter::API < Grape::API
  mount Twitter::APIv1
  mount Twitter::APIv2
end
class Twitter::API < Grape::API
  mount Twitter::APIv1 => '/v1'
end

4.为API添加描述

desc "Returns your public timeline." do
  detail 'more details'
  params  API::Entities::Status.documentation
  success API::Entities::Entity
  failure [[401, 'Unauthorized', "Entities::Error"]]
  named 'My named route'
  headers [XAuthToken: {
             description: 'Valdates your identity',
             required: true
           },
           XOptionalHeader: {
             description: 'Not really needed',
            required: false
           }
          ]
end
get :public_timeline do
  Status.limit(20)
end

details:更加详细的描述

params:直接从实体定义参数

success:实体对象的默认使用路由

failure:请求失败返回的http代码

(====续=====)

参数认证和约束

(1).在block中定义设置参数的约束项:

params do
  requires :id, type: Integer  
  optional :text, type: String, regexp: /^[a-z]+$/  #text全是小写字母
  group :media do #参数嵌套;与[:id]协同
    requires :url
  end
end
put ':id' do
  # params[:id] is an Integer
end

(2).命名空间认证和约束

允许定义参数以及在命名空间内部使用各种方法,命名空间就是一个sandbox,叫做module;采用这种模块化机制可以有效限定作用域;

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]) #通过 :user_id获取:status_id;
    end
  end
end

(3)用户认证

class AlphaNumeric < Grape::Validations::Validator
  def validate_param!(attr_name, params)
    unless params[attr_name] =~ /^[[:alnum:]]+$/  #[:alnum]  posix字符类字母数字类
      throw :error, status: 400, message: "#{attr_name}: must consist of alpha-numeric characters"
    end
  end
end
params do
  requires :text, alpha_numeric: true  #也可这样约束类型
end
class Length < Grape::Validations::SingleOptionValidator
  def validate_param!(attr_name, params)
    unless params[attr_name].length <= @option
      throw :error, status: 400, message: "#{attr_name}: must be at the most #{@option} characters long"
    end
  end
end
params do
  requires :text, length: 140
end

请求头

get do
  error!('Unauthorized', 401) unless headers['Secret-Password'] == 'swordfish'
end

get do
  error!('Unauthorized', 401) unless env['HTTP_SECRET_PASSWORD'] == 'swordfish'
end
header "X-Robots-Tag", "noindex"

路由

get ':id', requirements: { id: /[0-9]*/ } do  #Regexp 条件过滤
  Status.find(params[:id])
end

namespace :outer, requirements: { id: /[0-9]*/ } do
  get :id do
  end

  get ":id/edit" do
  end
end
module StatusHelpers
  def user_info(user)
    "#{user} has statused #{user.statuses} status(s)"
  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 a module
  helpers StatusHelpers

  get 'info' do
    # helpers available in your endpoint and filters
    user_info(current_user)
  end
end