Но здесь минус — теряется ясность мысли. К примеру, если заказ можно только подтвердить и метод предназначен только для этого.
Еще пример:
POST notifications/1/resend
Ясно что переотправка оповещения. К примеру, если не пришло СМС — отправить повторно (считаем что на сервере по какой-либо причине статус отпавки — не определено и вынуждены доверять пользователю после 30 сек.).
Не комильфо. По канону будет так:
POST notifications/1/sending-attempt
Т.е. как бы создаем новую попытку отправки. По идее и таблицу попыток нужно бы добавить, но можно пока и без таблицы — главное API.
Ты или придерживается REST и тогда используешь его как положено, либо не придерживаешься и делаешь всё как бог на душу положит. Тут нет правильного или не правильного ответа, просто не надо называть вещи не своими именами.
Вроде как у тебя понимание REST есть, но "теряется ясность мысли" — это я не понимаю. Что значит теряется. REST это не про ясность, это про определённые гарантии, в основном про идемпотентность. То бишь можно ли данный запрос повторять, если возникла ошибка или нельзя.
Здравствуйте, vsb, Вы писали:
vsb>Вроде как у тебя понимание REST есть, но "теряется ясность мысли" — это я не понимаю. Что значит теряется. REST это не про ясность, это про определённые гарантии, в основном про идемпотентность. То бишь можно ли данный запрос повторять, если возникла ошибка или нельзя.
См. второй пример — оба варианта POST. Вопрос в другом — глагол или имя существительное.
В первом примере POST и PATCH. Оба считаются не идемпотентыми.
Здравствуйте, Shmj, Вы писали:
S>Но здесь минус — теряется ясность мысли.
S>Как вы на все это смотрите?
Как уже выше отметили, практическая ценность REST только в идемпотентности по большому счёту. Что там тебе кажется логичным и последовательным в именовании — это твоя личная "логичность и последовательность", не стоит заблуждаться по этому поводу.
Если мне от твоего сервиса что-то надо, я к тебе приду и спрошу что там надо дёргать (потому что во-первых см. выше, во-вторых стопудово документации нет). Если тебе так не нравится — задокументируй, читаю я хорошо
vsb>В твоём случае нет никакой причины ему быть не-идемпотентным.
Подтвердить заказ, который только что отменили — должно вызвать ошибку. vsb>POST во втором случае не нужен. Делай PUT notifications/1/attempt/{uuid} и повторяй его, если получена ошибка (кроме 4xx).
Ключ идемпотентности можно применить и к POST. Вкусовщина.
Здравствуйте, rosencrantz, Вы писали:
R>Как уже выше отметили, практическая ценность REST только в идемпотентности по большому счёту.
Идемпотентность не специфична только для REST.
R>Что там тебе кажется логичным и последовательным в именовании — это твоя личная "логичность и последовательность", не стоит заблуждаться по этому поводу.
Не путаю. Я поэтому и написал "обычно". Да, можно спроектировать API, в котором PATCH не-идемпотентен (я привёл такой пример в своём сообщении). Хотя лично я считаю, что это ошибка и такие API проектировать не надо. PUT и PATCH нужно делать идемпотентными, а все не-идемпотентные операции переложить на POST и там можно даже не притворяться, что у нас REST, а просто писать, как удобно, всё равно там никаких гарантий. Но в идеале таких операций быть не должно.
vsb>>В твоём случае нет никакой причины ему быть не-идемпотентным.
S>Подтвердить заказ, который только что отменили — должно вызвать ошибку.
Это можно о любой операции сказать. Идемпотентность не означает, что эта операция всегда срабатывает. PUT тоже не будет работать, если это нарушает какие-то условия уникальности, к примеру. Если возникают проблемы, ты возвращаешь ошибку 4xx, которую уже можно и нужно показывать пользователю в каком-то виде, ну или ещё как-то обрабатывать, но не повторять,
S>Ключ идемпотентности можно применить и к POST. Вкусовщина.
Можно, но принято эту семантику накладывать на PUT.
Даже и не пытался спорить. Я про твои логичные последовательные попытки нагородить эту онтологию из confirmation, notification, notification-attempt, и т.д.
RESTFul API — это не стандарт, не спецификация, т.е. никто не накладывает жестких ограничений
лучше всего делать так, как удобно вам, т.к. в подавляющем большинстве случаев речь идет об АПИ в рамках одной компании, т.е. для связи бэка и фронта/мобилок
это обычно обходится быстрее и дешевле, т.к. меньше времени уходит на буквоедство, а фичи быстрее появляются в продакшене; сам проходил через это, когда обсуждали очень долго как лучше назвать, какой глагол (GET/PUT/PATCH) использовать, как правильно/не правильно, вместо того, чтобы забить на это и сделать так, как НАМ удобно самое важное: чтобы была консистетность в рамках одного АПИ + документация к этому
Уместно будет вспомнить байку про goto
Стадии профессионального развития разработчика.
1. следует правилу от более опытных коллег "никогда не используй goto"
2. иногда, если нужно, можно использовать goto
3. наставляет молодых коллег словами "никогда не используй goto"
Конкретно по примерам. S>
S>POST orders/1/confirm
S>
Я бы сделал (в порядке убывания вероятности):
1.
PUT orders/1/status
body
{
"status": "confirmed"
}
2.
PUT orders/1/confirm
3.
GET orders/1/changeStatus?status=confirmed
S>
S>POST notifications/1/resend
S>
Я бы сделал одним из следующих вариантов (как конкретно — зависит от контекста и способов применения вызовов):
1.
POST notifications/clone
body
{
"fromId": 1
}
2.
POST notifications
body
{
тут вручную созданный клон нотификации с ID=1
}
3.
POST notifications/1/operations
body
{
"operation": "resend"
}
Опять же, самое главное: чтобы была прописана договоренность бэка и фронта, как максимально удобно и тем и другим, и второе — придерживаться консистентности в рамках одного АПИ.
В случае, когда делаем публичный АПИ, который идет на продажу, либо им будут пользоваться тысячи клиентов опен-осерса, то имеет смысл потратить больше времени на обдумывания и приведения к "более каноничному" стилю.
vsb>Не путаю. Я поэтому и написал "обычно". Да, можно спроектировать API, в котором PATCH не-идемпотентен (я привёл такой пример в своём сообщении). Хотя лично я считаю, что это ошибка и такие API проектировать не надо. PUT и PATCH нужно делать идемпотентными, а все не-идемпотентные операции переложить на POST и там можно даже не притворяться, что у нас REST, а просто писать, как удобно, всё равно там никаких гарантий. Но в идеале таких операций быть не должно.
S>>Подтвердить заказ, который только что отменили — должно вызвать ошибку.
vsb>Это можно о любой операции сказать. Идемпотентность не означает, что эта операция всегда срабатывает. PUT тоже не будет работать, если это нарушает какие-то условия уникальности, к примеру.
PUT должен отработать всегда — какая уникальность? Если сущность с таким ключом существует — то просто заменяете ее и все. Если нельзя заменить — то не обманывайте не притворяйтесь PUT-ом.
S>>Ключ идемпотентности можно применить и к POST. Вкусовщина. vsb>Можно, но принято эту семантику накладывать на PUT.
Это не мои попытки — это рекомендации многих уважаемых авторитетов. Ссылку вам дал.
Если авторитеты говорят — значит это всем должно быть понятно и проблем не вызовет. Когда городишь отсебятину — тогда да, сложно будет понять что ты там нагенерил в своей голове.
Здравствуйте, DiPaolo, Вы писали:
DP>это обычно обходится быстрее и дешевле, т.к. меньше времени уходит на буквоедство, а фичи быстрее появляются в продакшене; сам проходил через это, когда обсуждали очень долго как лучше назвать, какой глагол (GET/PUT/PATCH) использовать, как правильно/не правильно, вместо того, чтобы забить на это и сделать так, как НАМ удобно
Это да, но кроме создания проекта — вы нарабатываете хороший стиль кодирования. Т.е. привыкаете сразу либо делать правильно, либо делать не правильно. В будущем вы всегда будете делать как привыкли и тратить время одинаково либо на понятный стиль либо на непонятный никому кроме вас — просто как привыкните. До пенсии лет 25 еще есть, по этому раз 10 придется еще писать API — есть смысл наработать стиль. А может еще и на пенсии что-то придется писать. Смысл есть.
DP>Я бы сделал (в порядке убывания вероятности):
DP>1. DP>
Почему не PATCH?
DP>Опять же, самое главное: чтобы была прописана договоренность бэка и фронта, как максимально удобно и тем и другим, и второе — придерживаться консистентности в рамках одного АПИ.
Обычно фронт говорят — как сделаете так и будем юзать, решайте сами. По этому и хотелось бы по стандартам. И вообще выработать стиль на будущее.
DP>В случае, когда делаем публичный АПИ, который идет на продажу, либо им будут пользоваться тысячи клиентов опен-осерса, то имеет смысл потратить больше времени на обдумывания и приведения к "более каноничному" стилю.
Будешь меньше тратить времени, если сейчас заморочишься с этими вопросами.
Здравствуйте, Shmj, Вы писали:
S>PUT должен отработать всегда — какая уникальность? Если сущность с таким ключом существует — то просто заменяете ее и все. Если нельзя заменить — то не обманывайте не притворяйтесь PUT-ом.
Про то, что PUT должен отработать всегда — я в первый раз слышу. PUT /resource1/{resource1Id}/resource2/{resource2Id} тоже должен отработать всегда, создавая resource1 если он был удалён?
В общем не согласен. Всегда есть правила валидации и они могут изменяться во времени.
Здравствуйте, vsb, Вы писали:
vsb>В общем не согласен. Всегда есть правила валидации и они могут изменяться во времени.
Как вы считаете, почему PUT гарантированно идемпотентен а PATCH — нет?
Выше вы писали:
vsb>Это можно о любой операции сказать. Идемпотентность не означает, что эта операция всегда срабатывает. PUT тоже не будет работать, если это нарушает какие-то условия уникальности, к примеру.
Т.е. имели ли вы в виду, что первый запрос PUT — сработал, т.к. записи с таким ID не было. А второй PUT — уже не сработал, т.к. запись с таким ID уже есть в системе? Или о какой уникальности речь?
S>Т.е. привыкаете сразу либо делать правильно, либо делать не правильно.
В РЕСТе нет такого: правильно/неправильно. Есть рекомендации.
S>В будущем вы всегда будете делать как привыкли и тратить время одинаково либо на понятный стиль либо на непонятный никому кроме вас — просто как привыкните.
Ну я ж специально по этому поводу два раза подчеркнул: важно прописать стиль и придерживаться его.
S>До пенсии лет 25 еще есть, по этому раз 10 придется еще писать API — есть смысл наработать стиль. А может еще и на пенсии что-то придется писать. Смысл есть.
Ну вот все что я вам написал выше — оно уже основано на многолетнем опыте написания АПИ, и не только РЕСТа.
S>Почему не PATCH?
Меньше мороки. Фронтам скорее всего проще будет использовать.
S>Обычно фронт говорят — как сделаете так и будем юзать, решайте сами. По этому и хотелось бы по стандартам. И вообще выработать стиль на будущее.
Обычно, но не всегда. Команды бывают разные. Еще раз — РЕСТ — это НЕ стандарт.
S>Будешь меньше тратить времени, если сейчас заморочишься с этими вопросами.
Да какими вопросами? Вы из мухи слона делаете. Каким бы важным вам не казалось то, о чем вы сейчас спрашиваете, это — мелочь в масштабах продукта. И вот с опытом как раз приходит понимание, что важно и на что имеет смысл тратить больше времени, а что неважно.
Сразу хочется вспомнить "вам шашечки или ехать?". Программистам свойственен перфекционизм, а многие любят программировать ради программирования. И если их не ограничивать, они могут месяцами "вырабатывать стиль", переписывать с более лучшим дизайном, до посинения вылизывать АПИ. Продукт при этом будет стоять на месте.
Здравствуйте, Shmj, Вы писали:
vsb>>В общем не согласен. Всегда есть правила валидации и они могут изменяться во времени.
S>Как вы считаете, почему PUT гарантированно идемпотентен а PATCH — нет?
Потому, что те, кто придумывали эти ассоциации между методами и идемпотентностью, так решили. На мой взгляд это решение было неправильным и я в своём коде это ужесточаю до требований 100% идемпотентности в PATCH.
vsb>>Это можно о любой операции сказать. Идемпотентность не означает, что эта операция всегда срабатывает. PUT тоже не будет работать, если это нарушает какие-то условия уникальности, к примеру.
S>Т.е. имели ли вы в виду, что первый запрос PUT — сработал, т.к. записи с таким ID не было. А второй PUT — уже не сработал, т.к. запись с таким ID уже есть в системе? Или о какой уникальности речь?
Я имею в виду, что первый PUT может не сработать, если это нарушает какие-то ограничения по бизнес-логике, к примеру. Если первый PUT сработал, то второй обычно должен сработать. Но при этом могут быть ситуации, когда первый сработал, а второй не сработает. К примеру такая последовательность:
PUT /users/1/message/2
DELETE /users/1
PUT /users/1/message/2
В таком примере после удаления пользователя второй PUT с теми же параметрами что и первый уже не сработает, в этом нет смысла.
Может и второй PUT не сработать, если параметры отличаются. Хотя стандарты и говорят, что PUT заменяет данные, но это же не значит, что я обязан менять бизнес-логику под эти стандарты. К примеру у меня поле не должно меняться после создания. В этом случае второй PUT вернёт ошибку, если поля в базе отличаются от тех, что он пытается изменить. На самом деле я обычно так и делаю — PUT два раза подряд сработает только если параметры одинаковые. Не думаю, что это нарушает REST. А для редактирования уже PATCH.
Здравствуйте, vsb, Вы писали:
vsb>На самом деле я обычно так и делаю — PUT два раза подряд сработает только если параметры одинаковые.
Хорошо, очень интересно.
Такой вариант. PUT 2 раза подряд, параметры 100% одинаковые. Между двумя запросами никаких DELETE и пр. не было, т.е. все чисто. Но в PUT указано состояние Created. Однако когда пришел второй PUT — система в фоновом режиме начала обработку операции и изменила состояние на Processing, что значит заказ клиента начал исполняться и вернуть все в начальное состояние Created, как того требует второй PUT — никоим образом не представляется возможным. Что делать? Просто проигнорите и вернете 200, как бы подразумевая, что клиент использует PUT в качестве создания записи, а раз запись уже создана и хотя состояние изменилось — то все ОК. Или же вернете какую-то ошибку?