Обеспечение отдельных API - PullRequest
4 голосов
/ 10 января 2011

TL; DR: создание API.Нужны разные поля для разных версий.Научи меня, мудрые.

В настоящее время я пытаюсь найти лучший способ создания версионного API.То есть я хочу, чтобы URL-адрес /api/v1/projects.json отображал список проектов с несколькими полями, а 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 ]

1 голос
/ 10 января 2011

Я согласен с polarblau в том, что у вас должно быть несколько контроллеров для разных версий API. Итак, я стремлюсь к бонусному баллу за этот вопрос.

Я думаю, что для архивации возможности вызова #tickets_count необходимо переопределить #as_json и #to_xml методы модели. Я думаю, тебе придется сделать это так:

апи / 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 и :methods в respond_with вызове в контроллере также может быть хорошей идеей, поскольку вам не нужно переопределять эти #as_json и #to_xml методы.

1 голос
/ 10 января 2011

Просто в качестве комментария:

Вы уже посмотрели это?

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

Рекомендации по версионированию API?

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

Тогда вы можете использовать разные контроллеры для возврата различных полей.*

0 голосов
/ 10 января 2011

Смонтируйте приложение Sinatra на маршрутах в / api / v1 для обработки вызовов API. Упрощает добавление нового API и совместимость с предыдущими версиями, пока вы не устареете.

0 голосов
/ 10 января 2011

Проблема, как вы знаете, в том, что все, что вы выставляете, позволяет конечному клиенту создавать прямую зависимость.Сказав это, если вы непосредственно представляете свои модели миру, например, http://domain.com/products.json, каждый раз, когда вы меняете модель своих продуктов, у вас есть ограниченное количество вариантов:

  1. Конечный клиент должен житьс ним и ведут себя во многом как «база данных без схемы».Вы говорите, что это изменится, и вуаля, это сделано (читает клиенты должны будут иметь дело с этим)!
  2. Вы добавляете более корпоративную версионность в свой API.Это означает, что на более продвинутом уровне то, что вы предоставляете конечному клиенту, не является вашими моделями.Вместо этого вы выставляете открытые объекты, которые, в свою очередь, могут быть версионными.Это называется объектом передачи данных (http://en.wikipedia.org/wiki/Data_transfer_object)

. Если бы мы хотели использовать второй подход, мы могли бы сделать следующее:

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

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...