Pull to refresh
375
0

Разрабатываю API более 10 лет

Send message
Тем не менее, ни о каких ожиданиях сильной/слабой консистентности в REST нет ни слова.
И с точки зрения общего архитектурного дизайна никак не могу с вами согласиться. Eventual consistency скорее норма в больших распределённых системах, но это вовсе не значит, что (а) её не нужно явно декларировать, (б) нельзя сделать механизмы для обеспечения контрактов. Версия/ETag ресурса, например, это контракт, с помощью которого и клиент может понять, что произошло, и сервер может нивелировать проблемы. Например:
1. Клиент перезаписывает PUT /entity/{id}
2. Сервер отвечает ревизией ресурса
3. При запросе GET /entity/{id} клиент указывает последнюю известную ему ревизию; сервер проверяет, какой ревизией располагает он, и, если она младше, либо перенаправляет вопрос в мастер, либо отвечает какой-то ошибкой, индицирующей «спроси ещё раз попозже».
А этого REST по Филдингу этого как раз не требует. Там написано следующее: «Cache constraints require that the data within a response to a request be implicitly or explicitly labeled as cacheable or non-cacheable.»
Что такое «implicitly labeled» читателю предлагается проработать самостоятельно.
> Это Вы сами додумали и перегнули палку.

Это практически дословная цитата, как она написана самим Филдингом.

A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media types that are appropriate for the intended audience (i.e., expected to be understood by any client that might use the API). From that point on, all application state transitions must be driven by client selection of server-provided choices that are present in the received representations or implied by the user’s manipulation of those representations. The transitions may be determined (or limited by) the client’s knowledge of media types and resource communication mechanisms, both of which may be improved on-the-fly (e.g., code-on-demand). [Failure here implies that out-of-band information is driving interaction instead of hypertext.]
> Под stateless подразумевается не то, что какая-то из сторон ничего не хранит, а только то, что стороны процесса не должны рассчитывать на то что другая сторона буквально «помит» их предыдущие запросы и принимать на основе этого какие-то решения.

Предыдущие запросы это растяжимое понятие. Любые данные, которые система хранит, есть результат предыдущих запросов (особенно если это какой-нибудь PUT, семантика которого явна говорит: положи вот это как есть по такому-то адресу). Если сервер не хранит вообще никаких данных о клиенте, то почти никакой полезный сервис на этом не построишь.

> Например клиент не должен ожидать от сервера какой-то особой «памяти» и считать, что раз он только что создал на сервере документ, то он 100% получит его обратно вторым запросом. Как будто сервер — это гардеробщик, который запомнил вас в лицо и выдаст куртку без предъявления номерка, и заодно он ещё волшебник — создаст куртку из воздуха, если её успели украсть (удалили документ).

Это как раз никакого отношения к REST не имеет. Клиент вправе ожидать то, что оговорено контрактом, в том числе сильной консистентности и неизменности «своих» данных, если таков контракт.

> Это ограничение направлено на возможность простого горизонтального масштабирования сервисов. Что бы не было проблем, если первый запрос клиента обработает первая нода кластера, а второй запрос прилетит на вторую ноду, которая ничего не знает про первый запрос.

Да, я не спорю и ниже это оговариваю. Мой пойнт только в том, что принцип сформулирован максимально общо и допускает совсем другие толкования.
> Кешировать ведь может не только клиент, а и любой промежуточный узел. И разработчик клиента и сервера должен про это помнить, и соответствующим образом управлять этим процессом.

Снова пойнт в том, что *любая* система хоть как-то кэширует данные, нельзя задизайнить её иначе.
> Если можно что-то сделать, то это не означает что это «разрешено». «Многослойность» архитектуры должна быть изначально заложена в неё,

Пойнт следующий: *нельзя* не заложить многослойность, её всегда можно сделать, среверинижирив сервер.

> что бы разработчики сервера и клиента всегда помнили об этом, и соответствующим образом писали код.

Во-первых, разработчики клиента об этом думать не должны.
А во-вторых, интересный вопрос как раз и заключается в том, а что заложить-то. Каким образом код писать?
Мммм, пожалуй, нет.
Нет, API Gateway совершенно отдельная концепция
microservices.io/patterns/apigateway.html
Прокси не может отрезать Authorization, а вот гейтвей ещё как может. Соответственно, кэши за гейтвеем вполне могут существовать.
Они и не получат, метода GET нет.
Возвращать в ответе представление сущности, которая была модифицирована запросом — стандартная практика для современного фронтенда. Как минимум для того, чтобы клиент знал все значения полей по умолчанию.
Понятия не имею, что эта мозилловская страничка должна значить. Вот стандарт datatracker.ietf.org/doc/html/rfc7231#section-4.3.4
> Так можно сказать про любой авторизованный GET запрос. Не думаю что вы предлагаете заменять все такие читающие запросы на POST просто чтобы «пробить кеш».

Именно это и скажу. Или у запроса должен появиться уникальный URL, или он должен уйти за POST.
Сохраняет в память и возвращает обратно, всё совершенно логично. Куда PUT должен сохранять — стандартом не оговорено. Если кто-то другой обратится к echo, то перезапишет это значение. PUT может иметь тело ответа, конечно.
Кстати, лучше всего ваш `echo` описывается глаголом PUT — что ты в него положил, то и получил.
Окей, просто представление. Слово «внутреннее» лишнее.

> Когда мы делаем GET habr.com/ru/post/560590

Ну вот URL мне намекает, что я получу всё-таки какой-то пост под идентификатором 560590, а не копию моего запроса в ответ. На всякий случай уточню, что веб-сайт != API, и принципы его работы чуть другие. Это, кстати, Филдинг отдельно оговаривает

> POST, как и GET, тоже возвращает представление

Ну да. Разница в том, что GET возвращает представление *адресуемой* сущности, а POST не обязательно; он как раз задизайнен чтобы предоставлять доступ к сущностям, не имеющим прямого URL.

> Я бы просто использовал для этого Cache-Control, а не подменял бы смысл запроса на POST, давая знать, что этот ресурс кроме чтения может иметь другие побочные эффекты.

Вы просто говорите, что индицировать одни эффекты (немодифицируемость, идемпотентность) считаете более важными, чем другие (некэшируемость, семантика глагола). Я же говорю, что тут нет общего правила, надо ориентироваться на здравый смысл применительно к конкретной операции.

Запрос «себя» должен (обязан) пробивать кэш, поскольку иначе можно получить доступ к чьим-то чужим данным. С моей точки зрения это важнее, чем индикация идемпотентности. Для операции типа echo — ну, начинается некоторая вкусовщина. Для меня следование семантике важнее, чем потенциальный неперезапрос от прокси.
> Ресурс /echo семантически не предназначен ни для чего другого, кроме извлечения информации

Я же с вами абсолютно не спорю. Я всего лишь говорю, что, обращаясь к нему таким образом, вас интересует не внутреннее представление самого ресурса echo, а результат некоторого алгоритма, на который ссылается /echo. А для этого предназначен POST.

> Этой заменой ничего не выиграете.

Неправда, я выигрываю то, что никакая промежуточная прокси не закэширует результат запроса.
Спасибо. Я потратил на неё очень много времени и усилий ;)
Давайте сошлёмся.
Any information that can be named can be a resource: a document or image, a temporal service (e.g. «today's weather in Los Angeles»), a collection of other resources, a non-virtual object (e.g. a person), and so on.

`/me` — это что, по-вашему? Документ или сервис?

Information

Rating
Does not participate
Location
Россия
Registered
Activity