Есть ли проблемы с реализацией пользовательских методов HTTP?

34

У нас есть URL в следующем формате

/ Экземпляр / {instanceType} / {InstanceId}

Вы можете вызвать его стандартными методами HTTP: POST, GET, DELETE, PUT. Однако есть еще несколько действий, которые мы предпринимаем, такие как «Сохранить как черновик» или «Куратор»

Мы подумали, что можем просто использовать собственные методы HTTP, такие как: DRAFT, VALIDATE, CURATE

Я думаю, что это приемлемо, так как стандарты говорят

«Набор общих методов для HTTP / 1.1 определен ниже. Хотя этот набор можно расширить, нельзя предполагать, что дополнительные методы совместно используют одинаковую семантику для отдельно расширенных клиентов и серверов».

А такие инструменты, как WebDav, создают свои собственные расширения.

Есть ли у кого-то проблемы с пользовательскими методами? Я имею в виду прокси-серверы и брандмауэры, но приветствуются любые другие проблемы. Должен ли я оставаться в безопасности и просто иметь параметр URL, такой как action = validate | curate | draft?

Хуан Мендес
источник
6
Как я снова цитирую в RFC 1925 года: «В разработке протоколов совершенство достигнуто не тогда, когда нечего добавить, а когда нечего убрать». - если это работает, нет причин добавлять в http.
4
Ничего плохого, если вы понимаете, что теперь вы используете собственный протокол, а не HTTP.
user16764
10
@ user16764 «Набор общих методов для HTTP / 1.1 определен ниже. Хотя этот набор может быть расширен, нельзя предполагать, что дополнительные методы используют одну и ту же семантику для отдельно расширенных клиентов и серверов». w3.org/Protocols/rfc2616/rfc2616-sec9.html Поэтому это разрешено, и это все еще HTTP
Хуан Мендес
imho нечего добавлять / удалять из HTTP, так как определения методов утверждают, что использование пользовательских методов уже допустимо в пределах области HTTP / 1.1, но нельзя ожидать, что они будут использовать одну и ту же семантику, поэтому я предполагаю, что точки из @MichaelT и Juan Mendes могут быть несколько умиротворенным
Prof83

Ответы:

42

Одним из основных ограничений , HTTP и с центральным элементом дизайна REST является равномерная интерфейс обеспечивает (помимо всего прочего) небольшой фиксированный набор методов , которые применяются универсально ко всем ресурсам. У единого ограничения интерфейса есть ряд достоинств и недостатков. Я цитирую из Филдинга здесь.

Единый интерфейс:

  • проще
  • отделяет реализации от услуг, которые они предоставляют.
  • допускает многоуровневую архитектуру, включая такие вещи, как балансировщики нагрузки HTTP (nginx) и кэши (лак).

С другой стороны, единый интерфейс:

  • снижает эффективность, потому что информация передается в стандартизированной форме, а не в той, которая специфична для потребностей приложения.

Компромиссы «разработаны для общего случая Интернета» и позволили построить большую экосистему, которая обеспечивает решение многих распространенных проблем в веб-архитектурах. Придерживаясь единого интерфейса, ваша система получит выгоду от этой экосистемы, а ее разрушение усложнит задачу. Возможно, вы захотите использовать балансировщик нагрузки, такой как nginx, но теперь вы можете использовать только балансировщик нагрузки, который понимает DRAFT и CURATE. Возможно, вы захотите использовать слой кэширования HTTP, такой как Varnish, но теперь вы можете использовать только слой кэширования HTTP, который понимает DRAFT и CURATE. Возможно, вы захотите обратиться к кому-нибудь за помощью в устранении неполадок при сбое сервера, но никто больше не знает семантику запроса CURATE. Может быть трудно изменить предпочитаемые клиентские или серверные библиотеки, чтобы понять и правильно реализовать новые методы. И так далее.

Правильный * способ представить это как преобразование состояния ресурса (или связанных ресурсов). Вы не черпаете сообщение, вы преобразуете его draftсостояние trueили создаете draftресурс, который содержит изменения и ссылки на предыдущие черновые версии. Вы НЕ ОБРАЩАЕТЕ сообщение, вы преобразуете его curatedсостояние trueили создаете curationресурс, который связывает сообщение с пользователем, который его курировал.

* Правильно в том, что оно наиболее точно следует архитектурным принципам REST.

Рейн Хенрикс
источник
Спасибо за комментарии по балансировке нагрузки, я обязательно посмотрю на это. Знаете ли вы о ресурсе, в котором указано, являются ли пользовательские методы приемлемыми или нет?
Хуан Мендес
2
Я не вижу никакого преимущества для пользовательских методов, если они не являются частью широко поддерживаемого расширения, такого как WEBDAV (и даже тогда, не так много), поэтому я никогда не изучал его. Я бы просто рекомендовал вам относиться к этим изменениям как к изменениям состояния. Сеть прекрасно работает с методами, которые у нас уже есть. На самом деле нет веских причин добавлять больше, если они не имеют смысла как часть унифицированного интерфейса (например, PATCH).
Рейн Хенрикс
5
Я вижу преимущество в том, как вы хотите, чтобы ваш HTTP-сервис работал на себя. однако «нельзя предполагать, что дополнительные методы используют одну и ту же семантику» - достаточно справедливо, НО это все еще является частью области действия HTTP / 1.1, поэтому брандмауэры, прокси-серверы, балансировщики нагрузки и тому подобное должны допускать такую ​​возможность, если они не t тогда они не реализуют HTTP / 1.1 должным образом?
Prof83
Вы, вероятно, правы от спокойного POV, однако я не могу понять, почему пользовательские глаголы должны быть проблемой. Все инструменты должны относиться к ним так же, как к POST, т. Е. «Ресурс, вероятно, меняется, и это все, что мы знаем».
Маартин
7

Я бы предпочел проектировать их как подресурсы, для которых вы выполняете запрос POST.

Учитывая, что у вас есть ресурс /instance/type/1, я хотел бы, чтобы представление этого ресурса передавало пару ссылок на «действия», которые могут быть выполнены над ресурсом, такие как /instance/type/1/draftи /instance/type/1/curate. В JSON это может быть так просто:

{
    "some property":"the usual value",
    "state": "we can still inform the client about the current state",
    "draft": "http://server/instance/type/1/draft",
    "curate": "http://server/instance/type/1/curate"
}

Это позволяет клиенту четко указывать, что должно произойти, во время запроса POST на ссылку, предоставленную curateучастником. Размещенный там ресурс может содержать аргументы, подробно описывающие событие, которое, возможно, вызовет переход состояния.

Недостаток «наивного» подхода к перемещению между возможными состояниями ресурса имеет недостаток в том, что он не фиксирует, какие события привели к этим переходам.

Переходы между состояниями обычно происходят в ответ на определенные события, и я бы лучше зафиксировал эти события, чем позволил бы клиенту решить, что что-то сейчас находится в определенном «состоянии». Это также делает проверку намного сложнее. Кроме того, вы не сможете собрать какие-либо «аргументы», если не опишите их и в самом государстве. И потом становится все странно, когда какой-то код меняет код без реального перехода состояния и необходимой проверки, и все это быстро превращается в беспорядок.

Дэйв Ван ден Эйнде
источник
Хороший ответ. Быть способным предоставить аргументы для переходов состояний и иметь сервер, инкапсулирующий и управляющий ими, безусловно, лучший подход.
Томас В.
Компания, в которой я сейчас нахожусь (VMware), делает это. GET /vms/some-idвозвращает ссылки на подобные действия, POST /vms/some-id/restartи мы используем его, чтобы определить, следует ли включать или отключать действия. У меня есть отношения любовь / ненависть с HATEOAS :)
Хуан Мендес
Было бы гораздо больше смысла, если бы выполняемое действие было глаголом запроса, а не каким-либо случайным параметром запроса, сегментом пути ресурса или свойством body.
Мэтью Уайтед
Вы не можете связать с глаголом.
Дейв Ван ден Эйнде
6

Я думаю, что нестандартный метод HTTP - это самый лучший способ реализовать действия сущности. Добавление действия в тело объекта (POST) не кажется правильным, это не часть вашего объекта (хотя результат может быть сохранен в нем). Кроме того, с помощью пользовательских методов HTTP прокси-серверы могут определять свои действия без необходимости разбора тела объекта.

Это как CRUD, вы всегда хотели бы реализовать их, но также у вас есть свой собственный определенный набор действий (для каждой единицы). Я действительно не вижу, в чем проблема, продлевая их.

Также @Rein Henrichs «Вы не черновите сообщение, вы преобразуете его черновое состояние в true или создаете черновой ресурс», мне кажется ложным. draftsСвойство будет использоваться для постоянного сохранения состояния, а не для создания преобразования. Действия даже не обязательно приводят к состоянию или сохраняются в свойстве. Создание отдельного объекта для каждого состояния / преобразования кажется еще более нечетким. Попытайтесь сохранить ту же ссылку (URI) на объект.

Роберт де З
источник
1
Это справедливая точка зрения, хотя и с этим широко не согласны, я вижу причины этого и не согласен с понижением (особенно без комментариев от избирателя). Давайте возьмем в качестве примера обработку исключений PHP. «Практика», похоже, склоняется к использованию определенных типов исключений, чтобы предложить тип исключения, даже игнорируя реальное сообщение, такое как RuntimeException и BadMethodCallException. Так почему же так широко оспаривается использование DRAFT, если использование пользовательских методов уже считается частью области действия HTTP / 1.1? И балансировщики нагрузки и прокси должны действительно принять эту возможность
Prof83