Дизайн API Rails

У меня есть сайт rails, который в основном основан на REST, и я хочу добавить поддержку JSON API.

Для чистой базы кода я должен добавить эту поддержку в существующие контроллеры или создать новые контроллеры, которые обрабатывают только эти методы API, а затем переместить весь общий код в модели/помощники?

Ответ 1

Я использовал оба метода: написание логики API в тех же контроллерах и создание отдельных контроллеров для запроса API.

Если это только API, небольшое приложение, используемое только вами, переходите к стандартным отношениям модели контроллера, которые предлагает рельсы. Код будет довольно чистым. Файл маршрутов также будет чистым.

Если у вас есть веб-сайт, и вы хотите построить API, сделайте это отдельно. Я построил его вместе с существующими контроллерами, и код был слишком запутанным. Я несколько раз обновлял код, но мне все равно не понравилось (это тоже вопрос личного вкуса).

Другим решением является создание контроллеров с префиксом. Пример: ApiUsersController. Это заставит ваш файл routes.rb выглядеть уродливым, потому что вам нужно вручную указать маршрут в соответствии с соответствующим методом контроллера.

Рабочее решение для меня состояло в том, чтобы переместить всю логику API в отдельные контроллеры в пространстве имен API. Пространства имен также позволяют вам управлять версиями API. Так, например, ваши маршруты будут:

GET /api/v1/users.json
POST /api/v1/users.json

И тогда в будущем вы можете создать другую версию API, скажем v2, не нарушая существующие приложения, которые используют более старую версию API.

Здесь вы можете найти больше о пространствах имен: http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing

Удивительный учебник по REST-full API с версиями: http://railscasts.com/episodes/350-rest-api-versioning?view=asciicast

Ответ 2

Генераторы контроллеров Rails по умолчанию выполняют JSON-ответы.

Например, если у вас есть этот метод:

class UsersController < ApplicationController
  def index
    @users = User.all
  end
end

Вы можете добавить ответ JSON так:

class UsersController < Application Controller
  def index
    respond_to do |format|
      format.html
      format.js { render :json => @users }
    end
  end
end

Теперь у вас есть два ответа для /users

  • http://someapp.com/users
  • http://someapp.com/users.json

Вы можете добавить еще один очень легко; например.

format.xml { render :xml => @users }

Теперь ваше приложение ответит на http://someapp.com/users.xml


Настройка json

Скорее всего, вы не захотите выводить все поля таблицы в json. Для этого посмотрите rails/jbuilder. Он позволяет создавать структуры JSON со встроенным DSL-модулем.

Пример из jbuilder README

Jbuilder.encode do |json|
  json.content format_content(@message.content)
  json.(@message, :created_at, :updated_at)

  json.author do
    json.name @message.creator.name.familiar
    json.email_address @message.creator.email_address_with_name
    json.url url_for(@message.creator, format: :json)
  end

  if current_user.admin?
    json.visitors calculate_visitors(@message)
  end

  json.comments @message.comments, :content, :created_at

  json.attachments @message.attachments do |attachment|
    json.filename attachment.filename
    json.url url_for(attachment)
  end
end

Производит следующий вывод:

{ 
  "content": "<p>This is <i>serious</i> monkey business",
  "created_at": "2011-10-29T20:45:28-05:00",
  "updated_at": "2011-10-29T20:45:28-05:00",

  "author": {
    "name": "David H.",
    "email_address": "'David Heinemeier Hansson' <[email protected]>",
    "url": "http://example.com/users/1-david.json"
  },

  "visitors": 15,

  "comments": [
    { "content": "Hello everyone!", "created_at": "2011-10-29T20:45:28-05:00" },
    { "content": "To you my good sir!", "created_at": "2011-10-29T20:47:28-05:00" }
  ],

  "attachments": [
    { "filename": "forecast.xls", "url": "http://example.com/downloads/forecast.xls" },
    { "filename": "presentation.pdf", "url": "http://example.com/downloads/presentation.pdf" }
  ]
}