Во многих вакансиях появилось требование звучащее как — уметь проектировать хороший rest интерфейс
вот и задумался а что такое этот хороший интерфейс, у самого есть конечно мысли но какие то они не очень убедительные
вот хотел бы послушать коллективное бессознательное
1. Действия соответствуют семантике. GET возвращает, PUT кладёт, и тд. Кроме того соблюдается идемпотентность, т.е. можно вызывать GET или PUT сколько угодно раз с одними и теми же параметрами.
2. URL структурированный типа /users/123 а не /user?id=123.
Здравствуйте, a.v.v, Вы писали:
AVV>вот и задумался а что такое этот хороший интерфейс, у самого есть конечно мысли но какие то они не очень убедительные
Мне кажется, что самое важное:
— простота
— консистентность (согласованность)
vsb>1. Действия соответствуют семантике. GET возвращает, PUT кладёт, и тд. Кроме того соблюдается идемпотентность, т.е. можно вызывать GET или PUT сколько угодно раз с одними и теми же параметрами.
Подскажите, пожалуйста, как правильный REST будет выглядеть для случая например:
Есть модель объекта в обычном 3D пространстве и ее нужно сдвинуть на сколько-то в какую-то сторону.
Если "сдвинуть" слишком просто, то чуть служнее: сплющить.
А "идемпотентность" тут как-то можно соблюсти?
Если я неправильно смотрю на весь дизайн, то как правильно, при условии, что объект есть, и только штука с интерфейсом знает, как его правильно двигать и плющить.
Если расскажете, большое спасибо.
Если не расскажите, просто спасибо, что дочитали
Здравствуйте, ylem, Вы писали:
vsb>>1. Действия соответствуют семантике. GET возвращает, PUT кладёт, и тд. Кроме того соблюдается идемпотентность, т.е. можно вызывать GET или PUT сколько угодно раз с одними и теми же параметрами.
Y>Подскажите, пожалуйста, как правильный REST будет выглядеть для случая например: Y>Есть модель объекта в обычном 3D пространстве и ее нужно сдвинуть на сколько-то в какую-то сторону. Y>Если "сдвинуть" слишком просто, то чуть служнее: сплющить. Y>А "идемпотентность" тут как-то можно соблюсти?
Y>Если я неправильно смотрю на весь дизайн, то как правильно, при условии, что объект есть, и только штука с интерфейсом знает, как его правильно двигать и плющить.
Y>Если расскажете, большое спасибо. Y>Если не расскажите, просто спасибо, что дочитали
Лучше всего, если есть возможность получить положение и масштабы объекта, а потом их просеттить, это будет идемпотентно:
PUT /api/v1/model/{id}/position
PUT /api/v1/model/{id}/scale
Либо вообще
PATCH /api/v1/model/{id}
Если идемпотентность соблюсти нельзя, то ничего не поделаешь, придется делать без неё:
POST /api/v1/model/{id}/shift
POST /api/v1/model/{id}/scale
И обеспечивать отсутствие повторных запросов при реконнектах дополнительными заголовками а-ля X-Request-Id, чтобы сервер мог проверить, что этот запрос уже обработан.
Здравствуйте, ylem, Вы писали:
vsb>>1. Действия соответствуют семантике. GET возвращает, PUT кладёт, и тд. Кроме того соблюдается идемпотентность, т.е. можно вызывать GET или PUT сколько угодно раз с одними и теми же параметрами.
Y>Подскажите, пожалуйста, как правильный REST будет выглядеть для случая например: Y>Есть модель объекта в обычном 3D пространстве и ее нужно сдвинуть на сколько-то в какую-то сторону.
Вычисляешь новые координаты и вызываешь PATCH /object/25 {"x": 123, "y": 456}.
Y>Если "сдвинуть" слишком просто, то чуть служнее: сплющить.
То же самое, вычисляешь новый размер и вызываешь PATCH.
Y>А "идемпотентность" тут как-то можно соблюсти?
Если подход с вычислением нового состояния на клиенте невозможно применить, очевидно, что никак. Вызываешь POST и забиваешь на идемпотентность. Правда что делать, когда тебе не пришёл положительный ответ на POST, уже никто не скажет. Повторять его небезопасно. Можно сказать юзеру, что произошла ошибка и сложить ручки. В случае PUT можно, например, попробовать повторить запрос. Если проблема была неперманентная (например перезагружали сервер или были проблемы с сетью), пользователь даже не заметит ничего.
Y>Если я неправильно смотрю на весь дизайн, то как правильно, при условии, что объект есть, и только штука с интерфейсом знает, как его правильно двигать и плющить.
Как вариант — создавать некий ID для изменения-транзакции и делать PUT этой транзакции. На сервере, соответственно, проверять, что эта транзакция уже не выполнялась (т.е. вести лог).
Но такой подход в любом случае в REST не очень укладывается. REST это штука специфичная и натягивать произвольное API на него невозможно. В этом его минус и причина непрактичности, на практике его все нарушают и творят что хотят, именуя по сути обычный RPC интерфейс REST-ом.
Здравствуйте, ylem, Вы писали:
Y>Если "сдвинуть" слишком просто, то чуть служнее: сплющить. Y>А "идемпотентность" тут как-то можно соблюсти?
Можно. Например: "PUT /object/123123/move?deltaXMeters=33.5", — двигаем объект с ID=123123 на 33.5 метров. В результате получаем новый объект с ID=123124. К нему можно применять другие преобразования.
Здравствуйте, Cyberax, Вы писали:
C>Можно. Например: "PUT /object/123123/move?deltaXMeters=33.5", — двигаем объект с ID=123123 на 33.5 метров. В результате получаем новый объект с ID=123124. К нему можно применять другие преобразования.
В этом случае следующий повторный PUT добавит еще 1 объект с 123125 итд. Насколько я понимаю это будет противоречить логике, возлагаемой на PUT. Да и копировать объект на каждый чих занятие так себе.
Здравствуйте, a.v.v, Вы писали: AVV>вот хотел бы послушать коллективное бессознательное
Все предыдущие дискуссии по теме REST в этом форуме уже прочитаны?
Книга http://freecomputerbooks.com/Restful-Web-Services.html уже прочитана?
Тогда можно поговорить содержательно.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, ylem, Вы писали:
Y>Если я неправильно смотрю на весь дизайн, то как правильно, при условии, что объект есть, и только штука с интерфейсом знает, как его правильно двигать и плющить.
Вообще-то я остерегаюсь дизайнить слона по требованиям к мочке уха.
1. Что такое "модель объекта"? Сколько таких моделей используются согласованно? Т.е. мы говорим о целой сцене, в рамках которой (почему-то) хочется отдельно запрашивать/модифицировать отдельные объекты, или о "монолитном" объекте, который сам по себе, и у него есть привязка к координатам.
2. Какие ещё операции должны быть доступны, и насколько часто они выполняются?
В простом случае мы говорим о некотором "документе", который описывает весь объект, как он есть. Ну там — вершины, рёбра, грани. Массив точек, массив треугольников.
Операция "изменить объект" — это просто PUT с новым объектом. REST — он про состояние, а не про инкапсуляцию, где "только штука с интерфейсом знает, как его двигать".
Чтобы сделать "штуку с интерфейсом, которая знает как правильно двигать", нам достаточно иметь совершенно отдельный safe метод transform.
Мы отдаём ему на вход детальное описание объекта и описание трансформации, а на выходе он выдаёт нам новое описание объекта.
Примерно так работал google maps в те доисторические времена, когда рендер SVG был невероятной экзотикой. И у них была ферма серверов, которые умеют превращать SVG в PNG.
Так и здесь: выполняем GET /transforms/rotateEulerAngles/35.3,123,2,0.0/{тутОписаниеОбъектаВкомпактномПредставлении}/, получаем новое описание объекта.
Преимущество тут в полном отсутствии состояния — мы можем раскидать эти запросы по 1000000 геораспределённых машинок. То есть эта часть сервиса — safe & stateless.
Потом, совершенно отдельно от этого сервиса, делаем PUT /objects/{objectsId}/{тутОписаниеОбъектаВкомпактномПредставлении} — он уже вполне себе идемпотентен.
Но это — кирпичики. Как из них сделать то, что нужно — вопрос открытый. Потому, что подобный дизайн всё ещё провоцирует интересные ситуации.
Скажем, клиент А прочитал состояние объекта, клиент Б прочитал состояние объекта.
Теперь клиент А пытается записать в объект "сплюснут", а Б — "сдвинут". В зависимости от порядка обращения и надёжности каналов, выиграть может любой. Это, очевидно, очень плохо.
Не имея реального ТЗ, мы не можем понять, что именно ожидается в результате такого сценария: применится только одна трансформация (заранее неизвестно, какая), вторая получит 409 conflict
применяется результат той трансформации, которая вычитала объект первой; вторая получит 409 conflict
применятся обе трансформации, в произвольном порядке (именно так работают, например, пополнения банковского счёта)
применятся обе трансформации, в том порядке, как было выполнено чтение объекта.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Doc, Вы писали:
C>>Можно. Например: "PUT /object/123123/move?deltaXMeters=33.5", — двигаем объект с ID=123123 на 33.5 метров. В результате получаем новый объект с ID=123124. К нему можно применять другие преобразования. Doc>В этом случае следующий повторный PUT добавит еще 1 объект с 123125 итд. Насколько я понимаю это будет противоречить логике, возлагаемой на PUT.
Это вполне соответствует PUT/PATCH.
Doc>Да и копировать объект на каждый чих занятие так себе.
Целиком зависит от задачи.
Если копирование неприемлимо — можно использовать ключи идемпотентности.
Здравствуйте, Cyberax, Вы писали:
Doc>>В этом случае следующий повторный PUT добавит еще 1 объект с 123125 итд. Насколько я понимаю это будет противоречить логике, возлагаемой на PUT. C>Это вполне соответствует PUT/PATCH.
Как? Ведь если повторить PUT с тем же параметрами, то он не должен изменить состояние системы. А тут получаем новый объект. То, что вы описали больше подходит под POST (веть по сути создает новый объект, хоть и на базе старого).
Здравствуйте, Doc, Вы писали:
Doc>>>В этом случае следующий повторный PUT добавит еще 1 объект с 123125 итд. Насколько я понимаю это будет противоречить логике, возлагаемой на PUT. C>>Это вполне соответствует PUT/PATCH. Doc>Как? Ведь если повторить PUT с тем же параметрами, то он не должен изменить состояние системы.
Ну так оно и не изменится. Старые объекты все останутся как есть, новый объект будет себе просто так висеть и никого не трогать.
Doc>А тут получаем новый объект. То, что вы описали больше подходит под POST (веть по сути создает новый объект, хоть и на базе старого).
Я бы не заморачивался о тонкостях между PUT/POST/PATCH.
Здравствуйте, Doc, Вы писали:
C>>Я бы не заморачивался о тонкостях между PUT/POST/PATCH. Doc>И при этом спорите что это не POST, а PUT. Ну да после такого заявления вопросов больше нет
Не, ну я не адепт REST-о-строя. У меня в API большинство методов тупо POST, включая создание и редактирование. В новых сервисах вообще на него забил и использую Twirp ( https://github.com/twitchtv/twirp/ ).
При дизайне API гораздо важнее продумывать стратегии обеспечения идемпотентности, авторизации, retries и т.п.
Здравствуйте, Doc, Вы писали:
Doc>Как? Ведь если повторить PUT с тем же параметрами, то он не должен изменить состояние системы. А тут получаем новый объект. То, что вы описали больше подходит под POST (веть по сути создает новый объект, хоть и на базе старого).
Вот тут начинает играть роль тот вопрос, который я задавал в параллельной подветке: живёт ли объект сам по себе, или является частью сцены.
Если сам по себе — то ничего страшного, у нас получилось 2 (3, и т.д.) "копии" объекта. Т.е. имеем что-то типа набора версий, которые существуют более-менее независимо. "Сломать" объект нельзя, всегда можно вернуться к "старому" id и применить к нему другую трансформацию.
А вот если у нас есть сцена, то уже вопрос — как себя будет вести она при таких трансформациях. Очевидно, что она не должна забиваться репликами объектов. С другой стороны, даже такая архитектура ничему не противоречит — сцена может определяться "тегом", который навешивается на какие-то версии объектов. Модификация будет переносить тег со "старого" объекта на "новый" — либо автоматически, либо вручную.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Cyberax, Вы писали:
C>При дизайне API гораздо важнее продумывать стратегии обеспечения идемпотентности, авторизации, retries и т.п.
Очень странно говорить о стратегиях обеспечения идемпотентности и retries, при этом игнорируя глаголы HTTP.
Понятно, что в общем случае "просто выбрать PUT" недостаточно; я вот прямо сейчас наблюдаю чудеса во вполне РЕСТ-овом API от Microsoft (https://docs.microsoft.com/en-us/partner-center/develop/partner-center-rest-api-reference), где парни ухитрились на ровном месте сделать невозможным предотвращение двоящихся или потерянных заказов.
Я так понимаю — как раз потому, что они не очень хорошо понимали, что такое REST, и задумывались о "стратегиях, которые гораздо важнее". Идемпотентность в старом order api они обеспечивали через custom header "Request-ID", а в новом shopping cart команде просто об этом хидере не рассказали.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>А вот если у нас есть сцена, то уже вопрос — как себя будет вести она при таких трансформациях. Очевидно, что она не должна забиваться репликами объектов.
Сцена может иметь свою версию, представляющую срез версий ее объектов на момент времени.
Здравствуйте, Mihas, Вы писали:
S>>А вот если у нас есть сцена, то уже вопрос — как себя будет вести она при таких трансформациях. Очевидно, что она не должна забиваться репликами объектов. M>Сцена может иметь свою версию, представляющую срез версий ее объектов на момент времени.
Опять: всё зависит от неизвестных нам требований. "Момент времени" — штука условная. Может запросто оказаться, что интересная нам версия сцены состоит из объекта X в момент T0, и объекта Y на момент T1, при этом X-в-момент-T1 нам не нужен. В общем, это всё примерно как задача "а спроектируйте мне иерархию классов, описывающую квадрат, прямоугольник, круг, и эллипс".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.