Что за мода пошла? REST-like HTML CRUD

Вурдалак

Продвинутый новичок
Ну, не совсем. @grigori верно отметил, что REST в большей степени используется не на «полную», а лишь в лучшем случае на уровне level 2, поэтому мы видим тонну говно-API с CRUD-методами, ограниченными базовыми HTTP-глаголами. HATEOAS — интересная тема, правда даже в таком случае мне бы хотелось более явного RPC API а-ля GraphQL, потому что по моему опыту делать ресурсы а-ля /user/42/banUser получается геморройнее, чем если бы я просто отправлял команду BanUser с payload на определённый endpoint. Сюда же можно отнести чейнинг методов, с более-менее классическим RPC это делается проще. Я может ничего нового и не узнаю, но это может быть полезно другим, кто до сих пор верит в то, что POST /user/42 {"ban": true} — это true REST.

Есть ещё одна причина некоторой нелюбви к REST, но это больше опять-таки относится к недо-REST: вот эти споры на уровне «POST или PUT», «DELETE или POST» в случае неоднозначных команд. С обычными бизнес-глаголами о таком задумываться не будешь. Есть такие методы, которые могут просто что-то менять в сущности, но при стечении обстоятельств (например, expires < текущего времени) они удаляют сущность, и это вводит в ступор новичков и не только: разве POST должен удалять ресурс? Также никогда нельзя быть уверенным, что один и тот же глагол не придётся разбить на 2: сейчас мы удаляем только одним способом и пишем DELETE /users/42, а через год мы сможем насчитать их несколько + нужен некий payload (чего DELETE не поддерживает). Получается, нам по умолчанию всегда нужно писать POST /resource/action. А тогда спрашивается зачем нужен REST?

Ещё мне не очень нравится идея использовать HTTP-глагол в качестве бизнес-глагола (как с BAN): семантически, это именно HTTP-глагол. Если я хочу «отправить посылку» (post package), то столкнусь с тем, что вообще говоря, я понимаю под этим одно, а выглядит как самый обычный POST-метод. Упомянутый DELETE тоже имеет ограничения, хотя я, возможно, использую его в каком-то контексте как бизнес-глагол и хочу payload.

Ну и про PATCH хотелось послушать, раз уж @grigori его упомянул. Как по мне, так это тот же самый треш, что и POST/PUT/DELETE, который мешает явно выделить бизнес-действие.
 
Последнее редактирование:

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Тема достаточно объемная, прошу прощения за лаги в моих ответах.

@fixxxer, спасибо за историю, не знал ее :) Проблема с REST не в том, что идею искривили, а в том, что люди его не могут. Например, в корпорации, в которой принят хороший внутренний стандарт, разработчики предпочитают в документацию по API добавить раздел с описанием нарушений стандарта. А сделать RPC, и не морочить голову с REST - боятся.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Вот хороший пример, когда REST не вписывается в картину мира человека. Хорошо, что подняли тему, я так же думал, и мне долго объясняли.

Только причём тут состояние?

Мне кажется, каталог как-нибудь вывести можно и без HATEOAS. Да и навигация — это предпочтение клиента, разное количество выводимых ссылок, скролл вместо постраничной навигации и т.д.
Для начала, процитирую Фаулера по твоей ссылке.
use Restful web services to handle many of the integration problems that enterprises face
Не надо забивать гвозди логарифмической линейкой. REST удобен для интеграции enterprise-систем.
Для вывода каталога пользователям REST не нужен.
Реальный пример, где REST полезен. Мой проект. Мобильное приложение, которое использует много тысяч техников Ricoh(Toshiba, AT&T, etc), они ездят по стране и чинят принтеры, и каждый каждый человек загружает каталог запчастей. Одновременно десятки тысяч человек приходят на работу, включают приложение, и грузят каталог.
А часть из них бегает по лесам Амазонки со связью уровня 2g.

Нельзя просто так взять, и поменять RPC-протокол. У каждой компании своя версия аппликухи, и если методы в нее вшиты - это на много лет. А самоописывающийся REST c HATEOS - можно.

Предпочтения клиента и разное количество выводимых данных на страницу идут лесом, когда у тебя SLA на 100 тысяч одновременных раздач большого объема, и нет такого канала, который они не забьют. Пользователю приложение выведет данные, которые лежат на устройстве в IndexedDb, а грузиться все будет теми чанками, которыми удобно каналу. Хорошая связь, wifi в офисе - большие чанки, плохая скорость - маленькие чанки.
 
Последнее редактирование:

grigori

