确保API是独立的

时间:2011-01-10 08:45:11

标签: ruby-on-rails

TL; DR:制作API。需要针对不同版本的不同字段。教导我,聪明的人。

我目前正在尝试找出制作版本化API的最佳方法。也就是说,我希望有一个/api/v1/projects.json的URL,它将显示包含大量字段的项目列表,并api/v2/projects.json显示包含单独字段的项目列表。

我一直在考虑这个问题大约15分钟,这可能意味着一切都错了。目前我已经在app/models/project.rb文件中找到了这个:

def self.api_fields
  { 
    :v1 => ["name"],
    :v2 => ["name", "tickets_count"]
  }
end

然后我可以在我的API控制器(api/v1/projects_controller.rb)中使用它,如下所示:

def index
  respond_with(Project.all(:select => Project.api_fields[:v1]))
end

这很棒,按照我的意愿行事,但可能有更好的方法。那是你的任务!与我分享您的API制作智慧之山。

如果您想出一个解决方案,也可以让我使用方法来处理模型对象的实例,例如tickets_count方法上的Project方法,那么

奖励积分。

4 个答案:

答案 0 :(得分:1)

正如评论:

你有看过这些吗?

http://devoh.com/posts/2010/04/simple-api-versioning-in-rails

Best practices for API versioning?

devoh.com建议在路由级别拆分版本,这似乎是一个好主意:

map.namespace(:v1) do |v1|
  v1.resources :categories
  v1.resources :products
end

map.namespace(:v2) do |v2|
  v2.resources :categories, :has_many => :products
end

然后你可以使用不同的控制器来返回不同的字段。

答案 1 :(得分:1)

我同意polarblau您应该为不同版本的API提供多个控制器。所以,我的目标是这个问题的奖励点。

我认为要归档调用#tickets_count的功能,您必须覆盖模型的#as_json#to_xml方法。我想你必须这样做:

<强> API / V1 / projects_controller.rb

def index
  respond_with Project.all, :api_version => :v1
end

<强> project.rb

class Project < ActiveRecord::Base
  API_FIELDS = {
    :v1 => { :only => [:name] },
    :v2 => { :only => [:name], :methods => [:tickets_count] }
  }

  def as_json(options = {})
    options.merge! API_FIELDS[options[:api_version]]
    super
  end

  def to_xml(options = {}, &block)
    options.merge! API_FIELDS[options[:api_version]]
    super
  end
end

但是,如果你不介意控制器中的混乱,我认为在控制器的:only调用中指定:methodsrespond_with也可能是一个好主意,因为你不必覆盖那些#as_json#to_xml方法。

答案 2 :(得分:0)

问题是,如您所知,无论您公开什么,都允许最终客户端创建直接依赖关系。话虽如此,如果您将模型直接暴露给世界,例如http://domain.com/products.json,无论何时更改产品型号,您都可以选择有限的选项:

  1. 最终客户端必须与它一起使用,其行为与“无模式数据库”非常相似。你说它会改变,瞧它已经完成了(读取客户将不得不处理它)!
  2. 您在API中添加了类似企业版的版本。这是一种测量,在更高级别,您向最终客户端公开的不是您的模型。相反,您可以公开公共对象,而公共对象又可以进行版本控制。这称为数据传输对象(http://en.wikipedia.org/wiki/Data_transfer_object)
  3. 如果我们希望采用第二种方法,我们可以做到以下几点:

    class Project < ActiveRecord::Base
    end
    
    class PublicProject
      def to_json(version = API_VERSION)
        self.send("load_#{version}_project").to_json
      end
    
      private
        def load_v1_project
          project = load_v2_project
          # logic that transforms a current project in a project that v1 users can understand
        end
    
        def load_v2_project
          Project.find...
        end
    end
    

    希望它有所帮助。

答案 3 :(得分:0)

在/ api / v1的路由中安装Sinatra应用程序以处理您的API调用。使您更容易添加新API,并且在您弃用它之前仍然可以向后兼容。