Logo

grape实践小结

avatar roger 31 Aug 2016

grape

grape 是一个基于 Rack 的用来构建 RESTful API 的 Ruby 框架。它不仅提供了很好的 DSL 语法,而且还有很多配套的 projects

grape 的 DSL 语法

我们可以用下面的代码来看看 grape 提供给我们的 DSL。

# /app/services/api/statuses.rb
class API::V1::Statuses < Grape::API
  namespace :statuses do
    desc 'Return a public timeline.'
    get :public_timeline do
      ...
    end
  
    desc 'Return a status.'
    params do
      requires :id, type: Integer, desc: 'Status id.'
    end
    route_param :id do
      get do
        ...
      end
    end
  
    desc 'Create a status.'
    params do
      requires :status, type: String, desc: 'Your status.'
    end
    post do
      ...
    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
      ...
    end
  
    desc 'Delete a status.'
    params do
      requires :id, type: String, desc: 'Status ID.'
    end
    delete ':id' do
      ...
    end
  end
end
  • namespace 指明了一个命名空间,它也有其它的别名:groupresourceresourcessegment,我们可以根据情况使用
  • desc 描述一个 API
  • params 描述 API 的参数,grape 可以帮忙检查类型,而且可以在里面加入验证让 grape 帮我们处理
  • route_param 代表一个路径参数,一般是 id
  • get post put delete 这些都是 RESTful API 的动作参数,代表 http request method.

version 制定

我们的代码在不断迭代,API 的版本也会不断迭代,grape 提供一个 version 的方法可以方便我们定义 API 的版本。

# /app/services/api/v1.rb
class API::V1 < Grape::API
  # 在这里定义 v1 版本
  version 'v1' 
  prefix :api
  
  # 把 Statuses 这个 API 加载到 v1 下
  mount Statuses
end

异常处理

grape 也提供 rescue_from 这样的方法来帮助我们处理异常,例如我们用 cancancan 来做认证的时候,可以用到这个。

# /app/services/api/v1.rb
class API::V1 < Grape::API
  # 捕获 CanCan::AccessDenied 异常
  rescue_from CanCan::AccessDenied do |_|
    error! '401 Unauthorized', 401
  end

  # 捕获 ActiveRecord::RecordNotFound 异常
  rescue_from ActiveRecord::RecordNotFound do |_|
    rack_response('{"error": "资源不存在!"}', 404)
  end
end

这样我们可以在外面统一处理这些异常

grape 的 before 和 helper

grape 的 helper 类似于 Rails 的 helper,也是在 API 里面定义一些帮助方法。

class API::V1 < Grape::API
  # 引入 ::Helpers::BaseHelper 这个 module 里面的 helper 方法
  helpers ::Helpers::BaseHelper

  # 定义一些 helper 方法
  helpers do
    def authorize
      ....
    end
  end
end

这样我们就可以利用 before 来执行验证了。

class API::V1 < Grape::API
  before do
    authorize
  end
end

这样每次 API 调用时,我们都会运行 authorize 来验证。

用 grape-swagger 来生成 grape 的文档

grape-swagger 可以帮助我们快速地建立 API 的文档(RAILS 项目可以用 grape-swagger-rails)。要用上 grape-swagger 需要在 API 里面增加 add_swagger_documentation

class API::V1 < Grape::API
  add_swagger_documentation hide_documentation_path: true,
                            hide_format: true,
                            api_version: 'v1',
                            markdown:
                              GrapeSwagger::Markdown::RedcarpetAdapter.new(
                                render_options: { highlighter: :rouge }
                              )
end

后面那些参数是我们用到的一些设置,为了支持 markdown 语法,我们还需要增加 redcarpet(用于解析 markdown 语法) 和 rouge(用于语法高亮) 两个 gem

用 grape-entity 来包装 model

对于每个 API 来说,我们可能需要暴露的 model 的属性是不一样的,这时候我们就可以用 grape-entity 来做响应的格式化,控制暴露的属性,相当于 MVC 架构中的 View。

# /app/services/api/v1/entities/status_one.rb
module API::V1::Entities
  class StatusOne < Grape::Entity
    # 暴露 attribute_one
    expose :attribute_one
  end
end

# /app/services/api/v1/entities/status_two.rb
module API::V1::Entities
  class StatusTwo < Grape::Entity
    # 暴露 attribute_two
    expose :attribute_two
  end
end


# /app/services/api/statuses.rb
class API::V1::Statuses < Grape::API
  namespace :statuses do
    get :one do
      # 使用 StatusOne entity
      present Status.first, with: API::V1::Entities::StatusOne
    end
    
    get :two do
      # 使用 StatusTwo entity
      present Status.first, with: API::V1::Entities::StatusTwo
    end
  end
end

这样两个 API 返回的信息是不一样的,一个是 attribute_one,一个是 attribute_two。

使用 grape-attack 来做 API 的限流

我们用了 grape-attack 来做 API的限流保护,主要是 API 获取 token 这个功能用到。

# /app/services/api/statuses.rb
class API::V1::Statuses < Grape::API
  namespace :statuses do
    throttle max: 10, per: 1.minute
    get :throttled_api do
      ...
    end
  end
end

这里我们限制了这个 API 一分钟最多被调用 10 次,更多的设置可以参考官网。

个人使用 grape 的一些体会

  • grape 作为成熟的 API 框架,可以在参数里面负责很多的验证,可以很简单地生成文档,这都是我很喜欢的功能。
  • API 的错误返回需要提前考虑,推荐返回是有不同的 http status(例如成功是 200, 参数错误是 400)跟 error code(在返回结果里面用到,这个由项目定义,表示发生的错误,例如 { error_message: '数据请求错误', error_code: 4001 }
  • 在用 grape-entity 的时候,我们会增加两个类 API::Entities::ModelAPI::Entities:: ResourcesCollection
# 用于 singular 的情况
module API::Entities
  class Model < Grape::Entity
    expose :id
    expose :created_at
  end
end

# 用于 plural 的情况
module API::Entities
  class ResourcesCollection < Grape::Entity
    # 这里暴露 meta 属性返回分页信息,当然也可以增加其它内容
    expose :meta do
      expose :total
      expose :per_page
      expose :page
    end

    private

    def total
      return options[:total] if options[:total]
      object[:entries].respond_to?(:total_count) ? object[:entries].total_count : object[:entries].count
    end

    def per_page
      options[:per_page].present? ? options[:per_page] : Rails.application.config.per_page
    end

    def page
      options[:page]
    end
  end
end

其它 Entity 可以分情况继承,方便暴露一些固定的属性。

  • grape 可以结合 ransack 来集成搜索,排序的功能,主要是用 ransack 的条件规则作为参数规则,这样可以方便我们处理搜索跟排序的功能,这酸爽。
Tags
ruby
api
grape
Kontaktieren Sie uns