Важные аспекты RESTful API для вашего проекта

    Данная статья давно назревала в моей голове, но совсем в ином формате.
    Прочитав последние несколько неуклюжих статей на тему WEB-сервисов (например: http://habrahabr.ru/blogs/development/108973/) и применения в них REST технологии, решил что настало время все-таки откинуть лень, выделить время и написать «переформатированную» в своей голове статью.
    Итак, кратко, что Вы найдете в статье и кому она будет полезна:

    новичкам, которые интересуются или планируют написать WEB-сервис для проекта
    профи вряд ли найдут что-то новое для себя
    — общая идеология REST
    — применение CRUD в WEB-сервисах
    — принципы KISS при построении раутеров
    — лучшие практики
    — немного пиара ;)
    — ссылки, литература

    С чего начать решать вам, я порекомендую сперва дочитать статью, затем еще немного погуглить, выпить чашечку чаю/кофе/нужное_подчеркнуть, разложить мысли по полочкам и затем приступать к созданию вашего эй-пи-ай.

    ОБЩАЯ ИДЕОЛОГИЯ


    Что же такое REST *(1)?
    REST — это “Representational State Transfer”, другими словами — представление данных в удобном для клиента формате, под клиентом мы подразумеваем клиентское ПО из модели client <-> server.
    В свою очередь веб-сервисы созданные с использованием протокола HTTP и принципов REST называются RESTful web service.
    У непосвященного сразу же может возникнуть вопрос: зачем нужны подобные сервисы? Приведу простой пример на основе REST и OAuth технологий: у Вас есть аккаунт на gmail и аккаунт на dropbox. Дропбокс предлагает Вам дополнительное место за каждого приглашенного друга, однако писать письмо вручную и перебирать все адреса из адресной книги gmail — не просто нудно, но и долго. Дропбокс предлагает “давай, дружище, я сам импортирую контакты и разошлю приглашения”. Понятное дело, что никто из Вас не захочет давать свой пароль от ящика на гмыле дропбоксу. Тут в силу вступает “магия” RESTful веб-сервисов. Используя API (эй-пи-ай, а не “апи”, как многие привыкли произносить), дропбокс делает запрос к веб-сервису гугля, затем “отправляет” Вас на страницу авторизации гугля, таким образом вы вводите свой пароль в форме у гугла, гугл вас авторизует и дает знать (выдает access_token) дропбоксу об успешности auth-процесса (authentication). В конце концов, имея уникальный токен (access_token) и прикрепляя его к каждому запросу в API, дропбокс имеет возможность работать с Вашими персональными данными, не зная ничего ровным счетом о Вашем пароле.
    Важно помнить, что REST — это не обязательно использование протокола HTTP, хотя и изначально архитектура разрабатывалась с применением именно HTTP. В REST может использоваться любой протокол прикладного уровня (application layer) для транспортировки данных.

    CRUD


    При разработке RESTful веб-сервисов важно помнить о методологии CRUD *(2), что в расшифрованном переводе означает: создать-прочитать-изменить-удалить (create-read-update-delete).
    Так как разработка веб-сервисов ведется с использованием HTTP протокола, то и использовать нужно его плюшки, а именно: используйте методы протокола для определения действия над ресурсом. В мире REST общеприняты следующие стандарты:
    POST — create (создать)
    GET — read (прочитать, получить)
    PUT — update (изменить, обновить или создать, если не существует, иногда применяется и в таком варианте)
    DELETE — delete (удалить)
    Необходимо отметить, что PUT и DELETE сложно реализуемые методы и ввиду особенностей браузеров, данным методы реализовать “по-человечески” средствами AJAX на стороне клиента-браузера, к тому же еще и чтобы работало во всех браузерах, не представляется возможным. Поэтому принято данные методы использовать посредством “перегрузки” метода POST. Другими словами: Вы отправляете с POST-методом какую-нибуть зарезервированную переменную, например: http_method, с именем метода, который Вы перегружаете. POST + http_method=put — дает понять вашей серверной части, что клиент запросил PUT над ресурсом.

    ПРИНЦИПЫ KISS ПРИ ПОСТРОЕНИИ РАУТЕРА


    Для начала расшифруем KISS — keep it small and simple, для тех кто еще не понял — придерживайтесь простых и маленьких вещей, другими словами чтобы это было доступно и понятно каждому и легко запоминалось.
    Чтобы получить данные от вашего веб-сервера, клиенту необходимо знать URL, по которому ему необходимо обратиться. О принципах “построения” этих самых универсальных ресурсных указателей и пойдет речь.
    Первым делом определите базовый набор правил, который подходит Вашему сервису. Например, у себя в компании oDesk мы обозначили, что URI, база которых — /api, это ресурсы RESTful API. При желании свой эй-пи-ай, Вы можете вынести на отдельный субдомен, здесь стоит помнить о кросбраузерных запросах и других подводных камнях, которые Вам встретятся, даже при запросах “внутри” одного домена.
    Следующим моментом будет создание раутера ресурса — адреса, по которому можно будет обратиться к данным. Отметим несколько моментов, которые считаются хорошей манерой при составлении адреса:
    — разбиение на идиоматические группы: mc — message center ресурсы, team — коммандные ресурсы и тд
    например: /api/mc/…
    — версионирование
    например: /api/mc/v1/…
    — ясное название ресурса, к которому предоставляется доступ
    например: /api/mc/v1/prefs/… — доступ к настройкам мессадж центра
    — внесение персистентных данных под URL
    например: /api/mc/v1/prefs/mydevelopersuid
    — определение формата ответа: либо в названии урл-е раутера, либо в “системной” переменной
    например: /api/mc/v1/prefs/mydevelopersuid.json — oDesk использует суффикс “.json” когда требуется ответ в формате JSON, .xml — в формате XML и тд.; /mc/v1/prefs/mydevelopersuid?tqx=out:json — так делает Гугль, в GDS; /api/json/mc/v1/prefs/mydevelopersuid — просто еще один возможный вариант, формат является частью раутера.

    ЛУЧШИЕ ПРАКТИКИ


    Здесь можно много и долго говорить, перечислю основные моменты:
    — всегда предоставляйте возможность получения ответа в формате json. Так случилось, что в мире AJAX приложений JSON *(3) стал де-факто “стандартом” передачи данных между клиентской и серверной частью.
    — используйте для авторизации проверенные и отработанные схемы
    Не изобретайте свой самокат с использованием md5 подписей данных и прочего, доверьтесь профессионалам в области защиты данных. OAuth придумали за Вас, для него полно библиотек на разных языках — используйте эту мощь. Не хотите OAuth, попробуйте APIKeys как это реализовано у Flickr-а. Есть еще парочка вариантов — OpenID и другие, всех рассматривать не буду — суть я донес.
    — не забывайте о https, там где это нужно
    и, пожалуйста, не забывайте к документации к своему эй-пи-ай указывать, что ваш сертификат самописный, если он таковым является. Цените время коллег из соседнего цеха, чтобы они не искали баги в своих апликухах только потому, что там стоит проверка на валидность сертификата.
    — никогда не “вываливайте” в ответе что-то невнятное: ошибки Вашего веб-сервера, фаталы, проблемы соединения с базой и тд. Все это должно обрабатываться Вашим RESTful веб-сервером и отдавать внятный ответ в запрошенном формате. Другими словами, предоставьте внятный error responding клиенту. Формат ответа и заголовки — это отдельная тема. Хорошей практикой считается отдача корректных HTTP статусов в соответствии с RFC 2616 *(4) и если статус позволяет отдавать тело сообщения — наличие краткого сообщения в теле. Некоторые предпочитают отдавать всегда статус “200 ОК” с телом, таким образом клиент должен всегда парсить сообщение и проверять тело на наличие в нем сообщения/тега об ошибке. Однозначной практики и общего мнения пока нет!
    — пишите документацию к своему API, иначе эй-пи-ай безполезно :)
    — и, наконец, изначально продумайте внутреннюю архитектуру RESTful веб-сервиса, логика должна быть едина для любого запрошенного формата, а преобразование данных должно производится для каждого формата отдельно. Это позволит Вам легко и быстро расширять Ваши раутеры при необходимости добавить новый стандарт отображения данных.

    PR


    будучи основным разработчиком RESTful API в компании oDesk (API — developers.odesk.com — это пиар, берите, пользуйтесь на здоровье! :) ) и занимаясь REST сервисами длительное время, считаю, что было уместно поделиться базовыми знаниями в данном направлении. Верить или не верить мне — Ваше личное право, но все же глубоко надеюсь, что каждый прочитавший хоть малую но пользу для себя найдет, как минимум освежите знания ;)

    Архитектура REST все еще оставляет много вопросов и не редко приводит к холиварам, не обессудьте если мое мнение в чем-то не совпало с Вашим.

    P.S. статья охватывает только базовые аспекты и не претендует на детальную инструкцию, книгу и тд для создания веб-сервисов.

    ССЫЛКИ*


    (1) http://en.wikipedia.org/wiki/Representational_State_Transfer
    (2) http://en.wikipedia.org/wiki/Create,_read,_update_and_delete
    (3) http://en.wikipedia.org/wiki/JSON
    (4) http://www.w3.org/Protocols/rfc2616/rfc2616.html
    Литература: RESTful Web Services (Leonard Richardson, Sam Ruby), O'Reilly Media, Inc.
    Yahoo REST Group: http://tech.groups.yahoo.com/group/rest-discuss/
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 47
    • –1
      Кратко. Ёмко. Ясно. Спасибо, что хоть кто-то разложил по полочкам более-менее.
      • НЛО прилетело и опубликовало эту надпись здесь
        • +3
          Мне кажется, это самая правильная расшифровка :)
          • +1
            Авторская версия, тем не менее, звучала как «Keep it simple and stupid», что придавало несколько другую окраску.
        • +2
          PUT и DELETE достаточно просто реализуются “по-человечески” средствами AJAX на стороне клиента-браузера, не стоит пугать новичков (во всяком случае во всех актуальных браузерах). К тому же у многих популярных фреймворков есть соответствующие методы.
          • 0
            Зато если захотите помимо AJAX клиента подключиться клиентов на другой технологии (например Flash) то столкнетесь с этими граблями — на родном API флеш не умеет слать PUT/DELETE HTTP запросы и задача обычно решается как раз указанной в статье перегрузкой POST запроса. Так что лучше поддержать фичу на сервере заранее, чем прикручивать потом )
            • 0
              Опередили :)
              добавлю к этому еще, что необходимо чтобы еще и веб-сервер (Apache, lighttpd, или что там стоит) поддерживал соответствующий метод
              попробуйте хостера убедить открыть эти методы, да и на своих серверах открывать нужно о-о-очень аккуратно и грамотно, дабы не сделать дырку в безопасности сервака ;)
              • +1
                Browser Plugin API предоставляет только GET/POST, поэтому Flash как бы ни хотел, но PUT/DELETE не сможет…
                Если только собственный HTTP stack задейтсвует. Но в этом случае, прощайте браузерные куки.
          • –4
            Хоть REST используют в основном когда пишут на ява, но вот неплохая статья для пхпистов
            www.lornajane.net/posts/2008/Accessing-Incoming-PUT-Data-from-PHP
            И firefox RESTClient addons.mozilla.org/en-US/firefox/addon/9780/ лично мне очень помогал в разработке.
            • +3
              «Хоть REST используют в основном когда пишут на ява, но вот неплохая статья для пхпистов» — пруфлинт на статистику в студию
              по мне — смутное утверждение
              • 0
                есть мысль, что репозиторий пакетов для CMS MODx пишется как раз таки с соблюдений REST-архитектуры. Документации пока нету подробной, но то, что мне удалось раскопать, указывает на этот факт. Это к теме PHP и иже с ним.
              • 0
                Подобный экстеншн для хром — chrome.google.com/extensions/detail/fhjcajmcbmldlhcimfajhfbgofnpcjmb
                • 0
                  >Хоть REST используют в основном когда пишут на ява,

                  Полный бред. Технология эта не привязана к какому-то конкретному языку. И нет никакой проблемы сделать тоже самое на PHP, Ruby, .NET или еще где.
                  • 0
                    Да рельсы вообще практически «искаропки» работают с REST. Там даже скаффолдер маршруты и контролеры создаёт REST-ориентированные.
                    • 0
                      аналогично symfony, кодогенератор и автоматический роутинг для CRUD «REST-совместимый»

                      А вообще 99% совместимости урлов с REST для нормально спроектированного приложения, имхо, можно решить с помощью mod_rewrite и аналогов в других серверах. Сложнее с HTTP-методами
                • +15
                  (эй-пи-ай, а не “апи”, как многие привыкли произносить)

                  дропбокс делает запрос к веб-сервису гугля

                  «гугла», а не «гугля», как многие привыкли произносить…
                  • +4
                    Тогда уж «гугл» :)
                    • –4
                      соглашусь с вариантом «гугл», но «гугла» ничем не отличается от «гугля» — в англ нет склонений окончаний :)
                    • +3
                      Я тоже не понял, почему автора так затронуло слово «апи», когда в статье полно слов типа «гугля», «апликуха» и т.д.
                  • +6
                    Спасибо за информацию. Все–таки отмечу употребление терминов. Можно сделать текст гораздо более удобным для чтения, «раутер» — путь, «эй–пи–ай» — программный интерфейс или интерфейс, «мессажд» — сообщение. Если вы пишите «эй–пи–ай», то почему дальше по тексту HTTP, CRUD и другие, вместо аш–ти–ти–пи, си–эр–у–д?! А кое–где встречается API, а сразу за ним эй–пи–ай… Это небольшие замечания к оформлению, а не к сути, но из–за них хотелось поскорее прокрутить текст вниз, посмотреть список литературы и отправиться читать про сервисы в другом изложении.
                    • –4
                      спасибо за конструктивный комментарий, есть такой грешок — разбавлял статью, чтобы не была унылой, возможно выбрал неудачный способ, но после боя руками не машут
                      • 0
                        Всё вы сделали правильно.
                      • 0
                        это произносится как «эйч-ти-ти-пи» и «крад» соответственно.
                        • 0
                          си-ар-ю-ди
                        • 0
                          Сам спрошу и сам отвечу:
                          В: Для определения типа ответа, заголовок Accept является более идеологически верным способом чем «расширение», так?
                          О: Так, но возможно что не все клиенты будут полноценными (см POST vs PUT), т.е. не смогут передать нужный заголовок. Плюс теряется возможность отладки на скорую руку с помощью браузера.
                          • 0
                            все верно, есть еще уйма плюшек и заголовки в RESTful сервисах — это отдельная тема, в статье освешены только базовые аспекты
                            • +4
                              Думаю стоит также отметить еще один из основных идеологических принципов: URL — ресурс, объект. Метод — действие.
                              Примеры (верно != неверно):
                              POST /user != POST /user/add
                              GET /user/123 != GET /user/list/123
                              PUT /user/123 != PUT /user/edit/123
                              DELETE /user/123 != DELETE /user/delete/123
                          • 0
                            Спасибо.
                            Добавлю про метод HEAD, который, по идее, нужен для получения краткой информации об объекте.
                            Например, в REST-API для Amazon S3 HEAD используется, чтобы получить метаинформацию о файле, не скачивая его целиком.
                            • +3
                              Для быстрого старта в свое время мне помогла страничка microformats.org/wiki/rest вообще и microformats.org/wiki/rest/urls в частности

                              • 0
                                приведенная вами статья для примера — не REST.
                                • 0
                                  громкое заявление, но лишенное смысла
                                  просветите присутствующих?
                                  • 0
                                    Я описал построение элементарного API, реализуемого минимумом кода.
                                    Я не говорю, что вы не правы или REST это плохо. Но некоторые его особенности плохо вкладываются в предметную область — собственно выше это уже отметили. И самая его вкусная часть — PUT/DELETE не реализуема из флеша сотоварищи.
                                    Велосипедная авторизация у меня тоже была диктована предметной областью — мой пример вырван из SaaS системы. Естественно он является вырожденным и совсем не общим.
                                    Предлагаю не начинать войны — мы оба правы.
                                • 0
                                  Дальнейшее логическое развитие этого API — odata.org
                                  • 0
                                    да, что-то вроде этого и есть.
                                    • 0
                                      Может во мне бурлят «пережитки прошлого», но когда я слышу/вижу рядом «формат» и «Microsoft», то заранее содрогаюсь
                                      • –1
                                        У меня, возможно, бурлят пережитки настоящего, содрогаюсь, когда вижу «Google». Но это не мешает мне пользоваться их поиском, картами и ютюбиком :)
                                        • –1
                                          Гугл не потребует от вас прекратить использовать поиск и карты и заплатить за «недополученную прибыль», а вот от MS подобные прецеденты были за использование их форматов.
                                          • 0
                                            AtomPub/Rss/Odata — это открытые форматы.

                                            ЗЫ. Кроме того, МС блох никогда не ловил. Только крупных конкурентов.
                                    • –1
                                      «раутер» — рунглиш тут по круче суржика будет.
                                      про POST с PUT — ложь и провокация.
                                      и хватит уже википедию переносить. напишите на худой конец о своей реализации, получите и PR и конструктивную критику. а так…
                                      • 0
                                        > REST — это “Representational State Transfer”, другими словами — представление данных в удобном для клиента формате

                                        это настолько «другие» слова, что они окончательно потеряли связь с действительностью.
                                        • 0
                                          Сравнил написанное здесь, с тем что было написано в предыдущей «неуклюжей» статье, которая указана в первом абзаце. И, честно, я не нашёл сильно принципиальных отличий. Может быть только одно.

                                          1. Использование GET/POST/PUT/DELETE — уже написали выше, что это не подходит. Придётся использовать 'http_method' (в предыдущей статье это 'cmd').

                                          2. Использовать URI (/api/mc/v1/) вместо GET-параметров — единственное принципиальное отличие. Хотя (по моему) это не KISS (ИМХО). Потому что надо дополнительно делать rewrite_rule + парсить строку. А вот так не надо ничего лишнего: '/api?cmd=mc.v1.prefs.mydevelopersuid' — это ближе к KISS.
                                          Ну и команда «mc» — совсем не юзабельно. Без документации не разберёшься. Надо переименовать в messageCenter.

                                          3. OAuth я использовать вообще не могу, потому что пишу сервер для full-flash сайта на котором юзеры могут регистрироваться и авторизовываться как на html-сайте через формы.

                                          4. > error responding клиенту на уровне HTTP статуса. «200 OK».
                                          Видимо, это относится только к внутренним ошибкам скрипта. Например, при регистрации юзера, я могу вывалить сразу список ошибок: 150 и 170 — логин занят и мыло занято. То есть ошибки придётся отдавать в теле. И это тоже из предыдущей статьи.

                                          Не спорю, что статья хорошая и есть над чем подумать. Я уже запланировал несколько правок в своё эй-пи-ай. Но предыдущего оратора Вы зря обидели.
                                          • 0
                                            1. http_method нужно использовать только для перегрузки указанных методов, а не постоянно, соответственно сравните GET /api/mc/v1/prefs/mydevelopersuid.json и GET /api?cmd=mc.v1.prefs.mydevelopersuid&data=json&version=v1…
                                            2. я привел ссылки на книги, группы, вики, Вы можете погуглить и поинтересоваться на эту тему — споров много, но то как я указал — это хорошие манеры в REST
                                            3. «на котором юзеры могут регистрироваться и авторизовываться как на html-сайте через формы» — не понял каким боком это здесь, чем это OAuth при этом мешает? ну вводите данные с html-формы, в чем затык?
                                            4. что такое 150, 170 — что за коды? да rfc 2616 не запрещает 1хх, но явно указывает на его лимитирование: не использовать для http/1.0, если 150 и 170 — это Ваши статус коды, то Вы заведомо себя ограничили, как ни крути, а http/1.0 еще много где используется. В данном случае я бы возвращал «409 Conflict», который на мой взгляд больше подходит к ситуации: «The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request.»

                                            P.S. если речь идет о REST, давайте говорить о нем, в статье описаны базовые принципы и общепринятые практики, отработанные в компаниях с мировыми именами и разработанные при участии известных личностей. Если у Вас есть весомые аргументы, добро пожаловать в группу на яху, там всегда готовы к конструктивному диалогу.
                                            • 0
                                              1. Я правильно понимаю, что Вы предлагаете реализовать оба варианта и в документации к API написать: «есть два варианта: ....»?

                                              3. > в чем затык?
                                              Да он мне на фиг не нужен. Мой сайт не предоставляет API для третих сервисов.

                                              4. Представьте, юзер заполнил регистрационную форму во flash. Flash POST-ом отправил на сервер дюжину полей. В процессе валидации я обнаружил 3 ошибки (логин занят, е-майл занят, пароль слишком короткий). Если я верну «409 Conflict», то как flash поймёт сколько и какие ошибки? Никак. По этому, я изобретаю и в документации описываю свои коды ошибок и говорю, что в ответе их может быть несколько. Например:

                                              {
                                              errors:{ 150, 170, 250 }
                                              }

                                              так клиентский флеш понимает и показывает юзеру сразу несколько сообщений об ошибках.

                                              Статья хорошая. Но мы с Вами смотрим с разных сторон. Я через свою призму flash-сайтов. А Вы через призму межсервисной интеграции. Отсюда разная специфика.
                                              • +1
                                                Просто не надо пытаться все ошибки разрулить средствами HTTP. Оставьте ему ошибки общего характера (403, 404, 405, 406, 500 и т.д.), а прикладные ошибки возвращайте своими кодами и описаниями с HTTP кодом 200
                                          • НЛО прилетело и опубликовало эту надпись здесь
                                            • 0
                                              Table 1: Relationships Between SQL and HTTP Verbs

                                              Action SQL HTTP
                                              Create Insert PUT
                                              Read Select GET
                                              Update Update POST
                                              Delete Delete DELET

                                              www.oracle.com/technetwork/articles/javase/index-137171.html

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