#ruby-on-rails #rest #api-design #ruby-on-rails-6
Вопрос:
В основном это вопрос дизайна api. У меня есть API Rails, в котором есть маршруты для пользователей и маршруты для школ. Я хотел бы сделать один вызов из моего интерфейсного приложения в api с параметром идентификатора пользователя, который возвращает все школы, связанные с этим пользователем.
Каков наилучший способ сделать это? Должен ли я создать новый маршрут в пользовательском контроллере под названием user-schools? Или новый маршрут в SchoolsController называется schools-user? Или создать совершенно новый контроллер под названием пользовательский интерфейс? Спасибо за любые рекомендации!
PS: Получение записей из ActiveRecord в контроллере не является проблемой. Проблема в том, как лучше всего спроектировать этот api.
Ответ №1:
Единственный способ определить это-через вложенный маршрут:
GET /users/:user_id/schools
Те же основные принципы проектирования применяются здесь для API и «классических» приложений.
Это можно определить, вложив вызовы в макрос ресурсов:
resources :users do
resources :schools, only: [:index]
end
Это приведет /users/:user_id/schools
к SchoolsController#index
. Пока вы можете «понюхать» для user_id
парама:
class SchoolsController
# GET /schools
# GET /users/1/schools
def index
schools = if params[:user_id].present?
user = User.find(params[:user_id])
user.schools
else
School.all
end
render json: schools
end
end
Более чистый дизайн заключается в использовании отдельного контроллера для вложенного контекста:
resources :users do
resources :schools, only: [:index], module: :users
end
module Users
class SchoolsController < ApplicationController
# GET /users/1/schools
def index
user = User.find(params[:user_id])
render json: user.schools
end
end
end
Этот контроллер выполняет только одну работу. Вы также можете назвать его UserSchoolsController
, но разделение контроллеров на папки (и пространства имен) упрощает их организацию.
Комментарии:
1. Отличный ответ. Альтернативный подход, который, я думаю, стоит упомянуть (и имеет плюсы/минусы вложенности), заключается в поддержке параметра запроса в главном контроллере, который фильтрует результаты (например
schools?user=123
, илиschools?user_id=123
).2. @melcher с точки зрения спокойного дизайна параметр запроса на самом деле не подразумевает, что существует связь между двумя ресурсами, и я бы действительно рассмотрел это только в том случае, если вам нужна одна конечная точка с множеством параметров фильтрации или если параметр на самом деле не является отношением, как, например
/schools?name="Zoolander"
.3. Полностью согласен с вашими соображениями, и это зависит от варианта использования и типа отношений. Мое предположение состояло в том, что пользователи были «связаны» со школой (например, HABTM), но это не было основным отношением, поэтому вложение не обязательно имело бы смысл, особенно если вам может потребоваться отфильтровать по другим отношениям.