( ͡° ͜ʖ ͡°)
Команда форума
На мой взгляд, это выглядит интереснее, когда речь идёт про ограничение списка команд: объявление о продаже можно отредактировать, но только если оно не забанено; опубликовать статью можно, только если она одобрена и т.д. Как в конечном автомате у тебя есть состояние (нет, это не то состояние, о котором написано в Википедии) и список допустимых переходов.
У тебя есть состояние ресурса, которое ты можешь узнать на момент запроса.
Между вызовами консистентность состояния между клиентом и сервером не обеспечивается.
Я не люблю слово "идемпотентность", поэтому говорю про состояние, но мы об одном.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
вот эти споры на уровне «POST или PUT», «DELETE или POST» в случае неоднозначных команд. С обычными бизнес-глаголами о таком задумываться не будешь. Есть такие методы, которые могут просто что-то менять в сущности, но при стечении обстоятельств (например, expires < текущего времени) они удаляют сущность, и это вводит в ступор новичков и не только: разве POST должен удалять ресурс? Также никогда нельзя быть уверенным, что один и тот же глагол не придётся разбить на 2: сейчас мы удаляем только одним способом и пишем DELETE /users/42, а через год мы сможем насчитать их несколько + нужен некий payload (чего DELETE не поддерживает). Получается, нам по умолчанию всегда нужно писать POST /resource/action. А тогда спрашивается зачем нужен REST?

Ещё мне не очень нравится идея использовать HTTP-глагол в качестве бизнес-глагола (как с BAN): семантически, это именно HTTP-глагол. Если я хочу «отправить посылку» (post package), то столкнусь с тем, что вообще говоря, я понимаю под этим одно, а выглядит как самый обычный POST-метод. Упомянутый DELETE тоже имеет ограничения, хотя я, возможно, использую его в каком-то контексте как бизнес-глагол и хочу payload.
Синдром поиска глубинного смысла в HTTP-глаголах - это забавно.
Я смотрю на стандарт, личные предпочтения - то такое.
Это что-то вроде вопроса на какой строке ставить скобку. Как напишешь в стандарте своей компании - так и должно быть.
Фаулер привел пример подхода.
Выделяем доменные сущности: врачи, слоты, статусы. Нравится или нет, в REST статус - это отдельный ресурс.
В твоем случае, есть статус поста. POST должен менять ресурс.

Ну и про PATCH хотелось послушать, раз уж grigori его упомянул. Как по мне, так это тот же самый треш, что и POST/PUT/DELETE, который мешает явно выделить бизнес-действие.
процитирую тебе корпоративный стандарт:
3.1.1.5 Recommend: PATCH to update a portion of a document resource. POST tunneling is an alternative.
PUT is a complete replacement of the resource. Often times, partial update is preferable. However, there is no standard partial update HTTP method defined in the HTTP 1.1 specification. PATCH method was mentioned in HTTP 1.0, but not commonly implemented. As an alternative, POST tunneling could be provided to support partial update, such as:

POST /hcmCoreResources/11.1.5/departments/12 HTTP/1.1
Host: cloud.oracle.com
X-HTTP-Method-Override: PATCH
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Нельзя просто так взять, и поменять RPC-протокол. У каждой компании своя версия аппликухи, и если методы в нее вшиты - это на много лет. А самоописывающийся REST c HATEOS - можно.
Ты ошибаешься.
«Правильный» REST — этот тот же RPC, просто команды/запросы оформлены в виде URI.
«Изменения» в RPC и REST имеют одни и те же проблемы: ты можешь добавить новые команды/запросы, но старые просто так поменять не получится из-за BC.
Приложение всё равно нужно «научить» работать с новыми командами.
Тебе ничего не мешает в JSON RPC добавить те же rels/links, просто в качестве «реализации» ресурса будут не URI, а команды/запросы в более явном виде.
«Самоописывающий» API просто интересен тем, что ты можешь сэкономить на документации, не более. Прямого отношения к RPC/REST это не имеет.

У меня есть опыт поддержки API, который включал в себя динамические элементы меню в iOS/Android приложения, т.е. чтобы можно было добавлять что-то в уже зарелизенное приложение. И я могу сказать, что это был ужасный опыт, потому что нативные приложения так или иначе пытались кастомизировать под свой дизайн и под своим нужды этот API, т.е. он был очень условно обратно-совместимый. Проще уж WebView встроить.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Смысл HATEOS в том, что приложение сможет обработать изменение REST API. Конечно, сущности это не поменяет, но добавить в выдачу новые сущности или переименовать можно без проблем.
<link rel = "/linkrels/appointment/cancel" uri = "/slots/1234/appointment"/>
в
<link rel = "/linkrels/appointment/unset" uri = "/slots/1234/appointment"/>


Гибкость не бесплатна, конечно.
 

Вурдалак

Продвинутый новичок
процитирую тебе корпоративный стандарт:
Что именно ты хотел этим сказать? Меня не интересует API, который оперирует понятием ресурса, как просто пачкой свойств, которую я могу полностью «заменить» или «частично проапдейтить». Мне клиент должен сообщить что он хочет (забронировать, отменить заявку, переназначить), а не status = closed, потому что в status = closed не отражает мотивации.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
хорошо, я не уговариваю, не интересует - не надо
это разные подходы, и в твоих словах есть смысл

