Как создать REST URL без глаголов?

Я изо всех сил пытаюсь определить, как создать спокойные URL-адреса. Я полностью за спокойный подход использования URL с существительными, а не глаголами, не понимаю, как это сделать.

Мы создаем сервис по внедрению финансового калькулятора. Калькулятор принимает несколько параметров, которые мы будем загружать через файл CSV. Варианты использования будут включать:

  1. Загрузить новые параметры
  2. Получить последние параметры
  3. Получить параметры для определенной бизнес-даты
  4. Сделайте набор параметров активным
  5. Проверить набор параметров

Я полагаю, что спокойный подход будет иметь следующие типы URL:

/parameters
/parameters/12-23-2009

Вы можете достичь первых трех вариантов использования с:

  1. POST, где вы включаете файл параметров в запрос поста
  2. ПОЛУЧИТЬ первый URL
  3. ПОЛУЧИТЬ второй URL

Но как вы делаете 4-й и 5-й вариант использования без глагола? Разве вам не нужны такие URL, как:

/parameters/ID/activate
/parameters/ID/validate

??

8 ответов

Решение

Возможно что-то вроде:

PUT /parameters/activation HTTP/1.1
Content-Type: application/json; encoding=UTF-8
Content-Length: 18

{ "active": true }

Общие принципы хорошего дизайна URI:

  • Не используйте параметры запроса для изменения состояния
  • Не используйте смешанные пути, если вы можете помочь; строчная лучше
  • Не используйте специфичные для реализации расширения в ваших URI (.php, .py, .pl и т. Д.)
  • Не впадайте в RPC с вашими URI
  • Ограничьте URI-пространство как можно больше
  • Держите отрезки пути короткими
  • Предпочитаю либо /resource или же /resource/; создать 301 перенаправления из того, который вы не используете
  • Использовать параметры запроса для подбора ресурса; т.е. нумерация страниц, поисковые запросы
  • Переместите материал из URI, который должен быть в заголовке HTTP или теле

(Примечание: я не сказал "RESTful URI design"; URI по существу непрозрачны в REST.)

Общие принципы выбора метода HTTP:

  • Никогда не используйте GET для изменения состояния; это отличный способ, чтобы робот Google испортил ваш день
  • Не используйте PUT, если вы не обновляете весь ресурс
  • Не используйте PUT, если вы также не можете законно сделать GET на тот же URI
  • Не используйте POST для извлечения информации, которая является долговременной или может быть целесообразной для кэширования
  • Не выполняйте операцию, которая не идемпотентна с PUT
  • Используйте GET как можно больше
  • Используйте POST вместо PUT, если сомневаетесь
  • Используйте POST всякий раз, когда вам нужно сделать что-то похожее на RPC
  • Используйте PUT для классов ресурсов, которые больше или иерархические
  • Используйте DELETE вместо POST для удаления ресурсов
  • Используйте GET для таких вещей, как вычисления, если ваш ввод не велик, в этом случае используйте POST

Общие принципы проектирования веб-сервисов с HTTP:

  • Не помещайте метаданные в тело ответа, который должен быть в заголовке
  • Не помещайте метаданные в отдельный ресурс, если их включение не приведет к значительным накладным расходам.
  • Используйте соответствующий код статуса
    • 201 Created после создания ресурса; ресурс должен существовать на момент отправки ответа
    • 202 Accepted после успешного выполнения операции или асинхронного создания ресурса
    • 400 Bad Request когда кто-то делает операцию с данными, которые явно поддельные; для вашего приложения это может быть ошибкой валидации; обычно резервируют 500 для неисследованных исключений
    • 401 Unauthorized когда кто-то получает доступ к вашему API либо без предоставления необходимого Authorization заголовок или когда учетные данные в пределах Authorization недействительны; не используйте этот код ответа, если вы не ожидаете учетные данные через Authorization заголовок.
    • 403 Forbidden когда кто-то обращается к вашему API способом, который может быть вредоносным, или если он не авторизован
    • 405 Method Not Allowed когда кто-то использует POST, когда он должен был использовать PUT и т. д.
    • 413 Request Entity Too Large когда кто-то пытается отправить вам недопустимо большой файл
    • 418 I'm a teapot при попытке заваривать кофе с чайником
  • Всегда используйте заголовки кэширования, когда вы можете
    • ETag заголовки хороши, когда вы можете легко уменьшить ресурс до значения хеша
    • Last-Modified Следует указать вам, что сохранение отметки времени обновления ресурсов - это хорошая идея.
    • Cache-Control а также Expires должны быть даны разумные значения
  • Сделайте все возможное, чтобы соблюсти заголовки кэширования в запросе (If-None-Modified, If-Modified-Since)
  • Используйте перенаправления, когда они имеют смысл, но они должны быть редкими для веб-службы

Что касается вашего конкретного вопроса, POST следует использовать для № 4 и № 5. Эти операции подпадают под "RPC-подобное" руководство выше. Для #5, помните, что POST не обязательно должен использовать Content-Type: application/x-www-form-urlencoded, Это также может быть полезная нагрузка JSON или CSV.

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

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

Каждый раз, когда предлагается ресурс, называемый "параметром", он должен посылать красные флаги в уме каждого члена команды проекта. "параметр" может буквально применяться к любому ресурсу; это не достаточно конкретно.

Что именно представляет "параметр"? Вероятно, ряд разных вещей, каждая из которых должна иметь отдельный ресурс, посвященный этому.

Другой способ добиться этого - когда вы обсуждаете свое приложение с конечными пользователями (теми, кто, по-видимому, мало знает о программировании), какие слова они часто используют сами?

Это те слова, вокруг которых вы должны разрабатывать свое приложение.

Если у вас еще не было этого преобразования с потенциальными пользователями - остановите все прямо сейчас и не пишите еще одну строку кода, пока не сделаете! Только тогда ваша команда будет иметь представление о том, что должно быть построено.

Я ничего не знаю о финансовом программном обеспечении, но если бы мне пришлось угадывать, я бы сказал, что некоторые ресурсы могут быть названы, например, "Отчет", "Оплата", "Перевод" и "Валюта".

Есть много хороших книг по этой части процесса разработки программного обеспечения. Два, которые я могу порекомендовать, - это шаблон проектирования и анализа, управляемый доменом.

Дизайн ваших URL не имеет никакого отношения к тому, является ли ваше приложение RESTful или нет. поэтому фраза "RESTful URLS" - это нонсенс.

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

Тем не менее, давайте перейдем к вашему вопросу: последние два случая не являются RESTful и не вписываются в какую-либо схему отдыха. Это то, что вы могли бы назвать RPC. Если вы серьезно относитесь к REST, вам придется заново продумать, как ваше приложение работает с нуля. Либо так, либо откажитесь от REST и просто сделайте свое приложение как приложение RPC.

Хммм может и нет.

Идея заключается в том, что вы должны рассматривать все как ресурс, поэтому, если у набора параметров есть URL, с которого вы можете ссылаться на него, вы просто добавляете

получить [parametersurl] / validationresults

post [paramatersurl]

тело: {команда:"активировать"}

но опять же, эта активирующая вещь - RPC, а не REST.

Требования активации и проверки - это ситуации, когда вы пытаетесь изменить состояние ресурса. Не отличается то, что оформление заказа "выполнено", или какой-то другой запрос "отправлен". Существует множество способов смоделировать такие изменения состояния, но я считаю, что часто работает то, что нужно создавать ресурсы коллекций для ресурсов одного и того же состояния, а затем перемещать ресурс между коллекциями, чтобы влиять на состояние.

например, создать некоторые ресурсы, такие как,

/ActiveParameters
/ValidatedParameters

Если вы хотите сделать набор параметров активным, то добавьте этот набор в коллекцию ActiveParameters. Вы можете передать набор параметров в виде тела объекта или передать URL-адрес в качестве параметра запроса следующим образом:

POST /ActiveParameters?parameter=/Parameters/{Id}

То же самое можно сделать с /ValidatedParameters. Если параметры недействительны, то сервер может вернуть "Bad Request" в запрос, чтобы добавить параметры в коллекцию проверенных параметров.

Мне немного грустно видеть, что по прошествии более чем 10 лет нет ответа, действительно указывающего, как такая вещь, как запрошенная в OP, может быть спроектирована в архитектуре REST, поэтому я чувствую необходимость сделать это сейчас.

Перво-наперво, что такое REST?! Аббревиатура REST или ReST расшифровывается как "передача репрезентативного состояния" и определяет обмен состоянием ресурса в определенном формате представления. Формат представления соответствует согласованному типу носителя. На случай, еслиapplication/html формат представления может быть потоком текстового содержимого в формате HTML, которое отображается в браузере, вероятно, после применения некоторого форматирования таблицы стилей для размещения определенных элементов в определенных местах.

В принципе, REST является обобщением известной нам сети с возможностью просмотра, хотя нацелен на все виды приложений, а не только на браузеры. Следовательно, по замыслу те же концепции, которые применяются в Интернете, применимы и к архитектуре REST. Такой вопрос, как "как достичь чего-то с помощью RESTful", решается путем ответа на вопрос, как достичь чего-либо на веб-странице, а затем применять те же концепции на уровне приложения.

Веб-калькулятор обычно может начинаться с некоторой "страницы", которая позволяет вам ввести некоторые значения для расчета перед отправкой введенных данных на сервер. В HTML это обычно достигается через HTML.<form>элементы, которые обучают клиента доступным параметрам для установки, целевому местоположению для отправки запроса, а также формату представления, применяемому при отправке входных данных. Это может выглядеть примерно так:

<html>
  <head>
    ...
  </head>
  <body>
    <form action="/../someResource" method="post" enctype="application/x-www-form-urlencoded">
      <label for="firstNumber">First number:</label>
      <input type="number" id="firstNumber" name="firstNumber"/>

      <label for="secondNumber">Second number:</label>
      <input type="number" id="secondNumber" name="secondNumber"/>

      <input type="submit" value="Add numbers"/>
    </form>
  </body>
</html>

В приведенном выше примере, т.е. говорится, что есть два поля ввода, которые могут быть заполнены либо пользователем, либо некоторыми другими автоматами, и что после вызова элемента ввода submit браузер позаботится о форматировании входных данных в application/x-www-form-urlencoded формат представления, который отправляется в указанное целевое местоположение с помощью указанного метода HTTP-запроса, POSTв этом случае. Если мы войдем1 в firstNumber поле ввода и 2 в secondNumber поле ввода, браузер сгенерирует представление firstNumber=1&secondNumber=2 и отправьте это как полезную нагрузку тела фактического запроса на целевой ресурс.

Поэтому необработанный HTTP-запрос, отправленный серверу, может выглядеть следующим образом:

POST /../someResource
Host: www.acme.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 28
Accept: application/html

firstNumber=1&secondNumber=2

Сервер может выполнить вычисление и ответить дополнительной HTML-страницей, содержащей результат вычисления, поскольку запрос указывает, что клиент понимает этот формат.

Как уже указывал Бретон, не существует такой вещи, как "RESTful" URL или URI. URI/URL-адрес - это своего рода вещь, и она не должна передавать никакого значения клиенту / пользователю. В приведенном выше примере калькулятора пользователю просто не интересно, куда ему отправлять данные, он просто заинтересован в том, что после запуска поля ввода отправки запрос отправляется. Вся необходимая информация, необходимая для выполнения задачи, уже должна быть предоставлена ​​сервером.

Браузер также может не знать, что запрос фактически передает калькулятору некоторые входные параметры, это также может быть какая-то форма заказа, которая возвращает только следующее представление формы для продолжения процесса заказа, или какой-то совершенно другой вид ресурс. Он просто выполняет то, что требует спецификация HTML в таком случае, и ему все равно, что на самом деле делает сервер. Эта концепция позволяет браузеру использовать один и тот же формат представления для любых действий, таких как заказ некоторых вещей в предпочитаемом вами интернет-магазине, общение с лучшими друзьями, вход в онлайн-аккаунт и так далее.

Affordance некоторых элементов, например в случае ввода представить поле, которое обычно переводится как кнопка, определяет то, что вы должны, чтобы с ним. В случае кнопки или ссылки он обычно говорит вам нажать на нее. Другие элементы могут иметь другие возможности. Такое аффорданс также может быть выражено через отношения ссылок, например, сpreloadаннотированные ссылки, которые в основном сообщают клиенту, что он уже может загружать содержимое связанного ресурса в фоновом режиме, поскольку пользователь, скорее всего, получит этот контент следующим. Такие отношения ссылок, конечно, должны быть стандартизованы или следовать механизму расширения для типов отношений, как определено веб-ссылками.

Это фундаментальная концепция, которая используется в Интернете, и ее также следует использовать в архитектуре REST. Согласно "дяде Бобу" Роберту К. Мартину , архитектура основана на намерении, а цель архитектуры REST - отделение клиентов от серверов, чтобы серверы могли свободно развиваться в будущем, не опасаясь, что они сломают клиентов. К сожалению, это требует большой дисциплины, так как очень легко ввести взаимосвязь или добавить быстрые решения, чтобы выполнить работу и двигаться дальше. Как указал Джим Уэббер в архитектуре REST, вы, как поставщик услуг, должны попытаться разработать протокол доменного приложения, подобный текстовой компьютерной игре 70-х годов, которую клиенты будут выполнять, пока не достигнут конца процесса.

К сожалению, на самом деле многие так называемые API-интерфейсы "REST" делают все, кроме этого. Вы видите обмен в основном данными на основе JSON, которые указаны во внешней документации API, которую обычно трудно динамически интегрировать на лету. Формат, в котором должен выглядеть запрос, также жестко прописан во внешней документации, что приводит к большому количеству реализаций, интерпретирующих URI для возврата предопределенных типов.вместо использования какого-либо общего формата представления, согласованного заранее. Это предотвращает изменение серверов, поскольку теперь клиенты ожидают получить определенный формат данных (обратите внимание, не формат представления!) Для предопределенных URI. Этот обмен настраиваемым форматом данных, кроме того, не позволяет клиентам взаимодействовать с другими API, поскольку "формат данных" обычно связан с конкретным API. Мы знаем эту концепцию из прошлого по технологиям RPC, таким как Corba, RMI или SOAP, которые мы осуждаем как некое зло, хотя Peppol снова перешел на нее, заменив AS2 на AS4 в качестве протокола передачи по умолчанию в последнее время.

Что касается фактического вопроса, отправка данных в виде файла csv ничем не отличается от использования application/x-www-form-urlencodedпредставительства или подобные вещи. Джим Уэббер пояснил, что, в конце концов, HTTP - это всего лишь транспортный протокол, доменом приложения которого является передача документов через Интернет. Клиент и сервер должны как минимум поддерживатьtext/csvкак определено в RFC 7111. Этот файл CSV может быть сгенерирован в результате обработки типа мультимедиа, который определяет элементы формы, целевой элемент или атрибут для отправки запроса, а также метод HTTP для выполнения загрузки конфигурации.

Есть несколько типов носителей, которые поддерживают такие формы, как HTML, HAL Forms, halform, ion или Hydra. Однако в настоящее время я не знаю о типе носителя, который автоматически может кодировать входные данные вtext/csvнепосредственно, следовательно, может потребоваться определение и регистрация в реестре типов носителей IANA.

Я думаю, загрузка и загрузка полного набора параметров не должна стать проблемой. Как упоминалось ранее, целевой URI не имеет значения, поскольку клиент будет просто использовать URI для извлечения нового контента для обработки. Фильтрация по деловой дате также не должна быть сложной. Здесь сервер должен, однако, клиент со всеми возможностями, которые клиент просто может выбрать. В последние годы появились GraphQL и RestQL, которые представили SQL-подобный язык, который может быть нацелен на определенную конечную точку для получения отфильтрованного ответа. Однако в истинном смысле REST это нарушает идею REST, поскольку а) GraphQL, т.е. использует только одну конечную точку, что каким-то образом препятствует оптимальному использованию кеширования, и б) требует знания доступных полей вверху, что может привести к появлению связи клиентов. к базовой модели данных ресурса.

Активация или деактивация определенных параметров конфигурации - это просто вопрос запуска элементов управления гипермедиа, которые обеспечивают эту возможность. В формах HTML это может быть простой флажок, многострочный выбор в списке или тому подобное. В зависимости от формы и метода, который он определяет, он потенциально может отправить всю конфигурацию черезPUT или будьте внимательны с внесенными изменениями и выполняйте только частичное обновление через PATCH. Последний требует в основном вычисления представления изменений для обновленного и подачи на сервер необходимых шагов для преобразования текущего представления в желаемое. В соответствии со спецификацией PATH это должно выполняться внутри транзакции, чтобы выполнялись все шаги или ни один из них.

HTTP позволяет серверу предварительно проверять полученный запрос перед применением изменений. Для PUT в спецификации указано:

Исходный сервер ДОЛЖЕН проверить, что представление PUT согласуется с любыми ограничениями, которые сервер имеет для целевого ресурса, которые не могут или не будут изменены PUT. Это особенно важно, когда исходный сервер использует внутреннюю конфигурационную информацию, связанную с URI, для установки значений для метаданных представления в ответах GET. Когда представление PUT несовместимо с целевым ресурсом, исходный сервер ДОЛЖЕН либо сделать их согласованными, преобразовав представление или изменив конфигурацию ресурса, либо ответить соответствующим сообщением об ошибке, содержащим достаточно информации, чтобы объяснить, почему представление непригодно. Предлагаются коды состояния 409 (конфликт) или 415 (неподдерживаемый тип носителя),причем последнее относится к ограничениям на значения Content-Type.

Например, если целевой ресурс настроен так, чтобы всегда иметь Content-Type "text / html", а представление PUT имеет Content-Type "image / jpeg", исходный сервер должен выполнить одно из:

а. перенастроить целевой ресурс для отражения нового типа носителя;

б. преобразовать представление PUT в формат, совместимый с форматом ресурса, прежде чем сохранять его как новое состояние ресурса; или,

c. отклонить запрос с ответом 415 (неподдерживаемый тип носителя), указывающим, что целевой ресурс ограничен "текстом / html", возможно, включая ссылку на другой ресурс, который будет подходящей целью для нового представления.

HTTP не определяет точно, как метод PUT влияет на состояние исходного сервера, помимо того, что может быть выражено намерением запроса пользовательского агента и семантикой ответа исходного сервера....

Подводя итог этой публикации, вы должны либо использовать существующий тип мультимедиа, который позволяет вам обучать клиента необходимым или поддерживаемым входным параметрам, целевому местоположению для отправки запроса, операции, которую нужно использовать, а также типу мультимедиа, запрос должен быть отформатирован или определять свой собственный, который вы регистрируете в IANA. Последнее может быть необходимо, если вы хотите преобразовать ввод вtext/csvа затем загрузите CSV-представление на сервер. Проверка должна выполняться до того, как изменения будут применены к ресурсу. Фактический URI не должен иметь никакого отношения к клиентам, кроме как определять, куда отправить запрос, и поэтому может быть свободно выбран вами, разработчиком службы. Следуя этим шагам, вы в значительной степени получите свободу изменить свою серверную часть в любое время, и клиенты не сломаются, как следствие, если они поддерживают используемые медиа-типы.

Я бы предложил следующий мета-ресурс и методы.

Сделайте параметры активными и / или подтвердите их:

> PUT /parameters/<id>/meta HTTP/1.1
> Host: example.com
> Content-Type: application/json
> Connection: close
>
> {'active': true, 'require-valid': true}
>
< HTTP/1.1 200 OK
< Connection: close
<

Проверьте, являются ли параметры активными и действительными:

> GET /parameters/<id>/meta HTTP/1.1
> Host: example.com
> Connection: close
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< Connection: close
<
< {
<     'active': true,
<     'require-valid': true,
<     'valid': {'status': false, 'reason': '...'}
< }
<

В среде REST каждый URL является уникальным ресурсом. Каковы ваши ресурсы? Финансовый калькулятор действительно не имеет никаких очевидных ресурсов. Вам нужно покопаться в том, что вы называете параметрами, и вытащить ресурсы. Например, календарь амортизации для кредита может быть ресурсом. URL-адрес календаря может включать начальную дату, срок (в месяцах или годах), период (когда начисляются проценты), процентную ставку и начальный принцип. Со всеми этими значениями у вас есть определенный календарь платежей:

http://example.com/amort_cal/2009-10-20/30yrsfixed/monthly/5.00/200000

Теперь я не знаю, что вы рассчитываете, но ваша концепция списка параметров не выглядит RESTful. Как кто-то сказал, ваши требования звучат более XMLRPC. Если вы пытаетесь на отдых, вам нужны существительные. Вычисления не являются существительными, они - глагол, который действует на существительные. Вы должны перевернуть его, чтобы вытащить существительные из ваших кальков.

Редактировать: Действительно, URI помешал бы GET запросы от остальных идемпотентов.


Однако для проверки использование кодов состояния HTTP для уведомления о действительности запроса (для создания нового или изменения существующего "параметра") будет соответствовать модели Restful.

Сообщить с 400 Bad Request Код состояния, если предоставленные данные являются / являются недействительными и запрос должен быть изменен перед повторной отправкой ( HTTP / 1.1 коды состояния).

Это зависит от проверки во время представления, а не откладывания, как в вашем случае использования. Другие ответы имеют подходящие решения для этого сценария.

Другие вопросы по тегам