2011-01-10 2 views
4

TL; DR: Создание API. Нужны разные поля для разных версий. Научите меня, мудрые.Определяющие 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 

Это замечательно и работает как я D нравится, но есть, вероятно, лучший способ. Это твоя задача! Поделитесь со мной своими горами API-разработки мудрости.

Бонусные баллы, если вы придумали решение, которое также позволит мне использовать методы для экземпляров объекта модели, например метод tickets_count по методу Project.

ответ

1

Я согласен с 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 

Однако, если вы не» t разум беспорядок в контроллере, я думаю, что указание :only и :methods в respond_with вызов в контроллере тоже может быть хорошей идеей, так как вам не нужно переопределять thos e #as_json и #to_xml методов.

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 

Затем вы можете использовать различные контроллеры для возврата различные поля.

+0

Это то, что я уже делаю, используя разные контроллеры, чтобы возвращать разные поля. Думаю, эта часть я определенно прибил. Вопрос в том, что «это правильный способ сделать это?». –

+0

Чувствую себя хорошим способом для меня, но именно поэтому я начал отвечать «Просто комментарий»: - Я недостаточно уверен в своей мудрости;). – polarblau

+0

У вас также должна быть структура каталогов для ваших контроллеров API, таких как 'app/controllerlers/api/v2/categories_controller.rb'. – yfeldblum

0

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

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

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

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 

Надежда помогает.

0

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

+0

Это для Rails 3 в действии. По этой причине я не хочу использовать Синатру. Я думаю, что Rails должен быть способен сделать это просто отлично. –