【问题标题】:Grape: required params with grape-entity葡萄:葡萄实体的必需参数
【发布时间】:2015-06-13 13:42:34
【问题描述】:

我正在用grape 编写一个API 服务器,我选择使用grape-entity,因为它能够自动生成swagger 的文档。 但是现在当我根据需要设置参数时我遇到了问题。因为葡萄不验证参数是否存在。看起来葡萄忽略了实体参数的required: true

app.rb

module Smart
  module Version1
    class App < BaseApi

      resource :app do

        # POST /app
        desc 'Creates a new app' do
          detail 'It is used to re gister a new app on the server and get the app_id'
          params  Entities::OSEntity.documentation
          success Entities::AppEntity
          failure [[401, 'Unauthorized', Entities::ErrorEntity]]
          named 'My named route'
        end
        post do
          app = ::App.create params
          present app, with: Entities::AppEntity
        end
      end
    end
  end
end

os_entity.rb

module Smart
  module Entities
    class OSEntity < Grape::Entity

      expose :os, documentation: { type: String, desc: 'Operative system name', values: App::OS_LIST, required: true }

    end
  end
end

app_entity.rb

module Smart
  module Entities
    class AppEntity < OSEntity

      expose :id, documentation: { type: 'integer', desc: 'Id of the created app', required: true }
      expose :customer_id, documentation: { type: 'integer', desc: 'Id of the customer', required: true }

    end
  end
end

现在其他一切都很好,但我不知道如何以 DRY 方式使用实体,并使葡萄验证参数的要求。

