CORS в FastAPI

CORS в FastAPI FastAPI

Привет! Если ты разрабатываешь API, которое будет использоваться из браузера, рано или поздно столкнёшься с CORS. Это механизм безопасности, который ограничивает запросы с других доменов.

В этой статье мы разберём:

  • Что такое CORS
  • Зачем он нужен
  • Как настроить CORS в FastAPI
  • Параметры CORSMiddleware
  • Практические примеры

Что нужно знать перед началом

Для этого урока тебе понадобится:

  • Установленный FastAPI
  • Базовое понимание HTTP-запросов

Совет: CORS — это не баг, а защита. Браузеры блокируют запросы с других доменов по умолчанию для безопасности.

Основная часть

Что такое CORS

CORS (Cross-Origin Resource Sharing) — это механизм, который позволяет серверу разрешать или запрещать запросы с других доменов.

Пример проблемы:

# Ваше API: https://api.example.com
# Ваш фронтенд: https://frontend.example.com
# Запрос из фронтенда блокируется браузером!

CORS-заголовки:

Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type

Совет: Без CORS браузер блокирует запросы с других доменов. Настройка CORS нужна, чтобы разрешить их.

Установка CORS в FastAPI

FastAPI предоставляет встроенный CORSMiddleware из Starlette.

pip install fastapi[all]  # или просто fastapi

Импорт и использование:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Разрешает все домены
    allow_credentials=True,
    allow_methods=["*"],  # Разрешает все методы
    allow_headers=["*"],  # Разрешает все заголовки
)

@app.get("/")
async def root():
    return {"message": "Hello, World!"}

Важно: allow_origins=["*"] разрешает доступ с любых доменов. В продакшене так делать небезопасно.

Настройка CORS

Разрешённые домены (allow_origins)

# Конкретные домены
app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        "https://myfrontend.com",
        "https://app.myfrontend.com",
        "http://localhost:3000",  # Для разработки
    ],
)

Разрешённые методы (allow_methods)

# Только GET и POST
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["GET", "POST"],
)

Разрешённые заголовки (allow_headers)

# Только определённые заголовки
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_headers=["Authorization", "Content-Type"],
)

Разрешённые credentials (allow_credentials)

# Разрешить отправку cookies и токенов
app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://myfrontend.com"],
    allow_credentials=True,  # Разрешает отправку credentials
)

Важно: Если allow_credentials=Trueallow_origins не может быть ["*"]. Нужно указывать конкретные домены.

Полный пример CORS

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# Настройка CORS
origins = [
    "https://myfrontend.com",
    "https://app.myfrontend.com",
    "http://localhost:3000",
    "http://localhost:8080",
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
    allow_headers=["Authorization", "Content-Type", "Accept"],
    expose_headers=["X-Custom-Header"],  # Доступные клиенту заголовки
    max_age=600,  # Время кеширования CORS-запроса (в секундах)
)

@app.get("/")
async def root():
    return {"message": "CORS настроен!"}

CORS с использованием переменных окружения

import os
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

# Получаем разрешённые домены из переменной окружения
origins = os.getenv("ALLOWED_ORIGINS", "").split(",")

# Если переменная не установлена — разрешаем localhost для разработки
if not origins or origins == [""]:
    origins = ["http://localhost:3000", "http://localhost:8000"]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Совет: В продакшене указывай разрешённые домены через переменные окружения, чтобы безопасно управлять ими.

Предзапрос (Preflight Request)

Когда браузер отправляет небезопасный запрос (не GET, не с простыми заголовками), он сначала отправляет preflight запрос (OPTIONS).

# Preflight запрос (OPTIONS)
# Проверяет, разрешён ли основной запрос

# Основной запрос (POST)
# Отправляется только после успешного preflight

CORSMiddleware автоматически обрабатывает preflight запросы.

CORS и аутентификация

Если ты используешь JWT или cookies, нужно разрешить allow_credentials.

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://myfrontend.com"],
    allow_credentials=True,  # Разрешить cookies
    allow_methods=["*"],
    allow_headers=["Authorization", "Content-Type"],
)

На клиенте (JavaScript):

fetch("https://api.example.com/users", {
    credentials: "include",  # Важно! Отправляет cookies
    headers: {
        "Authorization": "Bearer " + token,
        "Content-Type": "application/json",
    }
})

Совет: Не забывай про credentials: "include" на фронтенде.

Обработка ошибок CORS

Если CORS настроен неправильно, браузер выдаст ошибку:

Access to fetch at 'https://api.example.com/users' from origin 'https://myfrontend.com' has been blocked by CORS policy

Как исправить:

  1. Проверь, что allow_origins содержит домен фронтенда.
  2. Проверь, что allow_methods содержит нужный метод.
  3. Проверь, что allow_headers содержит нужные заголовки.
  4. Если используешь allow_credentials=True, убедись, что allow_origins — не ["*"].

Задачи для закрепления

Задача 1. Настрой CORS для одного домена.

Задача 2. Настрой CORS для нескольких доменов.

Задача 3. Разреши только GET и POST методы.

Задача 4. Разреши отправку cookies.

Задача 5. Настрой CORS через переменные окружения.

Ответы:

Задача 1.

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://myfrontend.com"],
    allow_methods=["*"],
    allow_headers=["*"],
)

Задача 2.

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://frontend1.com", "https://frontend2.com"],
    allow_methods=["*"],
    allow_headers=["*"],
)

Задача 3.

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["GET", "POST"],
    allow_headers=["*"],
)

Задача 4.

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://myfrontend.com"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Задача 5.

import os
origins = os.getenv("ALLOWED_ORIGINS", "http://localhost:3000").split(",")
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_methods=["*"],
    allow_headers=["*"],
)

Нюансы и подводные камни

allow_origins=["*"] и allow_credentials=True несовместимы

Если нужно отправлять cookies или авторизационные заголовки, указывай конкретные домены.

CORS и preflight запросы

Preflight запросы могут замедлять работу API. Используй max_age для кеширования.

CORS в продакшене

Никогда не используй allow_origins=["*"] в продакшене. Указывай конкретные домены.

CORS и локальная разработка

Добавь http://localhost:3000 и http://127.0.0.1:3000 в список разрешённых для разработки.

Частые ошибки и как их избежать

Ошибка 1: allow_origins=["*"] с allow_credentials=True

Неправильно:

allow_origins=["*"],
allow_credentials=True

Правильно:

allow_origins=["https://myfrontend.com"],
allow_credentials=True

Ошибка 2: Забыл указать метод OPTIONS

CORSMiddleware автоматически добавляет OPTIONS.

Ошибка 3: Неправильный порт в разрешённых доменах

Указывай точный порт, если он отличается от 80/443.

Шпаргалка

ПараметрНазначение
allow_originsРазрешённые домены
allow_methodsРазрешённые методы HTTP
allow_headersРазрешённые заголовки
allow_credentialsРазрешить cookies и авторизацию
expose_headersЗаголовки, доступные клиенту
max_ageВремя кеширования preflight

Заключение

Сегодня мы:

  • Узнали, что такое CORS
  • Настроили CORS в FastAPI
  • Разобрали параметры CORSMiddleware
  • Практические примеры

КВИЗ

Что дальше?

Оцените статью
IMI-DS - PYTHON LERNEN
Содержание
Оглавление ×