Comments 128
Огромное спасибо!!! Всё по делу и всё понятно, без лишних обдумываний! +100500!
+10
Давно прочитал эту книжку, очень полезная (спасибо одному польз. хабра за рекомендацию ее =) ), и кстати он Brian Mulloy, а не Malloy. :)
+2
Кое-что новое для себя узнал, благодарю за статью.
Сам до сих пор грешу подобным, когда админскую часть пишу =/
Существительные — это хорошо, а глаголы — плохо
/getAllDogs
/getAllLeashedDogs
/getDog
/newDog
/saveDog
Сам до сих пор грешу подобным, когда админскую часть пишу =/
0
не знаю, далек от проектирования Web API, но вариант с разными запросами кажется мне гораздо менее понятным, чем такой список функций, или я неправ?
0
Это же ООП получается — есть интерфейс «сущность», с четырьмя методами(POST, GET, PUT, DELETE) и дальше вы просто приводите список имплементаций интерфейса.(dog, cat, carrot)
А кейс с newDog/deleteCat похож на c-like API
А кейс с newDog/deleteCat похож на c-like API
+1
А есть метод «SORT»? А есть метод «CHECK»? Мне всегда было непонятно, почему все считают, что ограниченного набора в 4 метода будет обязательно достаточно. Тем более, без хаков они всё-равно не используется. Просто использование ради использования, имхо.
+4
SORT — это что, отсортировать записи на сервере?
CHECK — HEAD? :)
CHECK — HEAD? :)
+1
Ну да. Вот у вас есть форум типа пхпбб, там можно разделы сортировать между собой.
0
А, а API для этой операции требуется на случай если кто-то захочет управлять интерфейсом форума из другого приложения, я понял.
0
sort — это один из параметров метода GET который возвращает список сущностей.
м?
м?
+6
UFO just landed and posted this here
Ну простите, ну аргументы в стиле 2007 года: «у всех IE6, он не поддерживает эти ваши новомодные ..., их нельзя использовать, всё делаем на флеше». Где бы мы сейчас были с таким подходом?
Нужно фиксить кривые клиенты и прокси, вместо того, чтобы портить API.
Что касается IE8, то начиная с висты он не актуален (есть обновления как минимум до IE9), а XP уже найти всё труднее и труднее… а уж если и найти, то у какого-нибудь гика, у которого никак не ie.
Нужно фиксить кривые клиенты и прокси, вместо того, чтобы портить API.
Что касается IE8, то начиная с висты он не актуален (есть обновления как минимум до IE9), а XP уже найти всё труднее и труднее… а уж если и найти, то у какого-нибудь гика, у которого никак не ie.
+4
Спасибо большое. Хорошо получилось. Просто, понятно и перевод удачный.
Когда то пытался прочитать эту книгу, да вот не сложилось. А тут прям конспект. Шикарно.
Когда то пытался прочитать эту книгу, да вот не сложилось. А тут прям конспект. Шикарно.
0
Статья класс! Можно подробней по поводу авторизации через API?
+5
Неплохо было бы дать ссылку на оригинал, из которого сделана выжимка. Как раз на днях его читал — там всё доходчиво и понятно даже для тех кто не сильно дружит с английским:
info.apigee.com/Portals/62317/docs/web%20api.pdf
info.apigee.com/Portals/62317/docs/web%20api.pdf
+5
Я оставил ссылку на странцу со скачкой книги в меню поста. Apigee разрешают скачивать её только после регистрации, так что не будем им ломать планы прямыми ссылками на файл :)
0
В дополнение к статье хотелось бы еще добавить, что сегодня не стоит забывать про метод
К слову, у гитхаба очень аккуратное и грамотное апи, рекомендую.
PATCH
для частичного изменения ресурса. Вот пример его использования в API гитхаба.К слову, у гитхаба очень аккуратное и грамотное апи, рекомендую.
+4
Это прекрасно, тысяча чертей, огромное спасибо! Как заставить горе-архитекторов это читать?
+1
Спасибо, очень годные советы. Единственно, мне кажется, для редактирования более подходит метод PATCH
+2
А мы еще юзаем префиксы в базовых урлах, которые:
* определяют локаль в которой нужно выдавать данные
* определяют клиента (мобильное устр-во, телевизор)
пример /tab_app/ru_ru/films/get-all?offset=0&limit=50
* определяют локаль в которой нужно выдавать данные
* определяют клиента (мобильное устр-во, телевизор)
пример /tab_app/ru_ru/films/get-all?offset=0&limit=50
0
А почему от типа клиента зависит результат запроса к api?
0
Разные тексты, картинки, ссылки на ресурсы… адаптивно же должно быть.
0
А что мешает перенести «служебную» информацию в хедеры? Язык — «Accept-Language», токен — «X-Access-Token», девайс — «User-Agent» или «X-User-Agent-Class», и не надо тогда пути несемантическим мусором забивать. «tab_app», «ru_ru» — это модификаторы только небольшой части содержимого, а не всего ресурса, и ладно бы они в параметрах передавались, так они ещё и в корне пути стоят. /me цокает языком. :)
+1
ооо… на то много причин :) и по опыту не надёжны эти заголовки… прокси всякие, в логах не хранятся, ссылку не передашь…
Если вы цокаете языком то пока просто не дошли до граблей :)
PS
подумайте почему на сайтах языки в урлах.
Если вы цокаете языком то пока просто не дошли до граблей :)
PS
подумайте почему на сайтах языки в урлах.
+1
За 4 года с проксями и хедерами, тьфу-тьфу-тьфу, никаких проблем, в логах всё замечательно хранится, а передавать ссылку на API… ну… я даже не знаю что тут сказать. :)
0
ну ничего, раз не знаете :) я ж не настаиваю
Просто не встречал протоколов которые подмешивают X-хэдеры в свою солянку.
Вы так делали где нибудь?
Просто не встречал протоколов которые подмешивают X-хэдеры в свою солянку.
Вы так делали где нибудь?
0
Да, локаль и платформа девайса — в хедерах передаются, всё прекрасно работает. В некоторых случаях и single_access_token'ы, чтобы в логах не светить лишний раз. И so far это всё отлично работает. А что, с таким подходом есть серьезные подводные камни? Расскажите, пожалуйста, будет интересно узнать, чего стоит опасаться.
+1
UFO just landed and posted this here
Полезное видео по теме: REST+JSON API Design — Best Practices for Developers
+1
Оставлю полезную ссылку: Best Practices for Designing a Pragmatic RESTful API
+1
очень хорошая подборка советов.
Однако на практике часто красота жертвуется в пользу оптимизации запросов.
Стандартный кейс:
есть красивое REST API по сущностям.
но для приложения работающего через хреновый GPRS каждый лишний запрос — это замедление времени работы приложения.
И часто нужно, чтобы одним запросом можно было вернуть все нужные данные и никакой умный кеш этой задачи не решит.
Как уложить в архитектуру такие запросы сразу нескольких объектов?
Однако на практике часто красота жертвуется в пользу оптимизации запросов.
Стандартный кейс:
есть красивое REST API по сущностям.
но для приложения работающего через хреновый GPRS каждый лишний запрос — это замедление времени работы приложения.
И часто нужно, чтобы одним запросом можно было вернуть все нужные данные и никакой умный кеш этой задачи не решит.
Как уложить в архитектуру такие запросы сразу нескольких объектов?
0
Каких именно нескольких объектов? Можно, например, вместе с инфо о товаре возвращать последние 10 комментариев и список фото для галереи товара, вместо того чтобы отдельно ходить за комментариями и отдельно за фотками.
0
да, я про такие случаи, как ваш пример с комментариями.
Это уже немного хак получается, что при получении объекта товара передваются еще зачем-то комментарии,
которые не всегда нужны в другом случае использования API.
Значит нужен доп. параметр (когда их передавать, когда нет), потом параметр лимит на число комментариев и т.д.
И в добавок к этому может, например, потребоваться вывести еще 3 похожих товара (если продолжить пример с магазином).
Это уже немного хак получается, что при получении объекта товара передваются еще зачем-то комментарии,
которые не всегда нужны в другом случае использования API.
Значит нужен доп. параметр (когда их передавать, когда нет), потом параметр лимит на число комментариев и т.д.
И в добавок к этому может, например, потребоваться вывести еще 3 похожих товара (если продолжить пример с магазином).
0
Хак или нет, но это довольно широко распространенная практика. В любом случае, когда вам по API надо вернуть объект, вы же не передаете голый набор object.attributes клиенту? Вы что-то излишнее убираете, что-то меняете, что-то добавляете. REST-сущность — это не обязательно строго информация о одной сущности и больше ничего — это набор рекомендаций, который предполагает что когда вы запросите сущность по rest — вы получите всю необходимую вам информацию о ней, включая так же доп инфо по усмотрению провайдера API.
На счет доп параметра, когда передавать, когда нет — это решается на этапе проектирования API. Комментарии либо нужны, либо нет. Если встает вопрос «иногда выводить, иногда нет» — значит проект был хреновый. Лимит не нужен — на етапе согласования решаем что нужно выводить 10 — выводим 10. Если кому-то 10 много — показывает 5, остальные игнорирует. Если кому-то 15 — делает доп.запрос. Если кому-то они не нужны — игнорирует комментарии.
Такие дела.
На счет доп параметра, когда передавать, когда нет — это решается на этапе проектирования API. Комментарии либо нужны, либо нет. Если встает вопрос «иногда выводить, иногда нет» — значит проект был хреновый. Лимит не нужен — на етапе согласования решаем что нужно выводить 10 — выводим 10. Если кому-то 10 много — показывает 5, остальные игнорирует. Если кому-то 15 — делает доп.запрос. Если кому-то они не нужны — игнорирует комментарии.
Такие дела.
0
Тоже интересуюсь данным вопросом. Пока пришел к выводу отдавать с запросом дефолтное (определяемое константой) количество зависимых сущностей, а потом, догребать их другим урлом если надо:
GET /articles/1 — вернет {title:'bla', body:«bbla», comments: [10 комментариев]}
GET /articles/1/comments?skip=10&limit=100
GET /articles/1 — вернет {title:'bla', body:«bbla», comments: [10 комментариев]}
GET /articles/1/comments?skip=10&limit=100
0
«Преждевременная оптимизация зло»
До построения чистого API вам сложно будет что-либо сказать как ваши методы исспользуются.
После построения API, можна исспользовать например SPDY и мультиплексирование вызовов.
Ни и последний совет о псевдонимах.
А вообще это архитектурный вопрос, на API примтивного уровня строим API более высокого порядка, если есть потребность отдает более высокий уровень клиенту, только очень акуратно, чтобы 3 таких клиента «тяжолыми» на выборку всей базы не положили все приложение, а чем сложнее API, тем сложнее такие вещи контролировать.
До построения чистого API вам сложно будет что-либо сказать как ваши методы исспользуются.
После построения API, можна исспользовать например SPDY и мультиплексирование вызовов.
Ни и последний совет о псевдонимах.
А вообще это архитектурный вопрос, на API примтивного уровня строим API более высокого порядка, если есть потребность отдает более высокий уровень клиенту, только очень акуратно, чтобы 3 таких клиента «тяжолыми» на выборку всей базы не положили все приложение, а чем сложнее API, тем сложнее такие вещи контролировать.
0
Дополнительный слой с мультиплексированием де мультиплексированием в 100 строчек кода сохранит чистое и светлое API и клиентский код сделает чище
0
Где можно почитать поподробне?
+1
Например вот тут tech.yandex.ru/events/yasubbotnik/chlb-feb-2012/talks/153/ начиная с 16-й минуты, а суть на 19-й
0
Спасибо!
0
Дополнить перевод одной фишечкой, которую все же стоит предусматривать при создании API стоит сразу закладывать, что будет версионность. В домене api.v1.....com или же ....com/api/v1/…
-1
Мы реализовывали версионность с помощью заголовков Accept и Content-Type, вида «application/appname-v1+json». Это дает бОльшую гибкость API.
Хорошее обсуждение этой темы: Best practices for API versioning
Хорошее обсуждение этой темы: Best practices for API versioning
+2
UFO just landed and posted this here
Стоит ли так делать? RFC 4627 пишет, что не стоит вроде. Вот обсуждение на SO
0
Вполне легальный способ, плюс браузерные dev tools правильно распознают ответ как JSON. См. tools.ietf.org/html/rfc6839
0
Вместо глаголов — HTTP
Мы только что описали собак с помощью двух базовых URL адресов с существительными. Теперь нам нужно работать с созданными сущностями. Чаще всего требуются операции чтения, создания, редактирования и удаления (CRUD — Create — Read — Update — Delete). Для этого нам прекрасно подойдут HTTP-методы GET, POST, PUT и DELETE.
POST /dogs — создать новую собаку
GET /dogs — получить список собак
PUT /dogs — редактирование всех собак сразу
DELETE /dogs — удаление всех собак
POST /dogs/12345 — вернуть ошибку (собака 12345 уже создана)
GET /dogs/12345 — показать информацию о собаке
PUT /dogs/12345 — редактировать собаку 12345
DELETE /dogs/12345 — удалить
Базовые URL выглядят просто, глаголы не используются, все интуитивно и понятно. Красота!
От заключительной фразы попахивает нездоровым слепым восхищением. На мой взгляд, в подобных случаях лучше использовать более явные URL адреса:
GET /dogs — получить список собак
POST /dogs/add — создать новую собаку
POST /dogs/update — редактирование всех собак сразу
POST /dogs/delete — удаление всех собак
GET /dogs/12345 — показать информацию о собаке
POST /dogs/12345/update — редактировать собаку 12345
POST /dogs/12345/delete — удалить
Это позволит добавлять новые (не входящие в набор CRUD) методы (например, "/dogs/12345/archive") не отходя от схемы именования и (опять же, на мой взгляд) это куда более нагляднее и понятнее, чем использование методов PUT и DELETE (малораспространённых, требующих дополнительных разъяснений и дополнительных технических возможностей от клиента).
+9
Да уж, или попадется какой-нибудь клиент, который ничего кроме
GET
и POST
не умеет. Вариант с ?method=DELETE
выглядит корявее, чем явное указание действия. +2
Извините, я немного не в курсе. А бывают такие клиенты?
0
В спецификации HTML 4.01 закреплено, что тег «form» поддерживает только GET и POST в качестве возможных значений аттрибута «method».
0
Даже в html5. Но я спрашивал про самих клиентов — может какие-то распространенные программы вообще не позволяют использовать методы, отличные от get и post?
+1
Так браузеры и есть клиенты.
0
Да, конечно среди распространённых браузеров вроде бы нет урезания функционала по выбору метода.
Но клиентом может быть и не браузер. API и создаётся для того, чтобы доступ к данным не зависел от клиента.
Я не исключаю случаи, что в каких-то устройствах возможны только методы POST и GET.
Например, есть такие gps трекеры от компании GlobalSat, которые могут передавать данные о местоположении по http протоколу. Трафика получается больше, чем по чистому tcp, но, может, доступа до слушания tcp на сервере нет.
Я никогда не работал с такими трекерами, но чую, что там только посылается исключительно GET запрос.
UPD. Как-то странно получилось, что я сам ответил на свой вопрос в этой ветке.
Но клиентом может быть и не браузер. API и создаётся для того, чтобы доступ к данным не зависел от клиента.
Я не исключаю случаи, что в каких-то устройствах возможны только методы POST и GET.
Например, есть такие gps трекеры от компании GlobalSat, которые могут передавать данные о местоположении по http протоколу. Трафика получается больше, чем по чистому tcp, но, может, доступа до слушания tcp на сервере нет.
Я никогда не работал с такими трекерами, но чую, что там только посылается исключительно GET запрос.
UPD. Как-то странно получилось, что я сам ответил на свой вопрос в этой ветке.
0
Да, конечно среди распространённых браузеров вроде бы нет урезания функционала по выбору метода.
Ну вот сейчас я попробовал создать HTML форму с
method="put"
и отправил её через браузер google chrome — метод был заменён на «get».Но клиентом может быть и не браузер. API и создаётся для того, чтобы доступ к данным не зависел от клиента.
Думаю, тут надо смотреть в сторону различный библиотек для языков программирования, которые могут использоваться для отправки HTTP запросов серверу (не думаю, что тут будут какие-либо серьёзные ограничения, т.к. от клиента требуется лишь сформировать соответствующую строку HTTP запроса).
+1
метод был заменён на «get».
Интересное поведение. Да, я ж говорил, что в html (4 и 5) значение method у формы может быть только get|post. Тем не менее, браузер может отправить http запрос с другим методом, например при помощи XmlHttpRequest.
+1
Ну это очень странное высказывание. POST запрос ничем принципиально от PUT или HEAD не отличается. Данные отправляются в одном и том же виде, в запросе меняются только слова POST, PUT, HEAD (или любой другой глагол).
Если речь идет о браузерах, которые не умеют отправлять ничего кроме GET и POST, то они умеют :)
И пример с form вообще не показателен. Мы же говорим о работе с API, а я не очень представляю зачем вообще в данном случае использовать form.
Браузерный клиент, работающий с API должен будет получать, обрабатывать и отправлять данные; в зависимости от ответов сервера создавать и показывать пользователю страничку (то есть страничка больше не должна генерится как встарину сервером). А это в любом случае что-нибудь вроде JavaScript с помощью которого отправить PUT-запрос не сложнее, чем GET или POST.
Если речь идет о браузерах, которые не умеют отправлять ничего кроме GET и POST, то они умеют :)
И пример с form вообще не показателен. Мы же говорим о работе с API, а я не очень представляю зачем вообще в данном случае использовать form.
Браузерный клиент, работающий с API должен будет получать, обрабатывать и отправлять данные; в зависимости от ответов сервера создавать и показывать пользователю страничку (то есть страничка больше не должна генерится как встарину сервером). А это в любом случае что-нибудь вроде JavaScript с помощью которого отправить PUT-запрос не сложнее, чем GET или POST.
0
Отличается. POST и PUT предполагают непустое тело запроса и, возможно, непустое тело ответа. HEAD предполагает пустое тело запроса и пустое тело ответа. Так что меняются не только слова.
Согласно RFC2616, PUT /some/URI означает, что тело запроса должно стать тем, что потом можно будет получить запросом GET /some/URI, т.е. создать или заменить этот ресурс.
PUT — идемпотентный запрос, т.е. выполняя его два или более раз мы получаем то же самое, что получили после выполнения один раз.
POST /some/URI- передать некоторые данные ресурсу /some/URI. POST — не идемпотентный запрос.
ну, а HEAD — это вообще обрезанный GET. Данные (тело запроса) вообще никакие не отправляются и не принимаются (тела ответа нет), только метаданные (заголовки).
Согласно RFC2616, PUT /some/URI означает, что тело запроса должно стать тем, что потом можно будет получить запросом GET /some/URI, т.е. создать или заменить этот ресурс.
PUT — идемпотентный запрос, т.е. выполняя его два или более раз мы получаем то же самое, что получили после выполнения один раз.
POST /some/URI- передать некоторые данные ресурсу /some/URI. POST — не идемпотентный запрос.
ну, а HEAD — это вообще обрезанный GET. Данные (тело запроса) вообще никакие не отправляются и не принимаются (тела ответа нет), только метаданные (заголовки).
0
Постойте, а разве в http 1.1 метод не может быть произвольным? Согласно rfc2616 «All other methods are OPTIONAL»
0
Но для этого веб-серверы должны обрабатывать все произвольные методы как GET или POST запросы (чтобы запрос с подобным методом дошёл до скрипта). Думаю, что под данной фразой имелось ввиду то, что веб-серверы могут реализовывать собственные специфические методы.
0
Стоп, в RFC полная фраза выглядит так: «The methods GET and HEAD MUST be supported by all general-purpose servers. All other methods are OPTIONAL». Т.е., по видимому, имеется ввиду, что методы PUT, DELETE и т.п. веб-агенты могут и не поддерживать.
0
>>Попробуйте посмотреть на ваши вызовы глазами пользователя. Вы увидите, что примерно 80% вызовов принимают и отдают данные одинаковой структуры. Это значит, что вполне можно сделать псевдонимы для последовательностей вызовов.
А можно парочку примеров? А то мне в голову приходят опять таки, только уродства типа /getDogsAndFoods или /getInitData
А можно парочку примеров? А то мне в голову приходят опять таки, только уродства типа /getDogsAndFoods или /getInitData
0
Например, нужно получить запись и комментарии к ней и это типовая операция. Вместо двух запросов (Запись/666 и КомментарииКЗаписи/666) комментарии (или первая их порция) включаются в ответ на запрос Запись/666.
0
Я, кстати, предпочитаю делать аналогично выводу ls -F:
/people/ — для списка
/people/123 — для конкретного
То есть, слеш в конце определяет, список это или нет. Это особенно удобно для под-ресурсов типа:
/account/profile — для профиля «текущего» юзера. Сразу ясно, что никакой это не список, а конкретный ресурс.
/people/ — для списка
/people/123 — для конкретного
То есть, слеш в конце определяет, список это или нет. Это особенно удобно для под-ресурсов типа:
/account/profile — для профиля «текущего» юзера. Сразу ясно, что никакой это не список, а конкретный ресурс.
+4
>Вместо глаголов — HTTP
… после чего вы наслаждаетесь какой ни будь прокси, проглотившей PUT и DELETE.
Не говоря про то, что автор просто не в курсе спецификаций http, где скорее уж PUT, а не POST должен выступать в роли «create», а для «update» скорее подходит PATCH.
Тем более POST как глагол означает не совсем create, а PUT не совсем update. Рассогласованные имена это хороший способ запутать всех, включая и автора статьи.
Я бы рекомендовал начинать с надежно работающего враппера вроде:
Получаем в ответ
И только потом развлекаться с попыткой корректно использовать для передачи директив HTTP методы.
Использовать дополнительные уровни протокола и обертки — это абсолютно нормально, и вам будет проще локализовать на каком из них произошла ошибка, а также проявлять гибкость. Лучше, если сторонний разработчик в явную прочитает про ваши команды и коды состояний, нежели будет догадываться и помнить о том, как именно вы трактовали HTTP
Также вы окажете одолжение разработчикам под ваше API, если в дополнение к протоколу опубликуете схемы передаваемых данных в виде JSON Schema, Avro и т.п. Будет проще и с документированием и с версиями.
… после чего вы наслаждаетесь какой ни будь прокси, проглотившей PUT и DELETE.
Не говоря про то, что автор просто не в курсе спецификаций http, где скорее уж PUT, а не POST должен выступать в роли «create», а для «update» скорее подходит PATCH.
Тем более POST как глагол означает не совсем create, а PUT не совсем update. Рассогласованные имена это хороший способ запутать всех, включая и автора статьи.
Я бы рекомендовал начинать с надежно работающего враппера вроде:
{
"method": "update",
"data": {
....
}
}
Получаем в ответ
{
"status": "success",
"statusCode": "200",
"message": "ok"
}
И только потом развлекаться с попыткой корректно использовать для передачи директив HTTP методы.
Использовать дополнительные уровни протокола и обертки — это абсолютно нормально, и вам будет проще локализовать на каком из них произошла ошибка, а также проявлять гибкость. Лучше, если сторонний разработчик в явную прочитает про ваши команды и коды состояний, нежели будет догадываться и помнить о том, как именно вы трактовали HTTP
Также вы окажете одолжение разработчикам под ваше API, если в дополнение к протоколу опубликуете схемы передаваемых данных в виде JSON Schema, Avro и т.п. Будет проще и с документированием и с версиями.
+4
Сплошная вкусовщина и противоречивые параграфы. То «использовать глаголы плохо», то «для действий используйте глаголы». Сразу бы написали, что «для сущностей используйте существительные, а для действий — глаголы», хотя это конечно очевидно.
Потом какой-то странный совет про CRUD over GET — это вообще за гранью добра и зла. Ладно бы ещё написали, что для получения данных использовать GET, а для изменения POST — этого достаточно для реализации чего угодно.
И вообще, CRUD — далеко не лучшее решение во многих случаях. Начиная с того, что Create и Update во многих случаях практически идентичные операции (разница лишь в том передаётся id записи или нет), заканчивая тем, что и действий с сущностями гораздо больше: Feed, чтобы покормить собаку, Walk, чтобы выглять и тд.
Коды ответов то рекомендуют использовать соответствующие HTTP коды, то говорят, что по умолчанию лучше выдавать всегда 200 o_0" И вообще много таких советов как усложнить себе жизнь: сделайте поддержку HTTP методов, а потом прикрутите их эмуляцию через ограниченный набор. Коли уж нужно втискиваться в рамки ограниченного набора — ну так и сделайте апи в этих рамках без всяких эмуляций. Незачем переусложнять.
Далее, префиксные параметры — позиционные с соответствующими косяками:
* Параметр всё-равно надо указывать даже если он не нужен. Например, мы хотим, чтобы язык выбирался автоматически — вместо /ru/ придётся писать что-нибудь типа /auto/. Либо допускать его не указание, но для этого нужны будут костыли типа «список возможных значений».
* Сложность добавления параметров. В серединку их добавить можно лишь со скрипом.
* Не всегда очевидно что означает тот или иной параметр. Имени-то у него нет. А если ещё и портянка из /auto/auto/ — вообще беда.
Опять же, псевдостатика хоть и популярна, но всё же менее понятна. Согласитесь, запись вида /ключ1/значение1/ключ2/значение2 (/owners/vasya123/dogs/bobby) — это какая-то хрень. Куда логичней иметь что-то типа /owner=vasya123/dog=bobby, а если чуть подправить пунктуацию, то получится всем известный формат ?owner=vasya123&dog=bobby
Но я бы предпочёл, что-то типа такого:
?dog;list для работы со списком собак
?dog=12345 для работы с отдельной собакой
?dog;list;owner=5678 для работы со списком собак отдельного человека
?dog;list;color=red;state=running;location=park для сложной фильтрации
?dog;list;api=13 просто указываем версию апи дополнительным параметром, если не указана — ну делаем редирект на «нормализованный урл» подставляя последнюю версию.
?owner;list;fields=name=address=dogs элегантный способ передать список значений, хотя и спорный, так как для поддержки такого формата сейчас нужно велосипедить.
?dog;list;offset=50;limit=50 пагинация
?convert;amount=100;from=EUR;to=USD конвертация 100 евро в доллары
?dog;list;format=json вместо формата по умолчанию используем json
?search=search+word глобальный поиск
?serch=search+word;type=owner=dog поиск по собакам и их хозяевам
Кстати, json далеко не лучший формат представления данных в апи. Потому что имеет косяки с расширяемостью. XML конечно тоже имеет проблемы, но сильно меньше. Зачастую изменение формата JSON приводит к необходимости увеличивать версию апи, потому что код работающий со старым форматом начинает падать на новом. В XML больше возможностей для расширения благодаря более абстрактным структурам (дерево из именованных узлов вместо массивов и хэшей в JSON). Есть ещё формат Tree, который ещё лучше чем XML в этом плане, правда библиотек под него почти что нет hyoo.ru/?article=%D0%A4%D0%BE%D1%80%D0%BC%D0%B0%D1%82+Tree;author=Nin+Jin
Для api можно вообще не заводить отдельный домент, например, на сайте hyoo.ru сервер всегда выдаёт довольно компактный api-xml, который в браузере преобразуется в страницу с помощью xslt.
Потом какой-то странный совет про CRUD over GET — это вообще за гранью добра и зла. Ладно бы ещё написали, что для получения данных использовать GET, а для изменения POST — этого достаточно для реализации чего угодно.
И вообще, CRUD — далеко не лучшее решение во многих случаях. Начиная с того, что Create и Update во многих случаях практически идентичные операции (разница лишь в том передаётся id записи или нет), заканчивая тем, что и действий с сущностями гораздо больше: Feed, чтобы покормить собаку, Walk, чтобы выглять и тд.
Коды ответов то рекомендуют использовать соответствующие HTTP коды, то говорят, что по умолчанию лучше выдавать всегда 200 o_0" И вообще много таких советов как усложнить себе жизнь: сделайте поддержку HTTP методов, а потом прикрутите их эмуляцию через ограниченный набор. Коли уж нужно втискиваться в рамки ограниченного набора — ну так и сделайте апи в этих рамках без всяких эмуляций. Незачем переусложнять.
Далее, префиксные параметры — позиционные с соответствующими косяками:
* Параметр всё-равно надо указывать даже если он не нужен. Например, мы хотим, чтобы язык выбирался автоматически — вместо /ru/ придётся писать что-нибудь типа /auto/. Либо допускать его не указание, но для этого нужны будут костыли типа «список возможных значений».
* Сложность добавления параметров. В серединку их добавить можно лишь со скрипом.
* Не всегда очевидно что означает тот или иной параметр. Имени-то у него нет. А если ещё и портянка из /auto/auto/ — вообще беда.
Опять же, псевдостатика хоть и популярна, но всё же менее понятна. Согласитесь, запись вида /ключ1/значение1/ключ2/значение2 (/owners/vasya123/dogs/bobby) — это какая-то хрень. Куда логичней иметь что-то типа /owner=vasya123/dog=bobby, а если чуть подправить пунктуацию, то получится всем известный формат ?owner=vasya123&dog=bobby
Но я бы предпочёл, что-то типа такого:
?dog;list для работы со списком собак
?dog=12345 для работы с отдельной собакой
?dog;list;owner=5678 для работы со списком собак отдельного человека
?dog;list;color=red;state=running;location=park для сложной фильтрации
?dog;list;api=13 просто указываем версию апи дополнительным параметром, если не указана — ну делаем редирект на «нормализованный урл» подставляя последнюю версию.
?owner;list;fields=name=address=dogs элегантный способ передать список значений, хотя и спорный, так как для поддержки такого формата сейчас нужно велосипедить.
?dog;list;offset=50;limit=50 пагинация
?convert;amount=100;from=EUR;to=USD конвертация 100 евро в доллары
?dog;list;format=json вместо формата по умолчанию используем json
?search=search+word глобальный поиск
?serch=search+word;type=owner=dog поиск по собакам и их хозяевам
Кстати, json далеко не лучший формат представления данных в апи. Потому что имеет косяки с расширяемостью. XML конечно тоже имеет проблемы, но сильно меньше. Зачастую изменение формата JSON приводит к необходимости увеличивать версию апи, потому что код работающий со старым форматом начинает падать на новом. В XML больше возможностей для расширения благодаря более абстрактным структурам (дерево из именованных узлов вместо массивов и хэшей в JSON). Есть ещё формат Tree, который ещё лучше чем XML в этом плане, правда библиотек под него почти что нет hyoo.ru/?article=%D0%A4%D0%BE%D1%80%D0%BC%D0%B0%D1%82+Tree;author=Nin+Jin
Для api можно вообще не заводить отдельный домент, например, на сайте hyoo.ru сервер всегда выдаёт довольно компактный api-xml, который в браузере преобразуется в страницу с помощью xslt.
-2
Кстати, json далеко не лучший формат представления данных в апи. Потому что имеет косяки с расширяемостью. XML конечно тоже имеет проблемы, но сильно меньше. Зачастую изменение формата JSON приводит к необходимости увеличивать версию апи, потому что код работающий со старым форматом начинает падать на новом. В XML больше возможностей для расширения благодаря более абстрактным структурам (дерево из именованных узлов вместо массивов и хэшей в JSON).
О каких косяках с расширяемостью Вы говорите? JSON и XML оба предназначены для представления древовидных данных, просто у JSON синтаксис более компактный. И они оба позволяют создавать дерево из именованных узлов.
+3
Здорово поддерживать несколько форматов ответа.
Google ?alt=json
Foursquare /venue.json
Digg ?type=json
Кстати, Digg позволяет установить формат ответа и через HTTP-заголовок Accept.
Нужно ли тут использовать костыли? В спецификации HTTP четко написано:
The Accept request-header field can be used to specify certain media types which are acceptable for the response.
Если какие-то прокси реализовуют протокол не правильно (не передают заголовки), то это по большей сути не ваша проблема. Иначе зачем вообще придумавать какие-то спецификации. А при использовании заголовков все просто и понятно.
ps: только недавно диплом защитил на эту тему, приятно видеть что на 90% решения выбранные в нем пересекаются со статьёй более опытных товарищей :)
+3
Это делается только для удобства работы с сервисом из браузера. Возможно более сведущие в этом вопросе меня поправят.
0
Если какие-то прокси реализовуют протокол не правильно (не передают заголовки), то это по большей сути не ваша проблема.
Это как раз проблема разработчика, что из-за какой-то никому не нужной фигни некоторые пользователи, которые используют прокси не смогут зайти.
+1
Возможно я был слишком категоричен, и в каждом конкретном случае нужно смотреть что больше — затраты на введение костылей или убытки из-за негативной реакции пользователей.
Просто из-за вот таких небольших отклонений (недочетов) в реализации стандартов и возникает куча костылей, хитрых хаков и запутанных поведений. Вообщем всего того что я так не люблю в программировании. Насколько я люблю разнообразие андроид устройств, настолько же я не люблю хаки, которые нужно применять для их поддержки. Со старыми браузерами думаю тоже самое.
Почему когда я пишу код который крашится или работает не правильно, тогда мне приходится (да я и рад) его переписывать. А когда производители устройств что-то партачят, мне приходится все их проколы программно закрывать?
Ввести бы сертификацию какую построже, прошел — получи значек красивый, не прошел — получи ужасный красный знак на пол коробки устройства о том что «сертификация не пройдена, подумайте еще раз перед тем как купить(установить) это». Глядишь и жизнь стала бы немного проще.
Просто из-за вот таких небольших отклонений (недочетов) в реализации стандартов и возникает куча костылей, хитрых хаков и запутанных поведений. Вообщем всего того что я так не люблю в программировании. Насколько я люблю разнообразие андроид устройств, настолько же я не люблю хаки, которые нужно применять для их поддержки. Со старыми браузерами думаю тоже самое.
Почему когда я пишу код который крашится или работает не правильно, тогда мне приходится (да я и рад) его переписывать. А когда производители устройств что-то партачят, мне приходится все их проколы программно закрывать?
Ввести бы сертификацию какую построже, прошел — получи значек красивый, не прошел — получи ужасный красный знак на пол коробки устройства о том что «сертификация не пройдена, подумайте еще раз перед тем как купить(установить) это». Глядишь и жизнь стала бы немного проще.
+1
Отличная статья, ещё и весьма вовремя (для меня, разумеется ;) ).
Конечно, с некоторыми положениями можно подискутировать, но в целом все очень толково.
Единственное, что удивило — не рассматривается простая особенность GET и POST http-запросов в контексте создания API: первый идемпотентный, а второй — нет. Разумеется, свойство это обеспечивается не названием метода, а реализацией API, но в целом это хороший и правильный тон — использовать GET для получения данных, а POST — для их изменения.
Конечно, с некоторыми положениями можно подискутировать, но в целом все очень толково.
Единственное, что удивило — не рассматривается простая особенность GET и POST http-запросов в контексте создания API: первый идемпотентный, а второй — нет. Разумеется, свойство это обеспечивается не названием метода, а реализацией API, но в целом это хороший и правильный тон — использовать GET для получения данных, а POST — для их изменения.
0
Это как раз описано: GET для получения, POST для создания, DELETE для удаления…
0
Вы немного не поняли мою мысль. Можно DELETE, PATCH, PUT и т.д. использовать, я все это в статье видел. Мой коментарий о том, что если не использовать все это многообразие, то все же вполне разумно изменяющие данные запросы отсылать через POST, а все остальные (безопасные) операции — делать через GET.
0
Я перечитал RFC и теперь могу сформулировать, что же мне не понравилось в вашем комментарии :)
Во-первых, к определениям: идемпотентный != не изменяющий данные на сервере. Идемпотентный (по определению) — это такой, многократное выполнение которого эквивалентно однократному. Т.е. PUT, DELETE, GET, HEAD, OPTIONS — все идемпотентные, т.к. выполняя DELETE /что/нибудь четыре раза подряд мы получим ровно такой же результат, как если бы выполнили его один раз. (В математике: идемпотентный оператор равен себе самому возведённому в любую степень.)
Подход «всё изменяемое — через POST» не совсем корректен. POST предназначен для действительно «одноразовых» событий, которые могут повторяться, и каждое повторение — это новое событие, должно регистрироваться независимо. Например, добавление комментария к записи или отправка письма, каждый раз мы отправляем новый комментарий или новое письмо.
Например, PUT — создать или изменить ресурс. Выполнив его (с одним адресом) несколько раз, мы не создадим несколько ресурсов, мы один раз возможно создадим и несколько раз заменим ресурс по этому адресу. PUT /что/нибудь имеет смысл «создать объект, который потом можно будет получить запросом GET /что/нибудь», причём тело запроса PUT будет телом ответа GET. Это идемпотентная операция, ничего страшного, если её выполнить неоднократно, однако она изменяет состояние. DELETE дополняет PUT, отменяя его действие.
Смысл методов POST и PUT/DELETE, как видите, существенно отличается, хотя все они изменяют данные. Соответственно если мы вынуждены реализовывать такие операции, как удаление ресурсов, через POST/GET, то GET (в смысле идемпотентности) больше похож на DELETE, чем POST. Ну, и соответственно, уместнее.
Во-первых, к определениям: идемпотентный != не изменяющий данные на сервере. Идемпотентный (по определению) — это такой, многократное выполнение которого эквивалентно однократному. Т.е. PUT, DELETE, GET, HEAD, OPTIONS — все идемпотентные, т.к. выполняя DELETE /что/нибудь четыре раза подряд мы получим ровно такой же результат, как если бы выполнили его один раз. (В математике: идемпотентный оператор равен себе самому возведённому в любую степень.)
Подход «всё изменяемое — через POST» не совсем корректен. POST предназначен для действительно «одноразовых» событий, которые могут повторяться, и каждое повторение — это новое событие, должно регистрироваться независимо. Например, добавление комментария к записи или отправка письма, каждый раз мы отправляем новый комментарий или новое письмо.
Например, PUT — создать или изменить ресурс. Выполнив его (с одним адресом) несколько раз, мы не создадим несколько ресурсов, мы один раз возможно создадим и несколько раз заменим ресурс по этому адресу. PUT /что/нибудь имеет смысл «создать объект, который потом можно будет получить запросом GET /что/нибудь», причём тело запроса PUT будет телом ответа GET. Это идемпотентная операция, ничего страшного, если её выполнить неоднократно, однако она изменяет состояние. DELETE дополняет PUT, отменяя его действие.
Смысл методов POST и PUT/DELETE, как видите, существенно отличается, хотя все они изменяют данные. Соответственно если мы вынуждены реализовывать такие операции, как удаление ресурсов, через POST/GET, то GET (в смысле идемпотентности) больше похож на DELETE, чем POST. Ну, и соответственно, уместнее.
0
Что за софистика =)
У HTTP-глаголов есть еще одна важная характеристика кроме идемпотентности — это безопасность.
Ознакомьтесь, пожалуйста, с ней.
У HTTP-глаголов есть еще одна важная характеристика кроме идемпотентности — это безопасность.
Ознакомьтесь, пожалуйста, с ней.
-1
Практическая истина такова, что все они небезопасные. GET тоже изменяет данные, хотя бы счётчики посещений. Так что нет у них безопасности.
0
Если честно я читаю, и вообще не понимаю о чем речь.
Метод — это просто строчка. Прокси, веб сервер и прочий middleware просто прокидывает эту строчку в приложение. А приложение уже наделяет эту стрчоку семантикой.
В этом смысле, не вижу проблем с методом SORT, сомневаюсь, что есть хоть какой-то вид софта, который заблокирует запрос, начинающийся с SORT только потому, что такого метода в RFC не описано
К слову, наличие или отсутствие тела на уровне парсера или прокси определяется по заголовкам Content-Length, Content-Type и Content-Disposition, а не по названию метода, так что даже для этих частей название не имеет значения.
Метод — это просто строчка. Прокси, веб сервер и прочий middleware просто прокидывает эту строчку в приложение. А приложение уже наделяет эту стрчоку семантикой.
В этом смысле, не вижу проблем с методом SORT, сомневаюсь, что есть хоть какой-то вид софта, который заблокирует запрос, начинающийся с SORT только потому, что такого метода в RFC не описано
К слову, наличие или отсутствие тела на уровне парсера или прокси определяется по заголовкам Content-Length, Content-Type и Content-Disposition, а не по названию метода, так что даже для этих частей название не имеет значения.
0
Я не вижу проблем с методом SORT. Я вообще не вижу места для метода SORT в HTTP. Нет метода — нет проблем.
Поясняю.
HTTP — это протокол типа «запрос-ответ» без состояния. Ещё раз: протокол без состояния. Никаких методов, которые наделяли бы его состоянием, нет. Не должно быть. Если мы хотим вести речь о состоянии (сессии, куки), мы делаем это в надстройках, поверх протокола: используем заголовки, куки и тому подобное.
Ещё особенность: адреса, к которым мы делаем запросы, не перечислимы. Мы не можем средствами протокола получить список всех объектов (URI) на сервере. Это тоже делается средствами более высокого уровня, поверх HTTP. Значит, с точки зрения HTTP никакого порядка объектов не предполагается. Структура (иерархическая) предполагается, а порядок записей в рамках одной ступени иерархии — нет.
SORT — штука, которая заведомо предполагает наличие состояния, причём в виде некоторого порядка записей на сервере. Как она вообще может появиться в таком протоколе?
Поясняю.
HTTP — это протокол типа «запрос-ответ» без состояния. Ещё раз: протокол без состояния. Никаких методов, которые наделяли бы его состоянием, нет. Не должно быть. Если мы хотим вести речь о состоянии (сессии, куки), мы делаем это в надстройках, поверх протокола: используем заголовки, куки и тому подобное.
Ещё особенность: адреса, к которым мы делаем запросы, не перечислимы. Мы не можем средствами протокола получить список всех объектов (URI) на сервере. Это тоже делается средствами более высокого уровня, поверх HTTP. Значит, с точки зрения HTTP никакого порядка объектов не предполагается. Структура (иерархическая) предполагается, а порядок записей в рамках одной ступени иерархии — нет.
SORT — штука, которая заведомо предполагает наличие состояния, причём в виде некоторого порядка записей на сервере. Как она вообще может появиться в таком протоколе?
0
Да блин, вы написали столько текста, а лучше бы постарались внимательнее понять что я хотел сказать.
Ясен пень, что в HTTP нет состояния, никто не предлагает делать цепочку в стиле SORT->GET
Ну представьте что у вас есть каталог, в котором элементы физически упорядочены и SORT скажем меняет их порядок, не на уровне представления, а на уровне хранения. Ну скажем этот порядок имеет значение потому что эти элементы потом в таком порядке кем-то обрабатываются. (т.е. что-то вроде того что делает CLUSTER в постгресе)
Ну или забейте на SORT, если он вас так смущает. Представьте что вам нужен метод REPLACE для атомарной замены. Или TRANSFER для перевода ресурсов из счета А в счет Б. Помоему, отличный кандидат на звание «метода» в нашем API.
Про то что «не все клиенты поддерижвают произвольные методы». Это правда, никто этот факт не игнорирует, просто нравится строить API из предположения что большинство клиентов все-таки нормальные, а все остальные смогут заюзать какой нибудь workaround, скажем можно продублировать логику так, что все запосы можно сделать с помощью POST с глаголом, размещенным в теле запроса или заголовке.
Ясен пень, что в HTTP нет состояния, никто не предлагает делать цепочку в стиле SORT->GET
Ну представьте что у вас есть каталог, в котором элементы физически упорядочены и SORT скажем меняет их порядок, не на уровне представления, а на уровне хранения. Ну скажем этот порядок имеет значение потому что эти элементы потом в таком порядке кем-то обрабатываются. (т.е. что-то вроде того что делает CLUSTER в постгресе)
Ну или забейте на SORT, если он вас так смущает. Представьте что вам нужен метод REPLACE для атомарной замены. Или TRANSFER для перевода ресурсов из счета А в счет Б. Помоему, отличный кандидат на звание «метода» в нашем API.
Про то что «не все клиенты поддерижвают произвольные методы». Это правда, никто этот факт не игнорирует, просто нравится строить API из предположения что большинство клиентов все-таки нормальные, а все остальные смогут заюзать какой нибудь workaround, скажем можно продублировать логику так, что все запосы можно сделать с помощью POST с глаголом, размещенным в теле запроса или заголовке.
0
Нет ничего упорядоченного на сервере. «REPLACE» и так есть, называется PATCH.
Что-то вроде TRANSFER могло бы появиться… правда, в логике HTTP оно называлось бы MOVE — переместить ресурс, как дополнение к GET — получить ресурс и т. д. Хотите сразу назову проблему с таким методом практически в любом клиенте?
А вот она: предполагается указание двух адресов. А формат строки запроса HTTP таков, что там нет места для второго адреса ресурса. МЕТОД РЕСУРС ПРОТОКОЛ/ВЕРСИЯ. Куда тут второй ресурс поместить? В заголовки? Ну, тогда, выходит, метаданные становятся обязательными (как заголовок Host в HTTP/1.1). Значит, появится некоторый стандартный заголовок (не-X-что-нибудь), который отдельные клиенты имеют право не знать и соответственно не желать отправить. Это только X-что-нибудь незнакомое разрешается отправлять.
Не, я не против таких методов. Но это уже будет не HTTP/1.1.
Вообще, если вам интересно, таких протоколов на базе HTTP есть дюжины. Есть IPP/1.1 (Internet Printing Protocol) — это супертип HTTP/1.1, там добавился метод, кажется, PRINT. В принципе, ничем больше от HTTP он не отличается. Есть протокол сервера трансляции Icecast, тот, который используется для подключения источников — тоже на базе HTTP/1.1. Тоже там добавилось пара методов вроде SOURCE и дальше адрес ресурса — это адрес точки монтирования источника. Даже SIP/1.0 похож на HTTP — главное отличие в том, что он поверх UDP и методы называются там REGISTER, INVITE и т. п., а коды состояния — ACK, DECLINE, BUSY и тому подобное.
Но это всё не соответствует RFC2616, то есть, не HTTP/1.1, и называется по-другому. И клиенты HTTP/1.1 (реализующие RFC2616) имеют право сознательно блокировать все эти некорректности.
Что-то вроде TRANSFER могло бы появиться… правда, в логике HTTP оно называлось бы MOVE — переместить ресурс, как дополнение к GET — получить ресурс и т. д. Хотите сразу назову проблему с таким методом практически в любом клиенте?
А вот она: предполагается указание двух адресов. А формат строки запроса HTTP таков, что там нет места для второго адреса ресурса. МЕТОД РЕСУРС ПРОТОКОЛ/ВЕРСИЯ. Куда тут второй ресурс поместить? В заголовки? Ну, тогда, выходит, метаданные становятся обязательными (как заголовок Host в HTTP/1.1). Значит, появится некоторый стандартный заголовок (не-X-что-нибудь), который отдельные клиенты имеют право не знать и соответственно не желать отправить. Это только X-что-нибудь незнакомое разрешается отправлять.
Не, я не против таких методов. Но это уже будет не HTTP/1.1.
Вообще, если вам интересно, таких протоколов на базе HTTP есть дюжины. Есть IPP/1.1 (Internet Printing Protocol) — это супертип HTTP/1.1, там добавился метод, кажется, PRINT. В принципе, ничем больше от HTTP он не отличается. Есть протокол сервера трансляции Icecast, тот, который используется для подключения источников — тоже на базе HTTP/1.1. Тоже там добавилось пара методов вроде SOURCE и дальше адрес ресурса — это адрес точки монтирования источника. Даже SIP/1.0 похож на HTTP — главное отличие в том, что он поверх UDP и методы называются там REGISTER, INVITE и т. п., а коды состояния — ACK, DECLINE, BUSY и тому подобное.
Но это всё не соответствует RFC2616, то есть, не HTTP/1.1, и называется по-другому. И клиенты HTTP/1.1 (реализующие RFC2616) имеют право сознательно блокировать все эти некорректности.
0
Ну так зачем ограничивать себя довольно узким протоколом, если это не даёт никаких преимуществ? Вот хоть убейте — не понимаю. Зачем ограничивать себя методами GPPD, если они уже по умолчанию не покрывают всего, что нам может понадобится и всё-равно придётся вводить дополнительный слой?
0
Затем же, зачем существует стек OSI — чтобы упростить и развязать сущности.
Точно так же TCP отвязан от HTTP, и сегодня видно, как оказалось преимуществом: HTTP через TCP v6 оказался тем же самым протоколом, и веб-разработчикам вообще ни о чём беспокоиться не пришлось (кроме тех, кто считает, что IP-адреса нужны приложению — они сами сознательно привязались к IPv4 и им всё-таки пришлось поэтому беспокоиться).
И над HTTP тоже есть слои: например, HTML-страница. Она может и не быть над HTTP, а быть просто файлом на диске, от этого она не перестаёт быть HTML-страницей. А может быть и не страница, а что-то другое, картинка.
В общем, разделение на слои упрощает эти слои и позволяет их менять независимо друг от друга.
Точно так же TCP отвязан от HTTP, и сегодня видно, как оказалось преимуществом: HTTP через TCP v6 оказался тем же самым протоколом, и веб-разработчикам вообще ни о чём беспокоиться не пришлось (кроме тех, кто считает, что IP-адреса нужны приложению — они сами сознательно привязались к IPv4 и им всё-таки пришлось поэтому беспокоиться).
И над HTTP тоже есть слои: например, HTML-страница. Она может и не быть над HTTP, а быть просто файлом на диске, от этого она не перестаёт быть HTML-страницей. А может быть и не страница, а что-то другое, картинка.
В общем, разделение на слои упрощает эти слои и позволяет их менять независимо друг от друга.
0
Но это уже будет не HTTP/1.1
Еще как будет: www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1
HTTP протокол не запрещает использовать произвольные имена методов.
Вы, мне кажеться, мешаете мух с котлетами. Протокол позволяет передать набор разных штук вроде метода, url, заголовков и тела в приложение.
Промежуточные узлы должны уметь поддерживать такую передачу.
То есть формальное описание HTTP — оно для разработчиков клиентского движка, разработчиков прокси и разработчиков веб-серверов.
А что вы туда положите — уже ваше дело. Не надо заявлять о проблеме того, что «метадананные становяться обязательными» — это не проблема уровня протокола. Клиентская библотека сможет закодировать новый заголовок? Прокси сможет его передать? Сервер сможет его прочитать и передать приложению? Если да, отлично, протокол справился.
HTTP не регулирует ограничения того, что клиент передает в запросах и что он расчитывает получить от сервера.
Возвращаясь к нашему примеру, это вовсе не HTTP-протокол обяжет вас передавать во втором заголовке путь назначения для MOVE. Вам это нужно будет сделать, что бы вас поняло приложение, которму вы шлете запрос. А протокольная часть она что, она закодирует ваш запрос без заголовка, и потом раскодирует ответ с ошибкой от приложения, которое не нашло заголовка и послало вам 400 Bad request.
Кстати, в SIP коды состояний такие же как и в HTTP — числовые. Только помимо 1xx-5xx(семантика которых совпадает с HTTP) есть еще 6xx.
ACK — это запрос(как PUT и GET), со спец. семантикой, а не код состояния. Но SIP это вобщем-то совсем не HTTP-протокол, просто он внешне похож.
0
Ну вы сначала приводите пример, что вроде бы протокол — это форматирование запросов и ответов, а как их понимать, дело приложения, а потом у вас SIP — совершенно другой, просто внешне похож.
А между тем по вашей же ссылке написано, что протокол HTTP определяет и семантику происходящего. Как прокси, так и сервер имеет право отказаться выполнять запрос, видя незнакомый метод, так как не знает его семантики (может, он вообще самому прокси предназначался, тогда его не надо было передавать). И ответить 501 Not Implemented.
А между тем по вашей же ссылке написано, что протокол HTTP определяет и семантику происходящего. Как прокси, так и сервер имеет право отказаться выполнять запрос, видя незнакомый метод, так как не знает его семантики (может, он вообще самому прокси предназначался, тогда его не надо было передавать). И ответить 501 Not Implemented.
0
Эмм, у SIP несколько другие свойства из-за того что он строится на UDP.
HTTP использует фичу TCP-соединения по поводу упорядоченности, и работает по принципу открыть соединение-отправить запрос-получить ответ по тому же TCP-соединению.
Это как раз свойства протоколов.
Когда мы говорим, что у нас есть метод MOVE, ожидающий заголовок Destination, мы создаем новый протокол, поверх HTTP.(а не меняем HTTP, и не создаем протокол похожий на HTTP) Вообще говоря описание API это и есть описание протокола.
HTTP использует фичу TCP-соединения по поводу упорядоченности, и работает по принципу открыть соединение-отправить запрос-получить ответ по тому же TCP-соединению.
Это как раз свойства протоколов.
Когда мы говорим, что у нас есть метод MOVE, ожидающий заголовок Destination, мы создаем новый протокол, поверх HTTP.(а не меняем HTTP, и не создаем протокол похожий на HTTP) Вообще говоря описание API это и есть описание протокола.
0
Очередное заблуждение.
Практическая истина такова, что часто люди делают веб-вервисы как попало, а потом еще осмеливаются называть свое творение RESTfull.
Но сделать GET-запрос небезопасным — это уж совсем большой грех. Вот в данном примере со счетчиком, если сделать его частью ресурса — можно сразу забывать о кешировании ответов сервера. Ну потому, что каждый раз ответ будет с новыми данными.
Как избежать такого? Ну не делать счетчик частью ресурса.
Практическая истина такова, что часто люди делают веб-вервисы как попало, а потом еще осмеливаются называть свое творение RESTfull.
Но сделать GET-запрос небезопасным — это уж совсем большой грех. Вот в данном примере со счетчиком, если сделать его частью ресурса — можно сразу забывать о кешировании ответов сервера. Ну потому, что каждый раз ответ будет с новыми данными.
Как избежать такого? Ну не делать счетчик частью ресурса.
0
Ну, не так. Счётчик посещений считает посещения. Не факт, что он их показывает прямо на той же самой странице, так что сама страница от него не зависит и может кэшироваться. Так что сам по себе счётчик, вообще говоря, кэшированию не мешает.
Но доверия этому счётчику при наличии такого кэширования всё равно нет. Сколько раз страница взята из кэша и соответственно посещение было, а счётчик не менялся? Сколько HEAD-запросов дошло до счётчика, и поэтому посещения ресурса фактически не было, а счётчик посчитался?
И то, что он делает запрос GET не идемпотентным — плохо. Несколько раз сделанный запрос — и каждый раз на сервере новое состояние. Может быть, эта же самая страница не изменится, но какой-нибудь рейтинг в другом месте ресурса может поменяться.
Подсчёт может делаться POSTом — он единственный не-идемпотентный метод с передачей данных.
Насчёт небезопасности GETа: да, я согласен, это плохо. Только мало смысла говорить о правильном использовании GET, если мы будем делать POSTом то, что нужно было делать DELETE, PUT или PATCH.
Вы говорите о том, что методы должны быть использованы правильно, но ветка-то комментариев про то, как быть, если это невозможно.
Но доверия этому счётчику при наличии такого кэширования всё равно нет. Сколько раз страница взята из кэша и соответственно посещение было, а счётчик не менялся? Сколько HEAD-запросов дошло до счётчика, и поэтому посещения ресурса фактически не было, а счётчик посчитался?
И то, что он делает запрос GET не идемпотентным — плохо. Несколько раз сделанный запрос — и каждый раз на сервере новое состояние. Может быть, эта же самая страница не изменится, но какой-нибудь рейтинг в другом месте ресурса может поменяться.
Подсчёт может делаться POSTом — он единственный не-идемпотентный метод с передачей данных.
Насчёт небезопасности GETа: да, я согласен, это плохо. Только мало смысла говорить о правильном использовании GET, если мы будем делать POSTом то, что нужно было делать DELETE, PUT или PATCH.
Вы говорите о том, что методы должны быть использованы правильно, но ветка-то комментариев про то, как быть, если это невозможно.
0
*RESTful
0
Спасибо за Вашу работу по изучению и обьяснению RFC (без сарказма).
Ваш текст, по хорошему, должен был присутствовать прямо в обсуждаемой статье )
Ваш текст, по хорошему, должен был присутствовать прямо в обсуждаемой статье )
0
Сколько читаю про идемпотентные запросы, так и не понимаю, как их реально можно применять в клиенте.
Вот допустим, хочу я добавить собачку владельцу. И тогда мне нужно сделать запрос
PUT /owner/123/dog/456
Вот откуда клиент может знать id нового, ещё не созданного, объекта? Получить от сервера предыдущим запросом? А если другой клиент опередит и создаст объект с таким id на миллисекунду раньше?
Если не передавать id и делать запрос PUT /owner/123/dog то сервер сам подставит новый id (да хоть AUTO_INCREMENT), и передаст его клиенту. Но это уже не идемпотентный запрос — десять таких запросов создадут десять объектов.
Выход вижу только в том, что делать нумерацию собачек не сквозную, а у каждого владельца с начала. Но такой подход тоже не всем подойдёт. Либо использовать PUT только для обновления данных, не создания (как, в принципе, в статье и предлагается). Но тогда разговоры об идемпотентности не стоят ни байта, о них написанного, ведь есть многие приложения, где не предполагается изменение данных (комменты, например).
Вот допустим, хочу я добавить собачку владельцу. И тогда мне нужно сделать запрос
PUT /owner/123/dog/456
Вот откуда клиент может знать id нового, ещё не созданного, объекта? Получить от сервера предыдущим запросом? А если другой клиент опередит и создаст объект с таким id на миллисекунду раньше?
Если не передавать id и делать запрос PUT /owner/123/dog то сервер сам подставит новый id (да хоть AUTO_INCREMENT), и передаст его клиенту. Но это уже не идемпотентный запрос — десять таких запросов создадут десять объектов.
Выход вижу только в том, что делать нумерацию собачек не сквозную, а у каждого владельца с начала. Но такой подход тоже не всем подойдёт. Либо использовать PUT только для обновления данных, не создания (как, в принципе, в статье и предлагается). Но тогда разговоры об идемпотентности не стоят ни байта, о них написанного, ведь есть многие приложения, где не предполагается изменение данных (комменты, например).
0
А если другой клиент опередит и создаст объект с таким id на миллисекунду раньше?
А другой клиент своим запросом получит другой ID. Сделать так, чтобы разные клиенты получили разные ID — проблема сервера.
Вообще PUT применим, если вы реально знаете конкретное место, на котором будет жить объект. А для комментов предполагается использовать POST.
-1
Ну вобщем предполагается что клиент генерит случайный ID из достаточно большого множества идентификаторов, что бы совпадения были исключены.
0
Сначала создаем новую собаку, потом добавляем ее владельцу.
0
А почему не (Json|XML)RPC?
Почему не SOAP?
Почему не SOAP?
-7
Хорошо бы ещё про средства документирования этих API упомянуть. Как то Swagger developers.helloreverb.com/swagger/
0
Ещё бы пару слов о возвращаемом JSON (best practices), нормализации (хотя это более общая тема, но постоянно вижу сервисы где запрос возвращает всё что можно), кешировании, ETAG, о том что возвращать в одном JSON поле то объект, то строку, то bool — плохо (постоянно такое вижу) и цены бы вашей и без того хорошей статье не было.
В любом случае спасибо!
В любом случае спасибо!
+1
Рекомендую заменить слово «паджинация» на «постраничность».
-4
не нашел ссылки на apiary.io/
оставлю тут
оставлю тут
0
api.* -> developers.*Навеяло Балмера.
dev.* -> developers.*
developer.* -> developers.*
+2
Смело берите HTTP коды ответов и сопоставляйте с ответами вашего API.
Предусмотрите в своем API параметр suppress_response_codes и сделайте его равным true по умолчанию.
Не стыкуется. Видимо, по умолчанию false.
0
Я тут в блоге своем писал о документации. В посте об этом важном вопросе — ничего
0
Only those users with full accounts are able to leave comments. Log in, please.
Разработка web API