【问题讨论】:

    标签: ruby-on-rails ruby api grape grape-entity


    【解决方案1】:

    经过一些工作,我能够按照我认为应该工作的方式制作葡萄。因为我不想重复验证和文档的代码。您只需将其添加到初始化程序中(当然,如果您在 rails 中)。我还能够支持嵌套关联。如您所见,API 代码看起来很简单,招摇看起来很完美。 以下是 API 和所有需要的实体:

    app/api/smart/entities/characteristics_params_entity.rb

    module Smart
      module Entities
        class CharacteristicsParamsEntity < Grape::Entity
    
          root :characteristics, :characteristic
          expose :id, documentation: { type: Integer, desc: 'Id of the characteristic' }
    
        end
      end
    end
    

    app/api/smart/entities/characterisitcs_entity.rb

    module Smart
      module Entities
        class CharacteristicsEntity < CharacteristicsParamsEntity
    
          expose :id, documentation: { type: Integer, desc: 'Id of the characteristic' }
          expose :name, documentation: { type: String, desc: 'Name of the characteristic' }
          expose :description, documentation: { type: String, desc: 'Description of the characteristic' }
          expose :characteristic_type, documentation: { type: String, desc: 'Type of the characteristic' }
          expose :updated_at, documentation: { type: Date, desc: 'Last updated time of the characteristic' }
    
        end
      end
    end
    

    app/api/smart/entities/apps_params_entity.rb

    module Smart
      module Entities
        class AppsParamsEntity < Grape::Entity
    
          expose :os, documentation: { type: String, desc: 'Operative system name', values: App::OS_LIST, required: true }
          expose :characteristic_ids, using: CharacteristicsParamsEntity, documentation: { type: CharacteristicsParamsEntity, desc: 'List of characteristic_id that the customer has', is_array: true }
    
    
        end
      end
    end
    

    app/api/smart/entities/apps_entity.rb

    module Smart
      module Entities
        class AppsEntity < AppsParamsEntity
    
          unexpose :characteristic_ids
          expose :id, documentation: { type: 'integer', desc: 'Id of the created app', required: true }
          expose :customer_id, documentation: { type: 'integer', desc: 'Id of the customer', required: true }
          expose :characteristics, using: CharacteristicsEntity, documentation: { is_array: true, desc: 'List of characteristics that the customer has' }
    
        end
      end
    end
    

    app/api/smart/version1/apps.rb

    module Smart
      module Version1
        class Apps < Version1::BaseAPI
    
        resource :apps do
    
            # POST /apps
            desc 'Creates a new app' do
              detail 'It is used to register a new app on the server and get the app_id'
              params Entities::AppsParamsEntity.documentation
              success Entities::AppsEntity
              failure [[400, 'Bad Request', Entities::ErrorEntity]]
              named 'create app'
            end
            post do
              app = ::App.create! params
              present app, with: Entities::AppsEntity
            end
    
          end
    
        end
      end
    end
    

    这就是让它发挥作用的代码:

    config/initializers/grape_extensions.rb

    class Evaluator
      def initialize(instance)
        @instance = instance
      end
    
      def params parameters
        evaluator = self
        @instance.normal_params do
          evaluator.list_parameters(parameters, self)
        end
      end
    
      def method_missing(name, *args, &block)
      end
    
      def list_parameters(parameters, grape)
        evaluator = self
        parameters.each do |name, description|
          description_filtered = description.reject { |k| [:required, :is_array].include?(k) }
          if description.present? && description[:required]
            if description[:type] < Grape::Entity
              grape.requires name, description_filtered.merge(type: Array) do
                evaluator.list_parameters description[:type].documentation, self
              end
            else
              grape.requires name, description_filtered
            end
          else
            if description[:type] < Grape::Entity
              grape.optional name, description_filtered.merge(type: Array) do
                evaluator.list_parameters description[:type].documentation, self
              end
            else
              grape.optional name, description_filtered
            end
          end
        end
      end
    end
    
    module GrapeExtension
      def desc name, options = {}, &block
        Evaluator.new(self).instance_eval &block if block
        super name, options do
          def params *args
          end
    
          instance_eval &block if block
        end
      end
    end
    
    class Grape::API
      class << self
        prepend GrapeExtension
      end
    end
    

    这是示例的结果:

    【讨论】:

    • 我的意思是desc 块中的参数对grape-swagger 没有用,那么为什么还要尝试声明它呢?为什么要为 API 声明特定实体?但另一方面:伟大的工作。为什么不在拉取请求中将其添加到葡萄?
    【解决方案2】:

    我喜欢用于构建 API 的 grape/grape-swagger/grape-entity 组合。我通常使用葡萄实体来构建结果,而不是用于记录/验证 API。根据文档(对于葡萄实体)它应该可以工作,但我猜只是为了构建文档。

    根据葡萄documentation on parameter validation and coercion,它需要block 来强制执行任何验证/强制。

    [编辑:混合参数]

    您可以使用实体在desc 中定义参数,但要进行验证,您必须提供与desc 块相同级别的params 块,例如:

        # POST /app
        desc 'Creates a new app' do
          detail 'It is used to re gister a new app on the server and get the app_id'
          params  Entities::OSEntity.documentation
          success Entities::AppEntity
          failure [[401, 'Unauthorized', Entities::ErrorEntity]]
          named 'My named route'
        end
        params do
          requires :name, String
          optional :description, String
        end 
        post do
          app = ::App.create params
          present app, with: Entities::AppEntity
        end
    

    它们都称为params,但位置完全不同,功能也不同。
    我不确定desc 块是否除了文档还有其他用途(以及如何提取此文档对我来说有点神秘)。

    grape-swagger gem 不用,我典型的desc 长这样:

      desc "list of batches", {
        :notes => <<-NOTE
          Show a list of all available batches.
    
          ## Request properties
    
          * _Safe:_ Yes
          * _Idempotent:_ Yes
          * _Can be retried:_ Yes
        NOTE
      }
      params do
        optional :page, desc: 'paginated per 25'
      end
      get do
        present Batch.page(params[:page]), with: API::Entities::Batch
      end
    

    :notes 使用 markdown 呈现。这在 swagger-ui 中看起来如何

    【讨论】:

    • 但如果只是为了文档(swagger),那params Entities::OSEntity.documentation是什么意思?因为您应该始终以其他方式添加参数以进行验证。同样在params API::Entities::Status.documentation 下的葡萄文档中说“参数:直接从实体定义参数”
    • 我现在理解了混淆:有两个params 命令。所以实体定义了参数,但是在desc 块中。哪个恕我直言不用于验证,可能用于生成文档,但不是由grape-swagger gem。与desc 处于同一级别的params 块处理验证(并由grape-swagger gem 使用)。
    • 有一个功能请求可以部分解决这个问题:github.com/intridea/grape/issues/827
    • 最后我创建了一个 hack 来制作葡萄,按我应该的方式工作。看我的回答
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多