ActixMiddleTechnical

Как настроить CORS в Actix-web?

Подключите крейт actix-cors и зарегистрируйте Cors::default() через App::wrap() первым в цепочке; настройте allowed_origin, allowed_methods, allowed_headers и supports_credentials() под нужды вашего SPA.

Настройка CORS в Actix-web

Для настройки CORS используется крейт actix-cors, который предоставляет готовый middleware Cors. Он реализует трейт Transform и регистрируется через App::wrap().

Добавьте зависимость в Cargo.toml:

[dependencies]
actix-web = "4"
actix-cors = "0.7"

Пример конфигурации для типичного SPA-приложения:

use actix_cors::Cors;
use actix_web::{http, middleware::Logger, web, App, HttpServer, HttpResponse};

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        // Настройка CORS для конкретного origin
        let cors = Cors::default()
            .allowed_origin("https://app.example.com")
            .allowed_origin("http://localhost:3000")
            .allowed_methods(vec!["GET", "POST", "PUT", "DELETE", "PATCH"])
            .allowed_headers(vec![
                http::header::AUTHORIZATION,
                http::header::CONTENT_TYPE,
                http::header::ACCEPT,
            ])
            .expose_headers(vec![http::header::CONTENT_DISPOSITION])
            .supports_credentials()   // разрешает cookie/credentials
            .max_age(3600);           // кэш preflight на 1 час

        App::new()
            .wrap(cors)
            .wrap(Logger::default())
            .route("/api/data", web::get().to(data_handler))
    })
    .bind("0.0.0.0:8080")?
    .run()
    .await
}

async fn data_handler() -> HttpResponse {
    HttpResponse::Ok().json(serde_json::json!({ "ok": true }))
}

Для полностью открытого API (публичный сервис без credentials) можно использовать:

let cors = Cors::permissive(); // эквивалент Access-Control-Allow-Origin: *

Для динамической проверки origin используйте метод allowed_origin_fn:

let cors = Cors::default()
    .allowed_origin_fn(|origin, _req_head| {
        origin.as_bytes().ends_with(b".example.com")
    })
    .allowed_methods(vec!["GET", "POST"]);

Порядок wrap важен: CORS должен быть первым (ближайшим к сети), чтобы preflight OPTIONS-запросы обрабатывались до любой аутентификации.

Подводные камни

  • supports_credentials() несовместим с allowed_origin("*") или Cors::permissive() — браузер отклонит такой ответ; нужно явно указать конкретный origin.
  • Порядок wrap: если middleware аутентификации зарегистрирован раньше CORS, preflight OPTIONS-запросы будут отклонены с 401 до того, как CORS-заголовки будут добавлены.
  • Отсутствие заголовка CONTENT_TYPE в allowed_headers: POST с JSON-телом отправляет preflight, и без разрешённого Content-Type он провалится.
  • Кэш preflight (max_age): слишком большое значение затрудняет отладку при изменении политики; в разработке лучше ставить 0 или 1.
  • Не добавлять кастомные заголовки в expose_headers: если сервер отдаёт, например, X-Request-Id, браузер скроет его от JS, если заголовок не объявлен в expose.
  • Cors::default() без явного allowed_origin блокирует все запросы с другого origin — это не «разрешить всё», а «запретить всё».
  • HTTPS/HTTP несоответствие: origin http://localhost:3000 и https://localhost:3000 — разные origins; оба нужно перечислить явно.

Common mistakes

  • Отвечать определением без production-сценария.
  • Не называть runtime boundary, security boundary или failure mode.
  • Игнорировать версию API, observability и тестовую проверку.

What the interviewer is testing

  • Объясняет механизм своими словами и без выдуманных API.
  • Называет реальные риски, диагностику и критерий корректности.
  • Связывает ответ с текущей документацией и миграционными ограничениями.

Sources

Related topics