Пилим мы REST-сервисы для проекта. Стараемся следовать рекоммендациям для RESTful-архитектур, но иногда не получается. Вот, например, намедни коллега добавила метод с такой сигнатурой:
[HttpPost]
[Route("ValueSet/$expand")]
public FHIR.Model.Bundle GetValueSetMembersByOIDs(IEnumerable<string> oids)
На что я ей заметил, что метод, запрашивающий данные, должен использовать GET-запрос, а не POST. Коллега не соглашается, заявляет, мол, "я много раз так делала и всё нормально".
Вопрос — а допустимы ли такие фокусы в правильных REST-сервисах? Ну т.е. мне вот с одной стороны бросается в глаза такой косяк, но в то же время в описаниях REST-архитектур я не смог найти запрета на использование POST запросов вместо GET, для запроса данных.
Далее.. позвал я ихнего техлида и он мне пояснил причину такого кода — в кач-ве параметра к запросу передаётся массив идентификаторов. Каждый идентификатор — около 30 символов. Если передавать их как параметры GET-запроса, то легко массив из 60 идентификаторов займёт уже пару килобайт и они не уверены, не превысит ли это лимитов длины HTTP-запроса.
Вот второй, более важный вопрос — как такие вещи делаются по-человечески? Вот если нужно поддерживать GET-запросы, в которых длина аргументов тянет на пяток килобайт, как быть? Требовать разбивать запросы? Или плюнуть и запрашивать данные через POST-запрос, передавая параметры в теле запроса? Или настраивать Web-сервер, чтоб увеличить максимальную длину запроса?
Посоветуйте. Кто-нибудь наверняка ведь сталкивался с ситуацией, когда для запроса данных нужно передать несколько десятков параметров. Может какие-то примеры есть среди общеизвестных REST-сервисов?
Здравствуйте, Artem Korneev, Вы писали:
AK>Вопрос — а допустимы ли такие фокусы в правильных REST-сервисах? Ну т.е. мне вот с одной стороны бросается в глаза такой косяк, но в то же время в описаниях REST-архитектур я не смог найти запрета на использование POST запросов вместо GET, для запроса данных.
Учитывая все несовершенсво http-протокола — да. Если url может стать больше 2048 символов,
то неважно передаем мы массив или просто несколько параметров — нам надо юзать post.
Вот напр. если имя пользователя может превышать 1000символов и фамилия больше 1000
и вся остальная часть url=96 символов,
а нам надо сделать запрос ввида ?name=xxx&secondname=yyy — нужно делать только post запрос.
Если переделать http, для современного веба, то по правильному http get — надо убать вообще.
http post надо кешировать как сейчас http get,т.е. кешировать на время указанное сервером.
к http post добавить флаг (IsUrlCanBeWrittenByHands)- специально для броузеров и поисковиков,
указывающий что этот запрос надо показать пользователю ввиде читаемого url.
Підтримати Україну у боротьбі з країною-терористом.
Здравствуйте, Artem Korneev, Вы писали:
AK>Вот второй, более важный вопрос — как такие вещи делаются по-человечески? Вот если нужно поддерживать GET-запросы, в которых длина аргументов тянет на пяток килобайт, как быть? Требовать разбивать запросы? Или плюнуть и запрашивать данные через POST-запрос, передавая параметры в теле запроса? Или настраивать Web-сервер, чтоб увеличить максимальную длину запроса?
AK>Посоветуйте. Кто-нибудь наверняка ведь сталкивался с ситуацией, когда для запроса данных нужно передать несколько десятков параметров. Может какие-то примеры есть среди общеизвестных REST-сервисов?
Вообще говоря POST предназначен для созданиям объектов, т.е. он ради сайд эффектов. Особого криминала приспособить его для получения объектов в случае когда на серевер отправляется много параметров запроса тоже не вижу. Но желательно пересмотреть архитектуру, например когда надо сделать запросы по ид от 1 до 10, то оформлять в виде диапазонов и пихать в querystring. Тут можно поглядеть обсуждение.
Здравствуйте, #John, Вы писали:
J>Если переделать http, для современного веба, то по правильному http get — надо убать вообще. J>http post надо кешировать как сейчас http get,т.е. кешировать на время указанное сервером. J>к http post добавить флаг (IsUrlCanBeWrittenByHands)- специально для броузеров и поисковиков, J>указывающий что этот запрос надо показать пользователю ввиде читаемого url.
Вот есть операции, универсальные для любого объекта -- получить, добавить, удалить и изменить. Зачем нужно удалять операцию "получить", базовую по сути?
Здравствуйте, Artem Korneev, Вы писали:
AK>Пилим мы REST-сервисы для проекта. Стараемся следовать рекоммендациям для RESTful-архитектур, но иногда не получается. Вот, например, намедни коллега добавила метод с такой сигнатурой
Вообще, если речь идёт о fhir, то в его спецификации прямо все расписано, как делать запросы и каким http-методом, разве не?
Согласно спецификации можно и GET и POST
Здравствуйте, Sharov, Вы писали:
S>Вот есть операции, универсальные для любого объекта -- получить, добавить, удалить и изменить. Зачем нужно удалять операцию "получить", базовую по сути?
Потому что в современном вебе http исспользуется не по назначению.
Был когда-то создан ftp протокол для обмена документами через сервак
и у него были вполене логичные комманды для манипулирования доками на серваке.
Но прошлое некоторое время, люди увидели что ftp — это слишком сложно: надо скачать документ,
потом проанализировать его название, содержимое, подобрать программу которая бы понимала документ,
открыть, прочитать. Этим людям нравилось просматривать гипертекстовые страницы(html+теги), а не маны,
так что бы все ключевы слова были выделенны, заголовки подчеркнуты
и можно было быстро легко переходить на другие документы по ссылкам.
Прибилизительно в тоже время кто-то изобрел мышку и этим людям оч. понравилось ею клацать.
И они решили создать более простой протокол который работал бы как ftp, но только для просмотра документов
определенного типа: гипертекстовых страниц.
Прошло время(развивался html -добавлялись новые теги),
люди захотели вставлять смишнявки(картиночки) в html, а также изменять содержимое этих документов,
что бы люди всегда получали самую актуальную информацию: были добавлены методы put, delete,
а чтобы серевер могу выдержать нагружку в 10человек, при дорогущем итернете, был запилем метод post.
Но в то время, наши прадеды и не догадовали, во что может превратиться их безобидная шутка с гипертекстовыми страницам.
Однажды(будущий создатель js) подумал: пользователи загружают статические странички, но они такие скучные:
"Было бы классно, если бы картиночки прыгали вверх-вниз, а текст мигал."
"Вот круто разыграю своего товарища" — подумал человек и создал js.
Если бы созадатели html+http+js только знали что в начале 21го века, компьютеры сами будут скачивать не доверенный,
не проверенный, не лицинзионный исполняемый код(js), который будет манипулировать пользовательскими данными.
А на js будут создаваться полноценные ria single page mvpmvvmmvc приложения с rest-full серверной архитектурой,
они бы никогда, никогда так не шутили.
Если кратко:
http — не создавался под нужды современного веба, а был адаптирован из безобидной шутки.
Підтримати Україну у боротьбі з країною-терористом.
Здравствуйте, #John, Вы писали:
J>Здравствуйте, Sharov, Вы писали:
S>>Вот есть операции, универсальные для любого объекта -- получить, добавить, удалить и изменить. Зачем нужно удалять операцию "получить", базовую по сути? J>Потому что в современном вебе http исспользуется не по назначению.
J>Если кратко: J>http — не создавался под нужды современного веба, а был адаптирован из безобидной шутки.
REST как раз и призван это исправить и как-то упорядочить.
Здравствуйте, Artem Korneev, Вы писали:
AK>Посоветуйте. Кто-нибудь наверняка ведь сталкивался с ситуацией, когда для запроса данных нужно передать несколько десятков параметров. Может какие-то примеры есть среди общеизвестных REST-сервисов?
Передавал параметры в сервис в закодированном виде, по максимум убрав лишнюю информацию (там много чего можно сократить и выбросить). Аргумент у метода сервиса был простой строкой, которая парсилась и превращалась обратно в массив. Что-то вроде "<количество id>_<id1>_<id2>" и это здорово сокращало длину URL.
Здравствуйте, Vladek, Вы писали:
V>Игрался с параметрами в Web.config
Норм. вариант, если делается типичное энтерпрайз single-page js-приложение,
работающее в одном из браузеров хрома, версии 50.x.x.x.x, max length
V>Аргумент у метода сервиса был простой строкой, которая парсилась и превращалась обратно в массив. Что-то вроде..
Норм. вариант. как частный случай для небольшого массива.
Что бы в js не пришлось вручную парсить, массивы обычно передают ввиде json arr(где ','-разделитель).
Підтримати Україну у боротьбі з країною-терористом.
Здравствуйте, #John, Вы писали:
J>Если переделать http, для современного веба, то по правильному http get — надо убать вообще. J>http post надо кешировать как сейчас http get
Зачем такие сложности? Поддержка передачи параметров в "body" вполне решила бы проблему с длиной URL.
Здравствуйте, Artem Korneev, Вы писали:
J>>Если переделать http, для современного веба, то по правильному http get — надо убать вообще. J>>http post надо кешировать как сейчас http get
AK>Зачем такие сложности? Поддержка передачи параметров в "body" вполне решила бы проблему с длиной URL.
Только по стандарту это может не работать. Если ты передаёшь body в get, то сервер SHOULD игнорировать. Информация возвращаемая должна зависеть только от url. https://tools.ietf.org/html/rfc2616#section-4.3
if the request method does not include defined semantics for an entity-body, then the message-body SHOULD be ignored when handling the request.
The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI.
Т.е. один из сильных аргументов rest "а давайте чтобы всё быстрее заработало — поставим обычный кеширующий http прокси" — вдруг всё поломает, т.к. прокси (да и любые другие компоненты) может игнорировать тело get и иметь ограничение на длину url.
Как последнее средство — попробовать передизайнить ваш API чтобы не приходилось слать большие списки в get.
Ещё можно свой http verb ввести, это стандарт позволяет.
Так что при всём богатстве выбора — альтернативы post нет.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, ·, Вы писали:
AK>>Зачем такие сложности? Поддержка передачи параметров в "body" вполне решила бы проблему с длиной URL. ·>Только по стандарту это может не работать.
Здравствуйте, Artem Korneev, Вы писали:
AK>·>Только по стандарту это может не работать.
AK>Разумеется. Поэтому я и написал "решила бы".
Тут ведь как. Если подумать, то это ограничение get возникло не на пустом месте. Оно позволяет кеши, балансеры и прочее реализовывать эффективно. Кешировать по небольшому урлу имеет смысл, а для произвольного размера body вряд ли получится сделать что-то полезное.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, α, Вы писали:
AK>>Пилим мы REST-сервисы для проекта. Стараемся следовать рекоммендациям для RESTful-архитектур, но иногда не получается. Вот, например, намедни коллега добавила метод с такой сигнатурой α>Вообще, если речь идёт о fhir, то в его спецификации прямо все расписано, как делать запросы и каким http-методом, разве не? α>Согласно спецификации можно и GET и POST
Да.. но там всё-таки POST используется для "search", т.е. выборки по каким-то фильтрам, а не просто для получения по ID.
Вот для запроса по одному ID там по спецификации требуется GET. А запроса по нескольким ID в спецификации нет — это мелкие дополнения нашей платформы. Всё-таки мы склоняемся к тому, что запрос по нескольким идентификаторам тоже должен быть GET.
Здравствуйте, Vladek, Вы писали:
V> Игрался с параметрами в Web.config
По-моему, нету у нас web.config Ну или я его не нашёл.
Шаблон проекта у нас не совсем обычный. Мы пилим микросервисы под Azure Service Fabric. А оно поддерживает self-hosted WebAPI сервисы через nuget-пакеты Owin. Т.е. там потом приложение запускается на кластере Azure Service Fabric и уже из него стартует WebAPI-сервис. Файла web.config там нет.
Наверное можно ещё какими-то параметрами инициализации в коде поиграться чтоб добиться того же эффекта. Но я вчера опытным путём установил, что сервис нормально принимает строки до 16Kb — вполне достаточно для наших нужд. Правда, я это тестировал на локальном эмуляторе Azure Service Fabric. Но вроде на то он и эмулятор чтоб предоставлять примерно тот же рантайм, что и реальное облако. В любом случае, надо будет ещё в облаке всё это потестировать.
Здравствуйте, ·, Вы писали:
·>Кешировать по небольшому урлу имеет смысл, а для произвольного размера body вряд ли получится сделать что-то полезное.
Так принципиальной разницы нет — урл тоже теоретически безразмерный. Стандарт не ограничивает. На практике установлено, что ASP.NET поддерживает, как минимум, ~16Kb в параметрах URL. А при желании можно и больше настроить.
Здравствуйте, Artem Korneev, Вы писали:
AK>·>Кешировать по небольшому урлу имеет смысл, а для произвольного размера body вряд ли получится сделать что-то полезное.
AK>Так принципиальной разницы нет — урл тоже теоретически безразмерный. Стандарт не ограничивает. На практике установлено, что ASP.NET поддерживает, как минимум, ~16Kb в параметрах URL. А при желании можно и больше настроить.
Зато он говорит, что можно выкинуть 414 uri too long, а значит это может сделать любой промежуточный софт на пути http. Т.е. основывать свой rest api на произвольной длины урлах я бы не советовал.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Artem Korneev, Вы писали:
AK>Вопрос — а допустимы ли такие фокусы в правильных REST-сервисах? Ну т.е. мне вот с одной стороны бросается в глаза такой косяк, но в то же время в описаниях REST-архитектур я не смог найти запрета на использование POST запросов вместо GET, для запроса данных.
С мотивацией "ну ведь в GET может не влезть" категорически недопустимы.
AK>Вот второй, более важный вопрос — как такие вещи делаются по-человечески? Вот если нужно поддерживать GET-запросы, в которых длина аргументов тянет на пяток килобайт, как быть? Требовать разбивать запросы? Или плюнуть и запрашивать данные через POST-запрос, передавая параметры в теле запроса? Или настраивать Web-сервер, чтоб увеличить максимальную длину запроса?
По человечески это делается через POST метод %create_operation%, который принимает аргументы операции и возвращает токен. Затем по этому токену можно получить результат. Достоинства: идеологически верно, гарантированно не вызывает проблем с HTTP миддлварью, позволяет безболезненно прикрутить CQRS в реализации.
P>По человечески это делается через POST метод %create_operation%, который принимает аргументы операции и возвращает токен. Затем по этому токену можно получить результат. Достоинства: идеологически верно, гарантированно не вызывает проблем с HTTP миддлварью, позволяет безболезненно прикрутить CQRS в реализации.
Блин, вот реально огромная благодарность за столь очевидный, но мне в свое время
P>По человечески это делается через POST метод %create_operation%, который принимает аргументы операции и возвращает токен. Затем по этому токену можно получить результат. Достоинства: идеологически верно, гарантированно не вызывает проблем с HTTP миддлварью, позволяет безболезненно прикрутить CQRS в реализации.
Между запросами эта операция должна храниться на сервере? Едва ли это идеологически верно.