RESTful routing

    В прошлой статье про роуты я много о чем еще не написал, но Iskin заметил, что я ничего не написал про RESTful routing. Это очень важная часть маршрутинга на рельсах. Общей картины о REST у меня не было, были лишь вырванные оттуда отсюда общие представления. Так что я решил взяться за это поосновательнее.

    Есть много литературы, но она наверняка не переведена. И есть пара скринкастов, которые я просмотрел:
    Скринкаст №35 «Custom REST Actions» Райана Бейтса
    Скринкаст №93 «RESTful Rails» Бала Паранжа — он более основательный и представляет собой часовую лекцию в университете. Еще у него индийский английский =).
    Отличный перевод статей по поводу REST

    ' />

    REST


    REpresentational State Transfer of Resources
    Простоты ради сформулирую так: это такая система организации ссылок. А ссылки — это запросы клиент-сервер. А запросы это в первую очередь HTTP метод!

    REST — это больше, чем просто системы ссылок, но практическое применение в рельсах это именно создание запросов и работа с ресурсами

    В былые времена мне и в голову не приходило указывать метод запроса в ссылке (в форме — дело другое, и то ограничивался GET и POST). Теперь в помощь нам мы имеем (конечно, они были и раньше) четыре HTTP метода: GET, POST, PUT, DELETE. Теперь главными являются не методы контроллера, но методы HTTP.

    Еще одна новость: для нас больше нет URL'ов. Теперь мы используем URI — Uniform Resource Identifier — ссылка на ресурс. Путь RESTful — указание URI и HTTP метода.

    К примеру, у нас есть ресурс — книги (books). Каждой книге соответствует его уникальный ID. Ссылка на удаление, просмотр и апдейт данных какого-нибудь экземпляра (c ID=1, скажем) будет выглядеть так:

    mysite/books/1

    Но как же программа поймет, какое из трех действий мы хотим выполнить? Вот здесь в помощь URI приходит HTTP method:

    GET mysite/books/1 — покажи нам информацию о книге с ID = 1
    POST mysite/books/1 — обновит информацию о книге
    DELETE mysite/books/1 — удалит книгу

    Рельсовая ссылка будет выглядеть (на удаление, скажем) так:

    <%= link_to 'Delete book', book_url(ID), :method => :delete %>

    Магия


    Подобные роуты берутся не из воздуха, а прописываются в конфигурационном файле routes.rb.

    #routes.rb
    map.resources :books

    Эта коротенькая строчка создаст для нас семь стандартных роутов:
    # HTTP    path_var           path            action
      GET     books_path         /books          index
      GET     book_path(id)      /books/id       show
      GET     new_book_path      /books/new      new
      POST    books_path         /books          create
      GET     edit_book_path(id) /books/id/edit  edit
      PUT     book_path(id)      /books/id       update
      DELETE  book_path(id)      /books/id       destroy

    Бала Паранж предлагает понимать RESTful роуты как предложения:
    Покажи мне список книг: GET books_path = GET /books
    Покажи мне форму редактирования для книги с ID=1: GET edit_book_path(1) = GET /books/1/edit
    Удали книгу 1: DELETE book_path(1) = DELETE /books/1
    Обнови данные для книги 1: PUT book_path(1) = PUT /books/1
    etc

    Как быть если мы хотим создать еще один метод. К примеру метод antique, который выдает нам список древних фолеантов из нашего каталога. В итоге мы хотим получить ссылку вида
    # HTTP    path_var            path            action
      GET     antique_books_path  /books/antique  antique

    Есть две опции для ресурсов: :member и :collection. В данном случае наш метод работает (возвращает) коллекцию — список книг (то есть много книг, хотя может не вернуть и ни одной), поэтому мы используем опцию :collection. Кроме того, так как antique _возвращает_ нам список, то мы должны использовать HTTP метод GET. Если это трудно понять, то можно действовать по аналогии: наш метод по сути делает то же самое, что и метод index — значит и HTTP методы у них должны быть одинаковыми.

    #routes.rb
    map.resources :books, :collection => { :antique => :get }


    Теперь создадим еще два метода:
    fire — сжечь все фолеанты
    repeblish — переиздать книгу
    Тогда роуты будут выглядеть так:

    #routes.rb
    map.resorces :books, :collection => { :antique => :get, :fire => :delete}, :member => { :republish => :put }


    # HTTP    path_var            path            action
      GET     antique_books_path  /books/antique  antique
      DELETE  fire_books_path     /books          fire
      PUT     book_path(id)       /books/id       republish

    Почему republish использует метод PUT? Потому что он обновляет данные (аналог update)
    Почему fire_books_path использует метод DELETE? Потому что он удаляет (аналог destroy)

    Разминка


    А теперь давайте посмотрим на следующие ссылки и подумаем почему они не RESTful и как их сделать RESTful

    1. GET /books/edit
    2. DELETE /books/1/update
    3. DELETE /books/1/destroy


    1. Если мы что-то редактируем, то мы обязаны указать ID (не забываем, что все — ссылка на ресурс! URI!). Поэтому правильно было бы написать
    GET /books/1/edit

    2. Для обновления данных мы используем метод PUT, а не DELETE. Коме того указание HTTP метода достаточно, поэтому указание на метод update — лишнее
    PUT /books/1

    3. Опять же указание метода контроллера — лишнее
    DELETE /books/1

    А теперь давайте обсудим чего здесь не хватает =)
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 14

      0
      интересно было бы посмотреть на список проксей и другого ПО, которые не пробросят delete & put запросы наружу…
      есть ли другой способ, например /books/id/del or something like this?
        0
        Да, никто не мешает пользоваться стандартными конструкциями вроде /books/id/destroy — но это просто вне идеологии rest
          +1
          на самом деле /books/destroy/id

          Но так как http-методы эти не реализованы до конца, то они эмулируются посредством скрытых форм с полем method.
            +1
            Можно даже /destroy/id/books
            Это смотря как настроишь +)
          0
          Метод передается как параметр, т.е. метод DELETE выполняется как POST запрос с дополнительным параметром method=delete. Это уже дело фреймворка обработать его как специальный метод.

          Важно помнить, что все запросы выполняющее изменение, должны выполняться через POST.
          +1
          Хех, я тогда еще ничего не написал про REST, почему-то будучи уверенным, что автор так и сделает отдельную статью по теме :) Ибо, REST — это действительно отдельная тема для рассмотрения.
            +2
            Добавлю ссылку на серию статей по теме: www.taknado.com/2007/8/31/rest-on-rails

            Это перевод на русский язык подробного описания REST, начиная от причин возникновения и заканчивая реализацией в рельсах.
              0
              Спасибо!
              Вынесу в топик.
              +1
              А еще есть отличные посты по REST от Миши Клишина, жаль он перестал вносить вклад в познание Rails для русскоговорящих. Это было действительно интересно и познавательно.
                0
                К сожалению репозиторий со статьями не доступен svn://www.novemberain.com/svn/articles/resources-on-rails

                  0
                  У меня есть копии, но без разрешения самого Михаила я не могу их опубликовать. Тем более он сам закрыл свой ресурс, видимо на это есть причины.
                    0
                    Пойду просить почитать
                +1
                «POST mysite/books/1 — обновит информацию о книге»
                Наверно PUT, а не POST.
                  0
                  «repeblish — переиздать книгу» наверно repUblish
                  хорошая статья…

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое