Comments 15
В любом случае, я предлагаю взглянуть на проблему шире, с точки зрения HATEOAS.
Т.е. если делать вывод, да, это всё решаемо в зависимости от задачи и ситуации, но всё же без обработки OPTIONS как таковой не обойтись.
В статье описано неправильное использование метода OPTIONS. Этот метод должен говорить в заголовке Allow о всех HTTP-методах, которые поддерживаются по данному URL. Независимо от того, имеет пользователь какие-то права для перечисленных там методов или нет.
Применить этот метод на практике в REST API очень даже можно. Мы в проекте для документирования API используем спецификацию OpenAPI 3.0. Когда клиент делает обращение по методу OPTIONS, мы парсим документацию и достаем из нее все эндпоинты по запрашиваемому URL (во время парсинга лишние поля вроде описаний выпиливаются). Таким образом клиент может получать правила валидации (допустим, maxLength, required, pattern и пр.), которые будет использовать для валидации форм «на лету» — без отправки на сервер.
The OPTIONS method represents a request for information about the communication options available on the request/response chain identified by the Request-URI. RFC 2316, секция 9.2
Простыми словами: информация должна возвращаться лишь в зависимости от запрашиваемого URL. Другие заголовки (ни Cookie
, ни Authorization
) не должны влиять на суть ответа. Этого уже достаточно, чтобы понять, что вы всё делаете неправильно.
Делать какую-либо бизнес-логику или отрисовку интерфейса на основе заголовка Allow
в принципе неправильно. Например, есть Google Docs. Что можно получить из Allow
:
- GET — я могу посмотреть документ.
- PUT & PATCH — я могу изменить документ.
- DELETE — я могу удалить документ.
Это всё? Но в 99% приложений бизнес-логика насчитывает куда больше возможностей для управления ресурсами. За одним методом POST может содержаться масса действий, которые могут требовать разные группы доступа. Например, под метод POST можно спрятать копирование документа в текущую папку (POST /api/v1/doc/11/clone
), а также возможность переслать его другому пользователю (POST /api/v1/doc/11/share
). Как вы будете это с помощью OPTIONS реализовывать? Никак. Этот HTTP-метод для этого не предусмотрен.
Вы всё делаете неправильно
Призываю вас быть осторожнее в формулировках. Исход нашей дискуссия не повлияет на ценность статьи, даже если я окажусь неправ. Кстати, приведённый в статье код никак не принимает во внимание авторизацию и лишь автоматизирует выдачу ответов по запросу OPTIONS.
Стандарт говорит:
This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.В стандарте идёт речь о цепочке запрос/ответ, определяемой по URI запроса. Надо ли серверу принимать во внимание такие хедеры как
Authorization
и формировать ответ в зависимости от прав текущего пользователя? Я полагаю, да. В стандарте явного запрета использовать хедеры нет.Давайте взглянем на ситуацию с другой точки зрения. Должен ли метод OPTIONS вернуть NotAuthorized, если текущей пользователь не аутентифицирован? Если вы ответите нет, то я скажу, что в вашем проекте что-то не в порядке с безопасностью.
Почему делать бизнес-логику или отрисовку на основе хедера Allow – неправильно, я не понял. Вы же сами вначале говорили о том, что вы в вашем проекте делаете валидацию форм таким образом?
Что касается методов, то приведённые вами примеры – это не RESTful подход. Это что-то другое. Вас сразу должно насторожить наличие глаголов в ваших Url.
Надо ли серверу принимать во внимание такие хедеры как Authorization и формировать ответ в зависимости от прав текущего пользователя? Я полагаю, да.
И каким образом вы в браузере сделаете запрос к такому эндпоинту? Перед каждым AJAX-запросом на другой ориджин браузер будет самопроизвольно прослушивать запрашиваемый адрес по методу OPTIONS в надежде увидеть успешный ответ, а не какой-нибудь 401, 403 или 404. Вы туда свой Authorization
никак не пропихнете. Как вариант, проблему можно решить, храня API и фронт на одном ориджине, но это такое себе решение.
Должен ли метод OPTIONS вернуть NotAuthorized, если текущей пользователь не аутентифицирован? Если вы ответите нет, то я скажу, что в вашем проекте что-то не в порядке с безопасностью.
Если пользователь узнает о существовании, скажем, банковской карты с UUID 123e4567-e89b-12d3-a456-426655440000
(например, OPTIONS /v1/bank-cards/123e4567-e89b-12d3-a456-426655440000
), то в этом ничего страшного не будет. Да, он узнает, что в системе существует какая-то банковская карта, ID которой был сгенерирован совершенно случайно. О том, как эта карта использовалась в системе и кому она принадлежит, он по методу OPTIONS узнать не сможет. Не вижу весомых причин возвращать 401 Not Authorized в методе OPTIONS. Но если вы можете придумать кейсы, где такое недопустимо, то поведайте о них.
Почему делать бизнес-логику или отрисовку на основе хедера Allow – неправильно, я не понял. Вы же сами вначале говорили о том, что вы в вашем проекте делаете валидацию форм таким образом?
Вы неправильно поняли. Мы в теле ответа отдаем JSON по спецификации JSON Schema, который описывает payload, принимаемый бэкендом. Мы парсим нашу документацию из формата Open API 3.0 (которая, кстати, является валидной JSON Schema), вырезаем из нее всё лишнее и отдаем клиенту. Заголовок Allow
у нас реализован только для того, чтобы соответствовать спецификации, и чтобы браузер корректно мог кроссдоменные запросы отправлять. Никто кроме браузера в нашей системе на этот заголовок не смотрит.
Что касается методов, то приведённые вами примеры – это не RESTful подход. Это что-то другое. Вас сразу должно насторожить наличие глаголов в ваших Url.
Слепо по REST можно писать только TODO-листы в рамках обучения. Увы, в HTTP нет методов CLONE, SHARE и вообще кастомных HTTP-методов, потому глаголы их и заменяют. REST не регламентирует подобное поведение. Если не знаете, какой HTTP-метод использовать, то пишите в конце ресурса глагол и отправляйте туда POST.
Похоже, идея авторизации и использования OPTIONS в качестве довеска к HATEOAS не всегда проходит.
Когда я писал статью, я имел в виду чистый RESTful подход безо всяких кроссдоменных запросов. Ну что ж, тогда полезной частью статьи остаётся автоматизация реализации OPTIONS в ASP.NET Web API 2.
Ещё раз благодарю за подробные ответы!
Нужен ли метод OPTIONS в REST-сервисах?