REST предполагает, что логика клиента отделена от логики сервера полностью.
 

Вурдалак

Продвинутый новичок
Смысл HATEOS в том, что приложение сможет обработать изменение REST API. Конечно, сущности это не поменяет, но добавить в выдачу новые сущности или переименовать можно без проблем. Гибкость не бесплатна, конечно.
Ты всего лишь пытаешься повторить то, что говорит автор REST; с практикой это никак не соотносится. Если приложение будет лишь лёгкой обёрткой над таким «вебообразном» API, то это будет абсолютно примитивное приложение, которое не может обогатиться, красиво отобразить какое-то действие и т.д. в силу того, что оно слепо; оно ничего не понимает о том, что реально происходит через API. Любая попытка его обогатить ставит крест на самой идее, что API в любой момент можно поменять. С таким же успехом можно написать одно-единственное универсальное приложение, которое будет работать для любого REST API с HATEOAS, но цена такой универсальности понятна: это будет тупой FTP-менеджер, а не богатое приложение.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
да, повторю: REST - это набор ограничений, выработанный с целью изоляции интегрируемых систем

когда нужна связанность и общая логика, изоляция не подходит
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Окей. Чем такое приложение лучше, чем просто WebView на всё приложение?
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
для webview нет стандарта
для "богатого" протокола есть SOAP и JSON-RPC 2, а "бедный" - вот, REST
иногда нужно
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Почему же — HTML/CSS/JavaScript. Браузеры как раз этот набор стандартов и реализуют. Приложение, которое использует WebView, будет супер-универсально.

Ты можешь возразить — приложение может реализовать что-то кастомно. А не может, потому что API в любой момент может измениться, поэтому оно должно быть тупым и не возникать.

С моей точки зрения, тут очевидный самообман: если API может в любой момент как угодно поменяться, то приложение должно быть слепым. Если уж оно слепо, то мы получаем просто браузер. А раз мы получаем браузер, то для этого уже есть вполне себе развитые технологии. Где конкретно ошибка в этой логической цепочке?
 

Вурдалак

Продвинутый новичок
Так ведь не ты реализуешь всю сложность браузеров; за тебя это давно это сделали. CSS можешь и не использовать.

Ты реально уверен, что те приложения, про которые ты говоришь из своей практики, не хардкодили ни в каком виде у себя последовательности appointment -> cancel для отображения, скажем, большой красной кнопки со словом «Отменить» с переводами на стороне приложения?
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Я совершенно уверен, что тот код, который мы писали для работы с API, ничего не выводит. Я даже хотел вынести это в web worker, но оказалось, некоторые браузеры не могут в web worker-е писать в indexedDb.
Данные пишутся в базу сами по себе, другой "процесс" их читает и парсит в оперативку, а вывод - это отдельная задача.

Представь, что REST используется для синхронизации данных между базами, а не для пользователя/
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Тогда каким образом программист, использующий API для отправки данных в твою базу, должен написать код таким образом, чтобы вы в любой момент могли поменять API без нарушения BC? У вас там есть, условно, тот же appointment -> cancel, программист такой: «окей, мы тут делаем отмену и для синхронизации им отправим соответствующий запрос». Тут приходишь ты, меняешь cancel на unset, cancel пропадает, у первого программиста в логах появляются 404.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
это уже заход на 2й круг

Level 1 - Resources
The first step towards the Glory of Rest in the RMM is to introduce resources. So now rather than making all our requests to a singular service endpoint, we now start talking to individual resources.
У тебя есть локальное представление ресурса - пост, в нем есть ссылка на родительский ресурс - список постов (кроме того, из него был получен адрес поста).
Если клиент хранит представление ресурса, включая метаданные — он обладает достаточной информацией для модификации или удаления ресурса.
Состояние ресурса может измениться, и клиент к этому готов.

Если пост уже удален - возвращай 404, клиент может перечитать список доступных постов.
Если ты изменил адресацию, и хочешь уведомить об этом клиентов - возвращай редирект. Ваш, КО.
 
Последнее редактирование:

Вурдалак

Продвинутый новичок
Если ты изменил адресацию, и хочешь уведомить об этом клиентов - возвращай редирект. Ваш, КО.
Так если тебе видятся преимущества REST + HATEOAS перед каким-то нибудь JSON RPC в том, что ты можешь строку «cancel» заменить на «abandon», то ты можешь и в JSON RPC в Error object добавить информацию «мы переименовали этот метод в abandon, повтори запрос». И вот ради этого ты собирал 50 человек или была причина серьёзнее?

REST предполагает, что логика клиента отделена от логики сервера полностью.
Разве это как-то противоречит тому, что я хочу явные команды вместо status = closed? Это просто язык общения между клиентом и сервером и он должен быть максимально понятным. Загадочность и двусмысленность ценится в религии, а не в программировании.
 
Сверху