Что такое HTTP-метод OPTIONS и его основное применение?

Ответ

OPTIONS — это один из стандартных HTTP-методов, который используется для описания параметров и возможностей взаимодействия с целевым ресурсом (URL).

Клиент может использовать OPTIONS для запроса информации о том, какие методы (GET, POST, PUT и т.д.) и заголовки поддерживаются сервером для данного ресурса, не инициируя при этом никаких действий с самим ресурсом.

Основное применение — CORS Preflight Request (предварительный запрос)

Это самый частый сценарий использования OPTIONS. Браузер автоматически отправляет OPTIONS-запрос перед "сложными" кросс-доменными запросами, чтобы проверить, разрешает ли сервер такой запрос.

"Сложным" запрос считается, если он:

  • Использует методы, отличные от GET, POST, HEAD.
  • Содержит заголовки, отличные от стандартных (например, Authorization, X-Custom-Header).
  • Имеет Content-Type, отличный от application/x-www-form-urlencoded, multipart/form-data или text/plain.

Как это работает:

  1. Браузер хочет отправить PUT запрос с заголовком Authorization с сайта A.com на API B.com/resource.
  2. Перед отправкой PUT, браузер автоматически посылает OPTIONS запрос на B.com/resource.
  3. Сервер B.com должен ответить на OPTIONS запрос, указав в заголовках ответа, что он разрешает метод PUT и заголовок Authorization с домена A.com (например, через Access-Control-Allow-Methods: PUT и Access-Control-Allow-Headers: Authorization).
  4. Если ответ на OPTIONS положительный, браузер отправляет основной PUT запрос.

Пример обработки в Go (net/http):

func handler(w http.ResponseWriter, r *http.Request) {
    // Устанавливаем общие заголовки для всех ответов
    w.Header().Set("Access-Control-Allow-Origin", "*") // В проде лучше указать конкретный домен

    // Обработка предварительного запроса
    if r.Method == http.MethodOptions {
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
        w.WriteHeader(http.StatusNoContent) // 204 No Content - стандарт для успешного preflight
        return
    }

    // Обработка основного запроса (GET, POST и т.д.)
    w.Write([]byte("Hello, world!"))
}

Примечание: В реальных приложениях для обработки CORS обычно используют готовые middleware-библиотеки (например, gorilla/handlers, rs/cors или встроенные в фреймворки вроде Gin/Echo), которые делают эту логику более надежной и простой в настройке.

Ответ 18+ 🔞

Ну слушай, вот есть у нас в HTTP такая штука — метод OPTIONS. Это как бы предварительный звонок, типа «эй, сервак, а можно я тебе вот так вот приеду?». Сука, просто представь: ты хочешь отправить запрос с одного сайта на другой, а браузер, этот перестраховщик ебаный, сначала стучится OPTIONS-ом, чтобы проверить, не наебешь ли ты ему всю систему.

Зачем это вообще нужно? В основном, блядь, для CORS — этих самых кросс-доменных запросов. Браузер, падла такая, сам отправляет OPTIONS перед тем, как сделать «сложный» запрос. А сложный — это если ты лезешь не с GET или простеньким POST, а, например, с PUT, да ещё и с каким-нибудь своим хитрожопым заголовком вроде Authorization. Или Content-Type у тебя не text/plain, а какой-нибудь application/json. Короче, любое отклонение от стандартного сценария — и вот тебе, на, получай предварительный запрос.

Как это выглядит на практике:

  1. Твой фронт на frontend.com хочет отправить DELETE запрос с заголовком X-API-Key на api.backend.com/data.
  2. Браузер, блядь, не дурак, он сначала шлёт OPTIONS запрос на тот же адрес: «Э, api.backend.com, а можно я тебе пришлю DELETE и заголовок X-API-Key?».
  3. Сервер должен ответить: «Да, хуле, можно. Вот тебе заголовки Access-Control-Allow-Methods: DELETE и Access-Control-Allow-Headers: X-API-Key. И заходи, родной, с frontend.com».
  4. Только после этого, довольный браузер отправляет уже настоящий, основной DELETE запрос. А если сервер в ответ на OPTIONS накосячил — всё, пиши пропало, запрос не уйдёт, будет тебе в консоли красненькое.

Вот как это может выглядеть на Go, если писать в лоб:

func handler(w http.ResponseWriter, r *http.Request) {
    // Сразу говорим, откуда можно приходить (в проде "*" — это пиздец как небезопасно, указывай конкретный домен)
    w.Header().Set("Access-Control-Allow-Origin", "*")

    // Ловим этот самый предварительный запрос
    if r.Method == http.MethodOptions {
        // Разрешаем методы
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        // Разрешаем заголовки
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, X-API-Key, Authorization")
        // Статус 204 — всё ок, но тела ответа нет
        w.WriteHeader(http.StatusNoContent)
        return
    }

    // А тут уже обрабатываем нормальные запросы (GET, POST и т.д.)
    w.Write([]byte("Всё работает, епта!"))
}

Важный момент, нахуй: В нормальных, боевых проектах никто эту хуйню вручную не прописывает. Берут готовые middleware — типа rs/cors для чистого net/http или встроенные средства в Gin/Echo. Они всё за тебя сделают, а то сам накосячишь с заголовками и потом будешь ебаться, почему запросы не летят.