Pull to refresh

Comments 15

Overkill, нет? У вас вообще как разграничение прав реализовано? У юзера должен быть мандат — список всех его разрешений. Вот его и передавать на клиент один раз при логине
OPTIONS полюбому придётся реализовывать каждому API, т.к. во время кросдоменных xhr и fetch запросов — первым всегда идёт OPTIONS. Если его не поддержать, то API физически работать не сможет. Ну разве только если оно не направлено сугубо под специфичную платформу, ну например бек для мобильного ПО или что-то такое.
Для таких запросов достаточно веб-сервер настроить для отдачи разрешенных ориджинов в ответ на опшинс запрос.
Не всегда это возможно. Например ориджин, зачастую генерируется на основании Refererrer заголовка, т.к. опция "*" не всегда работает (признаться, были там какие-то случаи, связанные с кроссдоменными запросами как раз, но не могу вспомнить). Можно, конечно, запилить плагин к серверу, но надо ли и не проще ли на уровне ПО решать? Помимо этого, по-хорошему надо по-разному формировать ответы на основе роутинга, ну например 404 никто не отменял.

Т.е. если делать вывод, да, это всё решаемо в зависимости от задачи и ситуации, но всё же без обработки OPTIONS как таковой не обойтись.

Да можно и обойтись, если просто какое-то SPA делать — просто дергать не api.example.com, а example.com/api

Так я же не спорю, но всё равно делов на 5 минут, а проблем на одну меньше становится (а точнее возможностей на одну больше).
Используем django rest framework. Из коробки OPTIONS, очень удобно.

В статье описано неправильное использование метода OPTIONS. Этот метод должен говорить в заголовке Allow о всех HTTP-методах, которые поддерживаются по данному URL. Независимо от того, имеет пользователь какие-то права для перечисленных там методов или нет.


Применить этот метод на практике в REST API очень даже можно. Мы в проекте для документирования API используем спецификацию OpenAPI 3.0. Когда клиент делает обращение по методу OPTIONS, мы парсим документацию и достаем из нее все эндпоинты по запрашиваемому URL (во время парсинга лишние поля вроде описаний выпиливаются). Таким образом клиент может получать правила валидации (допустим, maxLength, required, pattern и пр.), которые будет использовать для валидации форм «на лету» — без отправки на сервер.

А есть доказательства, что использование метода OPTIONS с учётом авторизации – неправильное?
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 как для авторизованного, так и для неавторизованного пользователя. Этот подход успешно работает в реальном приложении.
Sign up to leave a comment.

Articles