Речь будет о валидации, но в более широком смысле, чем проверка на not null и прочее подобное.
Пусть у нас есть REST API 2-го уровня (который на HTTP-глаголах, а не на HATEOAS и прочем). POST, PUT, вот это вот все.
Любые изменения ресурса выполняются в основном путем посылки PUT. Но так можно, например, поменять то поле в сущности, которое не должно меняться. Или еще хитрее: ряд полей могут меняться всеми, а другие только администратором. Но в PUT ведь подается не спецификация изменений, в отличие от PATCH, а именно новое представление. И как тогда бэкэнд должен выяснить, какие из изменений приемлемы, а какие нет? Сравнивать текущие значения в БД (при этом сравнивая версию, чтобы исключить интерференцию от других изменений) с тем, что пришло из UI, чтобы выяснить, что же менялось, а потом принимать решение о допустимости? Или, может быть, вы в своих приложениях доверяете своему клиенту полностью и применяете все те изменения, что вам прислали?
Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Здравствуйте, Slicer [Mirkwood], Вы писали:
SM>И как тогда бэкэнд должен выяснить, какие из изменений приемлемы, а какие нет?
PUT /resource/{id} — поля, которые может менять владелец ресурса
PUT /resource/{id}/admin — поля, которые может менять только админ
POST /user/{id}/password — смена пароля
Спасибо за ответ.
scf>PUT /resource/{id} — поля, которые может менять владелец ресурса scf>PUT /resource/{id}/admin — поля, которые может менять только админ
Это означает, что поля из {id}/admin не входят в {id}? То есть GET тоже нужно вызывать дважды — один раз для {id}, второй для {id}/admin? Ну ок, конкретно одну частную задачу вы решили — если над ресурсом разрешены два разных вида операций (привилегированные и обычные), вы разбиваете ресурс на два таких, для которых этой многовариантности нет. При условии, что это возможно.
scf>POST /user/{id}/password — смена пароля
Это мне кажется неудачной идеей сразу по 3 причинам. Во-первых, это почти то же самое, что PUT /user/{id} с указанием только password (partial put) — фактически мы заводим новое представление ресурса лишь только для того, чтобы хакнуть обычное представление и разрешить себе то, что мы бы хотели сделать при помощи partial put, но стеснялись, потому что partial put это нехорошо. По идее для этого есть более кошерный PATCH в основной ресурс, а не через введение нового ресурса. Во-вторых, POST??? Это выглядит так, словно мы создаем некий ресурс, а не изменяем. В-третьих, опять-таки в частном случае вы из положения вышли, хорошо. Но такой подход (опять-таки выделить кусок всего ресурса, который будет меняться данным POSTом) будет с ростом количества операций выглядеть все страннее. Смотрите: 1) сначала мы точку для изменения количества данного товара сделаем /{id}/count, 2) потом окажется что нам нужно разные проверки осуществлять при покупке и продаже, и нам придется либо иметь отдельно /{id}/countForSell и /{id}/countForBuy, либо оставить только /{id}/count и тогда опять будет вопрос — как понять, идет ли речь об изменении количества в результате покупки товара или его продажи.
Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Здравствуйте, Slicer [Mirkwood], Вы писали:
SM>Это означает, что поля из {id}/admin не входят в {id}?
Входят. GET и PUT так или иначе отличаются — к примеру, PUT должен игнорировать id, а GET не возвращать пароль
scf>>POST /user/{id}/password — смена пароля SM>Это мне кажется неудачной идеей сразу по 3 причинам.
Натягивание REST на задачи реального мира, по моему опыту, требует, так сказать, дополнительных допущений:
— GET/PUT/POST на одном и том же ресурсе — несимметричны.
-- GET может не возвращать некоторые данные (пароль)
-- PUT может не передавать некоторые данные, которые возвращаются из GET (id, modifiedDate, опциональные поля)
-- POST может передавать как меньше, так и больше данных, чем GET
— не все операции упаковываются в CRUD. для дополнительных обычно используется схема POST /resource/{id}/operation_name
— обязательные параметры ресурса и операций указываются в Path, опциональные — в query string.
— PATCH используется редко, т.к. качественная его реализация сложна и делать ее приходится руками
— поэтому partial PUT и популярен — это эффективнее, чем GET+PUT. Но для этого нужен json фреймворк, который разделяет null и отсутствие поля в документе.
— Контроль прав доступа на уровне отдельных полей модели реализовать можно, но сложно, поэтому на каждый ресурс часто накручивают отдельные привилегированные операции
SM>Смотрите: 1) сначала мы точку для изменения количества данного товара сделаем /{id}/count, 2) потом окажется что нам нужно разные проверки осуществлять при покупке и продаже, и нам придется либо иметь отдельно /{id}/countForSell и /{id}/countForBuy, либо оставить только /{id}/count и тогда опять будет вопрос — как понять, идет ли речь об изменении количества в результате покупки товара или его продажи.
GET /{id}?filter=count
PUT /{id} //partial update for count
А если окажется, что обновление count — сложная операция, то:
POST /{id}/count {amount: 1, operation: "sell"}
Здравствуйте, Slicer [Mirkwood], Вы писали:
SM>Пусть у нас есть REST API 2-го уровня (который на HTTP-глаголах, а не на HATEOAS и прочем). POST, PUT, вот это вот все. SM>Любые изменения ресурса выполняются в основном путем посылки PUT.
Есть мнение, что это религиозный спор, и он не обойдётся без испанской инквизиции.
Здравствуйте, Слава, Вы писали:
С>Есть мнение, что это религиозный спор, и он не обойдётся без испанской инквизиции.
Ну если в итоге все сведется к тому, насколько для реста важно именно это, и если реально весомых аргументов за или против не будет — обойдемся без инквизиции, просто решим, что царит анархия и каждый делает во что горазд =) Просто это легко говорить что рест это не догма и каждый готовит его по-своему, а на деле потом кто-то начнет тебя при поиске работы или в обсуждениях пытаться давить теми аргументами, что вот дескать еще в 2008 году тот же Филдинг выступал в поддержку применения именно гипермедиа в REST API. И Фаулер этому подпевал на разные лады. И все вот это вот.
Slicer
Специалист — это варвар, невежество которого не всесторонне :)
С>>Есть мнение, что это религиозный спор, и он не обойдётся без испанской инквизиции. SM>Ну если в итоге все сведется к тому, насколько для реста важно именно это, и если реально весомых аргументов за или против не будет — обойдемся без инквизиции, просто решим, что царит анархия и каждый делает во что горазд =) Просто это легко говорить что рест это не догма и каждый готовит его по-своему, а на деле потом кто-то начнет тебя при поиске работы или в обсуждениях пытаться давить теми аргументами, что вот дескать еще в 2008 году тот же Филдинг выступал в поддержку применения именно гипермедиа в REST API. И Фаулер этому подпевал на разные лады. И все вот это вот.
К примеру, вот буквально на днях ваш покорный слуга на stackoverflow задал вопрос по этой же тематике, и тут же нашелся перец, который написал что вообще-то все что ниже уровня 3 (гипермедиа) — ни разу не рест а фигня вообще. И если б он такой один был, я б мог дальше спокойно жить, но когда ты понимаешь что с подобными речами выступают на конференциях уважаемые люди и это как бы чуть ли не тренд (или тренд), понимаешь, что отболтаться по принципу "каждый делает по-своему" не выйдет =)
Специалист — это варвар, невежество которого не всесторонне :)
Здравствуйте, Slicer [Mirkwood], Вы писали:
С>>Есть мнение, что это религиозный спор, и он не обойдётся без испанской инквизиции. SM>кто-то начнет тебя при поиске работы или в обсуждениях пытаться давить теми аргументами, что вот дескать еще в 2008 году тот же Филдинг выступал в поддержку применения именно гипермедиа в REST API. И Фаулер этому подпевал на разные лады. И все вот это вот.
Знаете, это очень похоже на толкование каких-то сутр какими-то муллами. Вот-де мулла Фидлинг выпустил фетву, согласно которой правоверный RESTянин должен делать именно так, а не иначе. Во времена первой чеченской на Кавказе были в ходу видеокассеты, где некие бородатые авторитеты-мудрецы излагали, как надо поступать в таких-то и таких-то ситуациях.
По мне, сам подобный формат обсуждения этой темы какой-то гнилой. Начётничество.
Да и сама тема гнилая — как 100500 PM'ов со всего мира силами энтерпрайз, прости господи, девелоперов*, пытались натянуть на HTTP то, что с 80х годов было в SQL. И до сих пор не натянули, разве что в виде OData и GraphQL.
*то есть, не тех людей, которые способные делать и делают средства разработки.
С>Знаете, это очень похоже на толкование каких-то сутр какими-то муллами. С>По мне, сам подобный формат обсуждения этой темы какой-то гнилой. Начётничество.
Это вы критикуете меня за то, что я слишком буквально понимаю какие-то концепции REST? Так вы не критикуйте, а скажите, что именно из нашего с вами обсуждения (пока довольно короткого, кстати) вы считаете именно излишним догматизмом или буквоедством. Будет более продуктивно.
И потом, что такое понятие без толкования? Был бы термин задан более формально — можно было бы без толкований обойтись. Но нет, нам так не повезло: оказались возможны разночтения. Значит, каждый человек, когда пишет api, руководствуется тем или иным толкованием. С учетом этого я не считаю зазорным считаться с существующими толкованиями. Я мог бы на них наплевать и делать все по-своему, но я же собираюсь с другими людьми работать и на уже начатых проектах, так что я бы хотел говорить с людьми на одном языке. Поэтому если уж употребляется термин REST и разные его оттенки, то я бы хотел понимать, что он покрывает и что не покрывает — и понимать так же, как это понимает прогрессивная общественность (знать бы еще, кого можно ей считать).
Поэтому я и хочу: узнать, кто как решает описанную мной изначально проблему в контексте REST, и разобраться, действительно ли это решение REST или нет, и почему. Тем самым я лучше разберусь в том, как REST может выглядеть и как не должен.
Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Здравствуйте, scf, Вы писали:
scf>Натягивание REST на задачи реального мира, по моему опыту, требует, так сказать, дополнительных допущений:
Ага, ну вот это я понимаю. То есть вы в работе руководствуетесь тем, что использовать "пуристский" рест в реальных задачах не получается и надо его несколько видоизменять. Конечно, если это общая точка зрения, то я не знаю, зачем ломаться и говорить "мой рест хардкорнее вашего, а ваш это просто голимый RPC на самом деле", если фактически все знают, что каждый от этого реста оставляет только то, что ему удобно.
SM>>Смотрите: 1) сначала мы точку для изменения количества данного товара сделаем /{id}/count, 2) потом окажется что нам нужно разные проверки осуществлять при покупке и продаже, и нам придется либо иметь отдельно /{id}/countForSell и /{id}/countForBuy, либо оставить только /{id}/count и тогда опять будет вопрос — как понять, идет ли речь об изменении количества в результате покупки товара или его продажи.
scf>А если окажется, что обновление count — сложная операция, то: scf>POST /{id}/count {amount: 1, operation: "sell"}
Ну а тут прям вот напрашивается PATCH — он же так и работает =) Но в общем я вас уже понял.
Спасибо. Было б здорово альтернативные какие-то точки зрения узнать.
Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Здравствуйте, Slicer [Mirkwood], Вы писали:
SM>Речь будет о валидации, но в более широком смысле, чем проверка на not null и прочее подобное.
Я не знаток REST, тем не менее предлагаю не решение, а обострение ситуации. SM>Пусть у нас есть REST API 2-го уровня (который на HTTP-глаголах, а не на HATEOAS и прочем). POST, PUT, вот это вот все.
POST для изменений — зло. Каждый раз, когда не удается выкинуть его на согласовании, это приводит к проблемам в момент обрывов связи. Клиенту приходится узнавать, получил ли сервер POST, во время вызова которого произошел сбой. Но не об этом вопрос. SM>Любые изменения ресурса выполняются в основном путем посылки PUT. Но так можно, например, поменять то поле в сущности, которое не должно меняться. Или еще хитрее: ряд полей могут меняться всеми, а другие только администратором. Но в PUT ведь подается не спецификация изменений, в отличие от PATCH, а именно новое представление. И как тогда бэкэнд должен выяснить, какие из изменений приемлемы, а какие нет? Сравнивать текущие значения в БД (при этом сравнивая версию, чтобы исключить интерференцию от других изменений) с тем, что пришло из UI, чтобы выяснить, что же менялось, а потом принимать решение о допустимости? Или, может быть, вы в своих приложениях доверяете своему клиенту полностью и применяете все те изменения, что вам прислали?
Последний вариант не годится вообще. Только если для каких-то локальных частных сценариев. Но если мы рассуждаем о REST, то надо смотреть в более общем случае, чем конкретное приложние. REST торчит наружу. Значит, клиентом может быть вообще все что угодно, а не только лишь свой UI. Исхожу из предпосылки что верить нельзя никому.
Поэтому, если выбирать из двух вариантов (проверять/доверять), то надо проверять.
Но давайте сделаем еще хитрее. Часть полей доступно читать только администратору. Обычному пользователю неоткуда их взять, что бы сформировать корректное полное представление для PUT.
SM>>Ну а тут прям вот напрашивается PATCH — он же так и работает =) Но в общем я вас уже понял. scf>Всё несколько сложнее Вот, например: https://tools.ietf.org/html/rfc6902
Ну так вот же выдержка оттуда: { "op": "test", "path": "/a/b/c", "value": "foo" },
— то есть что и с чем сделать, по существу. Как у вас и предложено — указать название операции, помимо изменяемых значений.
Но с этим более-менее ясно все. Жду других предложений общественности
Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Здравствуйте, samius, Вы писали:
S>Здравствуйте, Slicer [Mirkwood], Вы писали:
SM>>Речь будет о валидации, но в более широком смысле, чем проверка на not null и прочее подобное. S>Я не знаток REST, тем не менее предлагаю не решение, а обострение ситуации.
Ну вот жаль, я все еще надеюсь, что и знатоки откликнутся
S>Поэтому, если выбирать из двух вариантов (проверять/доверять), то надо проверять. S>Но давайте сделаем еще хитрее. Часть полей доступно читать только администратору. Обычному пользователю неоткуда их взять, что бы сформировать корректное полное представление для PUT.
Тогда обычный пользователь не сможет и другие поля менять, раз без админских полей он не сможет сформировать полное представление для PUT. И кстати с точки зрения проверки мы должны быть готовы и к тому, что нам пришлют какие-то значения даже если взять их неоткуда — или пришлют неполное представление. Ну и вообще, задача проверки в общем случае все равно включает в себя понимание того, что именно пытаются проделать, наверное. А не только разграничение прав доступа к полям.
Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Здравствуйте, Slicer [Mirkwood], Вы писали:
SM>Здравствуйте, samius, Вы писали:
S>>Но давайте сделаем еще хитрее. Часть полей доступно читать только администратору. Обычному пользователю неоткуда их взять, что бы сформировать корректное полное представление для PUT. SM>Тогда обычный пользователь не сможет и другие поля менять, раз без админских полей он не сможет сформировать полное представление для PUT. И кстати с точки зрения проверки мы должны быть готовы и к тому, что нам пришлют какие-то значения даже если взять их неоткуда — или пришлют неполное представление. Ну и вообще, задача проверки в общем случае все равно включает в себя понимание того, что именно пытаются проделать, наверное. А не только разграничение прав доступа к полям.
Вот так мы отходим от как такового редактирования сущности PUT-ом к бизнес-операциям со своими правилами.
Здравствуйте, samius, Вы писали:
S>Вот так мы отходим от как такового редактирования сущности PUT-ом к бизнес-операциям со своими правилами.
И я всеми конечностями "за", вот только reasonable people не изобретают свой собственный способ построения апи (если они не рокстарс конечно), а следуют стандартам индустрии. И насколько я понимаю REST по крайней мере без гипермедиа — он как раз не про доменные операции, а про HTTP-глаголы и crud Даже такая простая вещь, как эндпойнт для формы, в которой редактируются сразу две сущности, а изменить их надо одновременно, или например для перевода денег с одного акаунта на другой, сразу вызывает вопросы: блин, а как это правильно сделать и почему это будет правильно?
Вот и пытаюсь разобраться.
Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Здравствуйте, Slicer [Mirkwood], Вы писали:
SM>Здравствуйте, samius, Вы писали:
S>>Вот так мы отходим от как такового редактирования сущности PUT-ом к бизнес-операциям со своими правилами. SM>И я всеми конечностями "за", вот только reasonable people не изобретают свой собственный способ построения апи (если они не рокстарс конечно), а следуют стандартам индустрии. И насколько я понимаю REST по крайней мере без гипермедиа — он как раз не про доменные операции, а про HTTP-глаголы и crud Даже такая простая вещь, как эндпойнт для формы, в которой редактируются сразу две сущности, а изменить их надо одновременно, или например для перевода денег с одного акаунта на другой, сразу вызывает вопросы: блин, а как это правильно сделать и почему это будет правильно? SM>Вот и пытаюсь разобраться.
Ну тут все просто. Достаточно разобраться в том, что REST должен стоять на службе у бизнес-операций, а не бизнес-операции на службе REST. Перевод денег — хороший пример, который указывает на то, что мы не должны давать клиенту вписывать PUT-ом величину денег на его счете и на счете того, кому он желает их перевести. Что клиент может вписать PUT-ом? Транзакцию или объект типа "перевод", который представляет собой и действие и бизнес-сущность не хуже, чем счет. И REST этому не мешает.
Здравствуйте, samius, Вы писали: S>Что клиент может вписать PUT-ом? Транзакцию или объект типа "перевод", который представляет собой и действие и бизнес-сущность не хуже, чем счет. И REST этому не мешает.
Это уже немного оффтоп, но все же: это обман. Мы можем изобразить, что существует ресурс "перевод", но на деле если в модели его нет, то запостить его можно, а вот сделать PUT или DELETE или GET — нельзя. (Ну, если в модели есть — дело другое). То есть получается мы придумываем некую абстракцию, которой не было в нашей доменной модели, лишь бы втиснуться в REST, да и то влезаем с некоторой натяжкой.
Slicer
Специалист — это варвар, невежество которого не всесторонне :)
Здравствуйте, Slicer [Mirkwood], Вы писали:
SM>Здравствуйте, samius, Вы писали: S>>Что клиент может вписать PUT-ом? Транзакцию или объект типа "перевод", который представляет собой и действие и бизнес-сущность не хуже, чем счет. И REST этому не мешает. SM>Это уже немного оффтоп, но все же: это обман. Мы можем изобразить, что существует ресурс "перевод", но на деле если в модели его нет, то запостить его можно, а вот сделать PUT или DELETE или GET — нельзя. (Ну, если в модели есть — дело другое). То есть получается мы придумываем некую абстракцию, которой не было в нашей доменной модели, лишь бы втиснуться в REST, да и то влезаем с некоторой натяжкой.
Нене, у вас опять телега с REST-ом впереди лошади. Я пытаюсь создать API, которое бы позволяло выполнить безопасный перевод. REST или нет — мне кашлять. Но если я в банке вижу API, предлагающее непосредственное манипулирование значением счета, я держусь от этого банка как можно дальше, либо пытаюсь заработать на багбаунти, даже если у банка REST по фэншую.
Какая там доменная модель и кто ее выдумывал — мне все равно. Могу ли я доверять деньги этому сервису — вот это важно.
Но все же я остаюсь при мнении что REST не противоречит созданию лишних сущностей во имя безопасности.
Здравствуйте, Slicer [Mirkwood], Вы писали:
SM>Речь будет о валидации, но в более широком смысле, чем проверка на not null и прочее подобное. SM>Пусть у нас есть REST API 2-го уровня (который на HTTP-глаголах, а не на HATEOAS и прочем). POST, PUT, вот это вот все. SM>Любые изменения ресурса выполняются в основном путем посылки PUT. Но так можно, например, поменять то поле в сущности, которое не должно меняться. Или еще хитрее: ряд полей могут меняться всеми, а другие только администратором. Но в PUT ведь подается не спецификация изменений, в отличие от PATCH, а именно новое представление. И как тогда бэкэнд должен выяснить, какие из изменений приемлемы, а какие нет? Сравнивать текущие значения в БД (при этом сравнивая версию, чтобы исключить интерференцию от других изменений) с тем, что пришло из UI, чтобы выяснить, что же менялось, а потом принимать решение о допустимости? Или, может быть, вы в своих приложениях доверяете своему клиенту полностью и применяете все те изменения, что вам прислали?
1. Можно просто запретить PUT для таких составных документов. Требуйте PATCH и всё. Нет никакой обязанности поддерживать PUT, если он не ложится в логику вашего ресурса.
Потому что иначе возникают странности — вот я отправляю какой-то PUT, а он не проходит (поле X не совпадает с тем, что сейчас в базе). А через минуту отправляю такой же — и он проходит (админ поменял поле X ровно на то же, что я передавал). Или наоборот — я зачитал ресурс, поменял в нём один атрибут (легальный), отправляю — фейл. Оказывается, там в это время админ поменял что-то запретное для меня, а я как бы пытаюсь "вернуть это на место".
2. Можно распилить ресурс на две (или более) частей. В чистом REST единица информации — это документ. Если у меня есть право его менять — я его меняю. Если я отправил неверный контент — это Bad Request, моя вина. Если нет права менять — то 403 forbidden. Внятно выразить в рамках REST концепцию "в документе можно менять не всё" способа нет. Поэтому можно дать отдельным изменяемым фрагментам свои адреса — вон, у MS в PartnerCenter API у тенанта billingProfile возвращается и в самом ресурсе ...customers/<tenantId>, и как отдельная штука по адресу ...customers/<tenantId>/billingProfile
Уйдемте отсюда, Румата! У вас слишком богатые погреба.