Ачааллж байна...
Энэ хичээл нэг тодорхой асуудалд зориулагдана. Тэр асуудал нь одоохондоо танд гараагүй — гэхдээ гарах нь гарцаагүй, тэр өдөр таныг гэнэт гайхшруулах болно.
Асуудал ийм харагдана. Та вэб хуудас бичиж, түүнээс өөрийн API руу хандахыг оролдоно. Код нь төгс. API нь ажиллаж байна. Гэтэл browser-ийн консол дээр улаан алдаа:
Access to fetch at 'http://127.0.0.1:8000/notes' from origin 'http://localhost:3000'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
on the requested resource.Хүсэлт огт очоогүй. Таны API юу ч хүлээж аваагүй. Browser өөрөө зогсоосон.
Энэ бол CORS. Энэ хичээлд бид түүнийг ойлгож, шийдлийг нэг мөрөөр авна.
Эхлээд нэг зүйлийг тодруулъя: та энэ сургалтын турш API-даа хэдэн зуун удаа хандсан. Яагаад CORS гараагүй вэ?
Учир нь та гурван арга-аар л хандсан:
/docs хуудас — тэр нь таны API-тай ижил хаяг дээр байдаг (127.0.0.1:8000). Ижил хаяг тул CORS шалгалт хамаарахгүй.
Browser-ийн хаягийн мөр — хаяг шууд бичих нь "хуудсаас илгээсэн хүсэлт" биш тул CORS хамаарахгүй.
requests код — Python скрипт бол browser биш. CORS бол зөвхөн browser-ийн дүрэм.
Тэгэхээр CORS нь зөвхөн нэг тодорхой нөхцөлд гардаг: нэг вэб хуудас өөр хаяг дээрх API руу JavaScript-ээр хандах үед.
Тэр өдөр ирэх нь гарцаагүй — таны API-г ашиглах frontend бичигдэх өдөр. Тиймээс урьдчилан ойлгох нь зүйтэй.
CORS-ыг ойлгохын тулд эхлээд яагаад browser хүсэлт хаадгийг ойлгох хэрэгтэй.
Browser-т same-origin policy гэсэн үндсэн аюулгүй байдлын дүрэм байдаг. Тэр нь: нэг хаяг дээрх хуудас өөр хаяг руу чөлөөтэй хандаж болохгүй.
Origin гэдэг нь гурван зүйлийн нийлбэр:
Protocol — http эсвэл https
Domain — example.com, localhost, 127.0.0.1
Port — 8000, 3000, 80
Гурвуулаа таарвал ижил origin. Аль нэг нь өөр бол өөр origin.
Жишээ:
Хаяг 1 | Хаяг 2 | Ижил үү |
|---|---|---|
|
| Тийм (зам хамаагүй) |
|
| Үгүй (port өөр) |
|
| Үгүй (protocol өөр) |
|
| Үгүй (domain өөр!) |
Сүүлийн мөрийг анзаараарай. localhost болон 127.0.0.1 нь ижил компьютер боловч browser тэдгээрийг өөр origin гэж үздэг. Энэ нь эхлэгчдийг байнга гайхшруулдаг.
Одоо хамгийн чухал хэсэг: энэ дүрэм яагаад оршдог вэ?
Хэрэв энэ дүрэм байхгүй байсан бол дараах зүйл болох боломжтой.
Та банкны сайтдаа нэвтэрсэн байна. Browser-т таны нэвтрэлтийн cookie хадгалагдсан.
Дараа нь та огт өөр сайт руу орлоо — жишээ нь хөгжилтэй зурагтай сайт. Тэр сайт дотор нуугдсан JavaScript байна:
javascript
fetch("https://таны-банк.mn/api/transfer", {
method: "POST",
body: JSON.stringify({to: "халдагчийн-данс", amount: 1000000})
})Cookie автоматаар илгээгдэнэ. Банк "энэ бол нэвтэрсэн хэрэглэгч" гэж үзнэ. Мөнгө шилжинэ.
Same-origin policy яг үүнээс сэргийлдэг. Хөгжилтэй зурагтай сайт нь банкны хаягаас өөр origin тул browser хүсэлтийг зогсооно.
Тиймээс CORS бол саад биш, хамгаалалт юм. Тэр нь таны хэрэглэгчдийг хамгаалж байна.
Барилгын хамгаалалтын үйлчилгээг төсөөлье.
Хаалганы харуулд зочны жагсаалт байна. Хэн нэгэн орох гэвэл харуул жагсаалтаа шалгана. Жагсаалтад байвал оруулна; байхгүй бол оруулахгүй.
Анхдагчаар жагсаалт хоосон. Хэн ч орж чадахгүй — энэ бол хамгийн аюулгүй байдал.
Барилгын эзэн (та) хэн орж болохыг тодорхой зааж өгөх ёстой: "Энэ компанийн хүмүүсийг оруул."
CORS тохиргоо бол тэр жагсаалт юм. Та API-даа "эдгээр хаягаас ирсэн хүсэлтийг зөвшөөр" гэж хэлдэг.
Гол зүйл: зөвшөөрөл нь серверээс ирдэг, browser-ээс биш. Browser зөвхөн серверийн хариуг шалгаж, зөвшөөрөгдсөн эсэхийг харна.
Одоо техникийн талыг богино харъя.
Хуудас өөр origin руу хүсэлт илгээхэд browser нь тэр хүсэлтэд Origin header нэмдэг:
Origin: http://localhost:3000"Би энэ хаягнаас ирж байна" гэсэн үг.
Сервер хариулахдаа Access-Control-Allow-Origin header буцаах ёстой:
Access-Control-Allow-Origin: http://localhost:3000"Тийм, тэр хаягийг зөвшөөрч байна."
Browser хариуг хүлээж аваад тэр header-ыг шалгана. Хэрэв байхгүй, эсвэл тохирохгүй бол — browser хариуг JavaScript-д өгөхгүй, алдаа шидэнэ.
Чухал нарийн ялгаа: таны API нь хүсэлтийг хүлээж авсан, боловсруулсан, хариу илгээсэн. Browser нь тэр хариуг JavaScript-д дамжуулахаас татгалзсан.
Тиймээс терминал дээрээ хараад та хүсэлт ирснийг харна. Гэхдээ browser-ийн консол дээр алдаа гарна. Энэ нь эхэндээ төөрөгдүүлдэг — "хүсэлт очсон шүү дээ, яагаад алдаа гарч байна вэ?"
Нэг зүйлийг мэдэж байх нь зүйтэй. Зарим хүсэлтэд browser нь урьдчилсан хүсэлт (preflight) илгээдэг.
Энэ нь OPTIONS гэсэн method-оор явдаг:
OPTIONS /notes
Origin: http://localhost:3000
Access-Control-Request-Method: POST"Би POST илгээх гэж байна, зөвшөөрөх үү?" гэсэн асуулт.
Сервер зөвшөөрвөл browser жинхэнэ хүсэлтээ илгээнэ. Зөвшөөрөхгүй бол огт илгээхгүй.
Preflight хэзээ явдаг вэ? Ерөнхийдөө "энгийн бус" хүсэлтэд: POST JSON-той, PUT, DELETE, эсвэл тусгай header (жишээ нь таны X-API-Key) байвал.
Тиймээс таны API-д бичих үйлдэл болон API key хоёулаа preflight үүсгэнэ. Энэ нь чухал: CORS тохируулахдаа тэдгээрийг зөвшөөрөх ёстой.
Сайн мэдээ: FastAPI-ийн CORSMiddleware үүнийг автоматаар зохицуулна. Та preflight-ийн тухай бодох шаардлагагүй.
Одоо кодыг харъя. FastAPI-д CORS-ыг нэг хэрэгслээр тохируулна: CORSMiddleware.
main.py-д нэмнэ:
python
# main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.config import APP_NAME, APP_VERSION
from app.database import create_db_and_tables
from app.errors import register_error_handlers
from app.routers import notes
app = FastAPI(
title=APP_NAME,
version=APP_VERSION,
description="Тэмдэглэл хадгалах API (v3 — өгөгдлийн сантай)",
)
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:3000"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
register_error_handlers(app)
app.include_router(notes.router)
@app.on_event("startup")
def on_startup():
create_db_and_tables()Хадгална.
Кодын задаргаа
from fastapi.middleware.cors import CORSMiddleware — CORS хэрэгслийг import хийж байна.
python
app.add_middleware(CORSMiddleware, ...)Middleware гэдэг нь шинэ ойлголт. Товчхон: middleware бол хүсэлт бүрийг дундуур нь өнгөрүүлдэг давхарга юм. Тэр нь хүсэлт орж ирэхэд болон хариу гарахад ажилладаг.
Курс 2-ын decorator-ийн зүйрлэлээр: middleware бол бүх endpoint-ийг бүхэлд нь боох цаас. Endpoint бүрд гар хүрэхгүйгээр бүгдэд нь нэмэлт зан төлөв өгдөг.
CORSMiddleware нь хариу бүрд зохих CORS header нэмдэг, мөн preflight хүсэлтэд автоматаар хариулдаг.
Дөрвөн параметрийг задалъя.
allow_origins — аль хаягуудаас хандахыг зөвшөөрөх. Жагсаалт. Энэ бол "зочны жагсаалт".
allow_credentials — cookie болон нэвтрэлтийн мэдээлэл илгээхийг зөвшөөрөх эсэх. Хэрэв таны frontend нь нэвтрэлттэй ажиллах бол True хэрэгтэй.
allow_methods — аль HTTP method-ыг зөвшөөрөх. ["*"] нь бүгд. Эсвэл тодорхой заж болно: ["GET", "POST"].
allow_headers — аль header-ыг зөвшөөрөх. ["*"] нь бүгд. Таны X-API-Key header ажиллахын тулд энэ нь чухал — хэрэв тодорхой жагсаалт бичвэл түүнийг оруулах ёстой.
Кодонд "http://localhost:3000" гэж бичсэн нь буруу — Бүлэг 9-т сурсан зарчим. Frontend-ийн хаяг нь орчин бүрд өөр байна: хөгжүүлэлтэд localhost:3000, бодит серверт https://миний-сайт.mn.
.env-д нэмнэ:
API_KEY=нууц-түлхүүр-12345
APP_NAME=Тэмдэглэлийн API
APP_VERSION=3.0.0
MAX_PAGE_SIZE=50
DATABASE_URL=sqlite:///notes.db
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173Хоёр хаяг таслалаар тусгаарлагдсан (3000 нь React-ийн ердийн port, 5173 нь Vite-ийнх).
app/config.py-д нэмнэ:
python
# app/config.py
import os
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.environ["API_KEY"]
APP_NAME = os.getenv("APP_NAME", "API")
APP_VERSION = os.getenv("APP_VERSION", "1.0.0")
MAX_PAGE_SIZE = int(os.getenv("MAX_PAGE_SIZE", "50"))
DATABASE_URL = os.environ["DATABASE_URL"]
ALLOWED_ORIGINS = os.getenv("ALLOWED_ORIGINS", "").split(",").split(",") — таслалаар салгаж жагсаалт болгож байна. Бүлэг 9-ийн "бүх утга текст" занга энд дахин: .env-ээс текст ирдэг тул гараар жагсаалт болгох ёстой.
main.py-д ашиглана:
python
# main.py (import болон middleware хэсэг)
from app.config import ALLOWED_ORIGINS, APP_NAME, APP_VERSION
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)Хадгална.
Одоо frontend-ийн хаяг өөрчлөгдвөл зөвхөн .env засна. Кодонд гар хүрэхгүй.
CORS ажиллаж байгааг батлахад frontend хэрэгтэй. Гэхдээ жижиг HTML файлаар туршиж болно.
Төслийн хавтаст test_cors.html файл үүсгэнэ:
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CORS туршилт</title>
</head>
<body>
<h1>CORS туршилт</h1>
<button onclick="loadNotes()">Тэмдэглэл татах</button>
<pre id="result"></pre>
<script>
async function loadNotes() {
const output = document.getElementById("result");
try {
const response = await fetch("http://127.0.0.1:8000/notes");
const data = await response.json();
output.textContent = JSON.stringify(data, null, 2);
} catch (error) {
output.textContent = "Алдаа: " + error.message;
}
}
</script>
</body>
</html>Хадгална.
Энэ бол JavaScript — таны сурсан хэл биш. Ойлгох шаардлагагүй; энэ нь зөвхөн туршилтын хэрэгсэл. Товч: товч дарахад API руу хүсэлт илгээж, үр дүнг харуулна.
Туршилт 1: file:// протоколоор
Файлын жагсаалт дээр test_cors.html-ыг баруун товшоод browser-оор нээнэ (эсвэл файлыг browser руу чирнэ). Хаягийн мөрөнд file:///C:/... гэх мэт зүйл харагдана.
Товч дарна.
Ихэнх browser-т алдаа гарна. Консол нээж (F12) харвал CORS-той холбоотой мессеж байна. file:// протокол нь ямар ч origin-д тохирдоггүй тул хаагдана.
Туршилт 2: жижиг сервер ажиллуулах
Илүү бодитой туршилт хийхийн тулд HTML файлыг сервереэс үйлчлүүлэх хэрэгтэй.
Шинэ терминал нээж (өөр цонх, таны FastAPI сервер ажиллаж байх ёстой), төслийн хавтаст:
python -m http.server 3000Энэ нь Python-ы суурь сан ашиглан 3000 порт дээр жижиг вэб сервер асаана.
Browser дээр нээнэ:
http://localhost:3000/test_cors.htmlТовч дарна.
Хэрэв ALLOWED_ORIGINS-д http://localhost:3000 байгаа бол — тэмдэглэлүүд гарч ирнэ. CORS ажиллаж байна.
Туршилт 3: CORS-ыг унтраах
Одоо .env-д origin-ыг өөрчилнө:
ALLOWED_ORIGINS=http://localhost:9999FastAPI серверээ дахин асаа (Ctrl+C, дараа нь fastapi dev main.py).
Browser-ийн хуудсаа шинэчилж, товч дарна.
Алдаа гарна. Консол дээр (F12):
Access to fetch at 'http://127.0.0.1:8000/notes' from origin 'http://localhost:3000'
has been blocked by CORS policyГэхдээ FastAPI-ийн терминалыг хараарай — тэнд хүсэлт ирсэн бичлэг байна. Сервер хүсэлтийг хүлээж авсан, хариулсан. Browser нь хариуг JavaScript-д өгөхөөс татгалзсан.
Энэ бол дээр ярьсан нарийн ялгааны бодит илрэл.
Дараа нь .env-ыг буцааж засаад серверээ дахин асаана.
Туршилт дууссаны дараа test_cors.html-ыг устгаж болно, мөн 3000 порт дээрх серверийг Ctrl+C-ээр зогсооно.
Интернэтээс ийм зөвлөгөө олж болно:
python
allow_origins=["*"]Од нь "бүх хаяг" гэсэн үг. Энэ нь бүх CORS асуудлыг шийднэ — учир нь бүгдийг зөвшөөрч байна.
Гэхдээ энэ нь ихэнхдээ буруу.
Яагаад? Учир нь та хамгаалалтыг бүрэн унтрааж байна. Дэлхийн ямар ч вэб хуудас таны API руу хандаж чадна. Хичээлийн эхэнд ярьсан банкны жишээ дахин боломжтой болно.
Мөн практик асуудал: allow_origins=["*"] болон allow_credentials=True хоёрыг хамт ашиглаж болохгүй. Browser энэ хослолыг татгалздаг. Хэрэв хоёуланг бичвэл cookie ажиллахгүй.
Хэзээ ["*"] зөв бэ? Хэрэв таны API нь бүрэн нээлттэй, олон нийтийн мэдээлэл өгдөг бол (жишээ нь цаг агаарын нээлттэй API, статистикийн API) — тэгвэл зөв. Нуух зүйл байхгүй, нэвтрэлт байхгүй.
Бусад бүх тохиолдолд тодорхой хаягуудыг жагсаа.
Тодруулъя.
CORS хэрэгтэй:
Таны API-г вэб хуудас (React, Vue, энгийн HTML) ашиглах бол.
Frontend болон API нь өөр хаяг/port дээр байвал.
CORS хэрэггүй:
Зөвхөн /docs-оор ашиглах бол.
Зөвхөн Python/requests кодоор хандах бол.
Гар утасны апп (iOS/Android) ашиглах бол — тэдгээр нь browser биш, CORS хамаарахгүй.
Frontend болон API нь ижил хаяг дээр байвал (жишээ нь FastAPI өөрөө HTML үйлчлүүлдэг бол).
Таны Тэмдэглэлийн API-д одоохондоо CORS хэрэггүй. Гэхдээ тохируулж үлдээх нь хор хөнөөлгүй бөгөөд ирээдүйд бэлэн байх болно.
Энэ хичээл нэг хаалга нээж байна. Одоо таны API-д frontend залгах боломжтой.
Тэр frontend нь юу байж болох вэ?
Энгийн HTML + JavaScript — дээрх туршилтын файл шиг. Хамгийн энгийн эхлэл.
React, Vue, Svelte — орчин үеийн frontend framework-ууд. Тэдгээр нь таны API руу fetch хийж, өгөгдлийг дэлгэцэнд харуулна.
Гар утасны апп — CORS хэрэггүй, гэхдээ ижил API ашиглана.
Өөр программ — таны API-г ямар ч программ ашиглаж чадна.
Энэ бол API-ийн жинхэнэ хүч: та нэг backend бичээд, олон төрлийн frontend түүн дээр баригдаж чадна.
Frontend хөгжүүлэлт нь энэ курсын хүрээнээс гадуур — тэр нь өөрөө том сэдэв (HTML, CSS, JavaScript, framework). Бүлэг 15-т бид түүнийг цаашдын зам болгон нэрлэнэ. Гэхдээ таны backend одоо бэлэн байна.
localhost болон 127.0.0.1 андуурах
ALLOWED_ORIGINS=http://localhost:3000Гэтэл frontend нь http://127.0.0.1:3000 дээр ажиллаж байвал — тохирохгүй. Browser тэдгээрийг өөр origin гэж үзнэ.
Засвар: хоёуланг нь оруулах:
ALLOWED_ORIGINS=http://localhost:3000,http://127.0.0.1:3000Энэ бол хамгийн түгээмэл CORS алдаа юм.
Төгсгөлийн зураас
ALLOWED_ORIGINS=http://localhost:3000/Төгсгөлийн зураастай. Origin-д зураас байх ёсгүй — тохирохгүй болно.
Засвар: зураасыг хасах.
Middleware-ыг router-ийн дараа нэмэх
python
app.include_router(notes.router)
app.add_middleware(CORSMiddleware, ...) # дараа ньЭнэ нь ихэвчлэн ажилладаг, гэхдээ найдвартай биш. Middleware-ыг router-ээс өмнө нэмэх нь зөв дараалал.
Серверийг дахин асаахгүй
.env-ийн ALLOWED_ORIGINS-ыг өөрчлөөд серверийг дахин асаахгүй бол өөрчлөлт хэрэгжихгүй. Бүлэг 9-ийн сануулга: .env өөрчлөвөл сервер дахин асаа.
CORS-ыг серверийн алдаа гэж бодох
CORS алдаа гарахад "миний API эвдэрсэн" гэж бодох нь түгээмэл. Үгүй — API ажиллаж байна, browser зогсоож байна.
Батлах арга: ижил хаяг руу /docs-оос эсвэл requests-ээр хандаж үзэх. Хэрэв тэнд ажиллаж байвал асуудал CORS-д байна, API-д биш.
allow_headers-ыг хязгаарлаад X-API-Key мартах
python
allow_headers=["Content-Type"] # X-API-Key байхгүйТаны API key header зөвшөөрөгдөөгүй тул бичих үйлдэл ажиллахгүй. Preflight хүсэлт татгалзана.
Засвар: ["*"] ашиглах, эсвэл ["Content-Type", "X-API-Key"] гэж тодорхой бичих.
Хүсвэл дээрх test_cors.html туршилтыг бүрэн хийж үзээрэй — origin зөвшөөрөгдсөн болон зөвшөөрөгдөөгүй хоёр тохиолдлыг. CORS-ыг өөрийн нүдээр харах нь ирээдүйд тэр алдаатай тааралдахад таныг бэлтгэнэ.
Сонирхвол allow_origins=["*"] тохируулаад, file:// протоколоор HTML файлыг нээж үзээрэй. Ихэнх тохиолдолд ажиллах болно — гэхдээ энэ нь яагаад аюултайг санаж, буцааж засаарай.
CORS бол зөвхөн browser-ийн дүрэм — requests, гар утасны апп, /docs-д хамаарахгүй.
Same-origin policy: нэг origin дээрх хуудас өөр origin руу чөлөөтэй хандаж болохгүй.
Origin = protocol + domain + port. Гурвуулаа таарах ёстой. localhost ≠ 127.0.0.1.
Энэ дүрэм нь хамгаалалт — хортой сайт таны нэвтэрсэн банк руу хүсэлт илгээхээс сэргийлдэг.
Сервер Access-Control-Allow-Origin header буцаах ёстой; browser түүнийг шалгана.
Нарийн ялгаа: API нь хүсэлтийг хүлээж авдаг; browser нь хариуг JavaScript-д өгөхөөс татгалздаг.
Зарим хүсэлтэд preflight (OPTIONS) явдаг — CORSMiddleware автоматаар зохицуулна.
Шийдэл: app.add_middleware(CORSMiddleware, ...) — allow_origins, allow_credentials, allow_methods, allow_headers.
Origin-ыг .env-д тавь — орчин бүрд өөр байна.
allow_origins=["*"] нь ихэнхдээ буруу — зөвхөн бүрэн нээлттэй API-д. allow_credentials=True-тэй хамт ажиллахгүй.
Хамгийн түгээмэл алдаа: localhost болон 127.0.0.1 андуурах.
Одоо таны API-д frontend залгах хаалга нээлттэй боллоо. Дараагийн хичээлд бид Бүлэг 8-аас эхэлсэн нэг ажлыг дуусгана: pagination-ыг зөв хийх. Одоогийн skip/limit нь ажиллаж байгаа боловч хэрэглэгчид "нийт хэдэн тэмдэглэл байна вэ, би хэддүгээр хуудсанд байна вэ" гэдгийг мэдэх арга байхгүй. Бид түүнийг засаж, мэргэжлийн API-ууд хэрхэн pagination хийдгийг үзнэ. Тэр нь Бүлэг 13-ыг төгсгөж, бид capstone руу орно.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.