Ruby Grape JSON-over-HTTP API,自定义 JSON 表示

标签 ruby ruby-grape jsend

我有一个 Grape::API 的小型原型(prototype)子类作为机架服务,并且正在使用 Grape::Entity 来呈现我的应用程序的内部对象。

我喜欢 Grape::Entity DSL,但我很难找到我应该如何超越默认的 JSON 表示,这对我们的目的来说太轻了。我被要求以“jsend 或类似”格式生成输出:http://labs.omniti.com/labs/jsend

我完全不确定什么性质的变化最符合 Grape 框架(我想要一条阻力最小的路径)。我应该创建一个自定义的 Grape 格式化程序(我不知道该怎么做),新的机架中间件(我已经这样做是为了通过 SysLog 记录 API 输入/输出 - 但格式看起来很糟糕,因为我需要从 JSON 解析主体以添加容器级别),或者从 Grape::Entity 更改为例如拉 bool ?

示例代码(“app.rb”)

require "grape"
require "grape-entity"

class Thing
  def initialize llama_name
    @llama_name = llama_name
  end
  attr_reader :llama_name
end

class ThingPresenter < Grape::Entity
  expose :llama_name
end

class MainService < Grape::API
  prefix      'api'
  version     'v2'
  format      :json
  rescue_from :all

  resource :thing do
    get do
      thing = Thing.new 'Henry'
      present thing, :with => ThingPresenter
    end
  end
end

机架文件(“config.ru”)

require File.join(File.dirname(__FILE__), "app")
run MainService

我启动它:

rackup -p 8090

并称它为:

curl http://127.0.0.1:8090/api/v2/thing
{"llama_name":"Henry"}

我想看到的:

curl http://127.0.0.1:8090/api/v2/thing
{"status":"success","data":{"llama_name":"Henry"}}

显然我可以做类似的事情

  resource :thing do
    get do
      thing = Thing.new 'Henry'
      { :status => "success", :data => present( thing, :with => ThingPresenter ) }
    end
  end

在每条路线上——但那似乎不是很干燥。当这个 API 变得更大并由整个团队维护时,我正在寻找更干净的东西,并且不太容易出现剪切和粘贴错误


奇怪的是,当我尝试 { :status => "success", :data => present( thing, :with => ThingPresenter ) } 使用 grape 0.3.2,我无法让它工作。 API 仅从 present 返回值 - 这里发生的事情比我最初想象的要多。

最佳答案

这就是我通过阅读 Grape 文档、谷歌搜索和阅读 github 上的一些拉取请求的组合而得到的结果。基本上,在声明 :json 格式(以获取它附带的所有其他默认好东西)之后,我用添加 jsend 包装层的新格式覆盖输出格式化程序。这比尝试包装 Grape 的 #present 帮助程序(不能很好地覆盖错误)或机架中间件解决方案(需要反序列化和重新序列化 JSON,加上需要大量额外的代码来弥补错误)。

require "grape"
require "grape-entity"
require "json"

module JSendSuccessFormatter
  def self.call object, env
    { :status => 'success', :data => object }.to_json
  end
end

module JSendErrorFormatter
  def self.call message, backtrace, options, env
    # This uses convention that a error! with a Hash param is a jsend "fail", otherwise we present an "error"
    if message.is_a?(Hash)
      { :status => 'fail', :data => message }.to_json
    else
      { :status => 'error', :message => message }.to_json
    end
  end
end

class Thing
  def initialize llama_name
    @llama_name = llama_name
  end
  attr_reader :llama_name
end

class ThingPresenter < Grape::Entity
  expose :llama_name
end

class MainService < Grape::API
  prefix      'api'
  version     'v2'
  format      :json
  rescue_from :all

  formatter :json, JSendSuccessFormatter
  error_formatter :json, JSendErrorFormatter

  resource :thing do
    get do
      thing = Thing.new 'Henry'
      present thing, :with => ThingPresenter
    end
  end

  resource :borked do
    get do
      error! "You broke it! Yes, you!", 403
    end
  end
end

关于Ruby Grape JSON-over-HTTP API,自定义 JSON 表示,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15498093/

相关文章:

json - 在 JSend 规范中,失败和错误有什么区别?

android - 使用 JSend 格式通过 Retrofit 将 JSON 响应转换为 POJO?

php - 如何在 tymondesigns/jwt-auth 包生成的 laravel 5 中捕获全局异常?

ruby - 在 Amazon EC2 上运行 Sinatra 应用程序

ruby - Ruby 的 fiber 4kB 堆栈大小的后果

ruby-grape - 在同一请求中上传多个文件

ruby - 为什么 Ruby 服务器仅在守护进程时才会产生僵尸?

ruby - 将 2 个 Hash 与由数组组成的值进行比较

ruby - Errno::ECONNRESET(对等方重置连接 - SSL_connect)OpenSSL 证书? ruby 轨道 2.3.15

ruby-on-rails - Rspec GET 到另一个端点一秒钟 - ActionController::UrlGenerationError