Ачааллж байна...
Өмнөх хичээлийн төгсгөлд бид нэг дутагдлыг тэмдэглэсэн: таны бүх endpoint тогтмол хариу буцаадаг. /books руу хэн ханддаг ч ижил гурван ном ирнэ. Гэхдээ бодит API ийм биш. Хэрэглэгч "гурав дахь номыг өгөөч" гэж хэлж чаддаг байх ёстой, тэгвэл серверт "аль номыг гэж" гэдгийг ялгаж таних арга хэрэгтэй.
Тэр арга бол path parameter юм. Энэ бол хаягны нэг хэсгийг хувьсагч болгож, түүнд юу ч тавьж болдог болгох арга. Энэ хичээлд бид түүнийг сурч, дараа нь энэ сургалтын хамгийн онцлох мөчүүдийн нэгтэй уулзана: хэрэглэгч буруу төрлийн утга илгээхэд FastAPI автоматаар татгалзаж, дэлгэрэнгүй алдааны JSON буцаана. Бид тэр JSON-ыг мөр мөрөөр нь уншина.
Танд гурван ном байна гэж бодъё. Хэрэглэгч тус бүрийн дэлгэрэнгүйг үзэхийг хүсэж байна. Гэнэн шийдэл нь ном бүрт нэг endpoint бичих:
python
@app.get("/books/1")
def get_book_one():
return {"id": 1, "title": "Монголын нууц товчоо"}
@app.get("/books/2")
def get_book_two():
return {"id": 2, "title": "Цог хийморь"}Энэ ажиллана. Гэхдээ мянган номтой болбол? Мянган функц бичих үү? Мэдээж үгүй. Асуудал нь: зам нь тогтмол текст байх албагүй, хувьсагч байж болно.
Зоогийн газрын цэсэн дээр хоол бүр дугаартай байдаг: 1-р хоол, 2-р хоол, 17-р хоол. Зочин "17-р хоол авъя" гэхэд зөөгч ойлгодог. Зөөгчид "1-р хоолыг ойлгох арга", "2-р хоолыг ойлгох арга" гэсэн тусдаа чадвар хэрэггүй. Түүнд ганц дүрэм л хэрэгтэй: "n-р хоол" гэсэн захиалгыг ойлго, тэр n-ийг гал тогоо руу дамжуул.
Path parameter бол яг тэр. Та FastAPI-д "/books/ дараа ямар ч утга ирж болно, түүнийг барьж аваад надад дамжуул" гэж хэлдэг.
main.py файлаа дараах кодоор солино:
python
# main.py
from fastapi import FastAPI
app = FastAPI(title="Номын API")
books = {
1: {"id": 1, "title": "Монголын нууц товчоо", "year": 1240},
2: {"id": 2, "title": "Цог хийморь", "year": 1962},
3: {"id": 3, "title": "Цагаан хэрэм", "year": 1971},
}
@app.get("/books")
def get_books():
return list(books.values())
@app.get("/books/{book_id}")
def get_book(book_id: int):
return books[book_id]Хадгална. Reload болсныг терминал дээр шалгана.
Кодын задаргаа
Эхлээд books бол энгийн Python dictionary — түлхүүр нь тоо, утга нь номын мэдээлэл агуулсан өөр dictionary. Курс 2-ын Бүлэг 5-д та үүнтэй адил үүрлэсэн бүтэцтэй ажиллаж байсан. Одоохондоо энэ бол зөвхөн санах ойд байгаа өгөгдөл — серверээ унтраахад алга болно. Түвшин 4-т бид үүнийг өгөгдлийн сангаар солино.
Дараа нь хамгийн чухал мөр:
python
@app.get("/books/{book_id}")Дөрвөлжин хаалт {} доторх нэр бол path parameter. Энэ мөр FastAPI-д ингэж хэлж байна: "/books/ гэсний дараа ямар ч зүйл ирж болно. Тэр зүйлийг барьж аваад book_id гэж нэрлэ."
Дараагийн мөр:
python
def get_book(book_id: int):Хоёр зүйлийг анзаараарай. Нэгд, функцийн параметрийн нэр (book_id) нь дөрвөлжин хаалт доторх нэртэй яг таарч байна. Энэ бол заавал — FastAPI нэрээр нь холбож өгдөг. Хоёрт, параметр дээр type hint байна: book_id: int.
Курс 2-ын Бүлэг 4-т та type hint сурахдаа "энэ бол зөвхөн уншихад тусалдаг шошго, Python түүнийг албадан хэрэгжүүлдэггүй" гэж сурсан. Тэр Python-ы хувьд үнэн хэвээр байна. Гэхдээ FastAPI-д type hint бол зөвхөн шошго биш — энэ бол заавар юм. Энэ хичээлийн үлдсэн хэсэг үүнийг харуулна.
Зөв хүсэлт
Browser дээр:
http://127.0.0.1:8000/books/2Хариу:
json
{"id":2,"title":"Цог хийморь","year":1962}Юу болсныг алхам алхмаар харъя. Хаягны 2 гэсэн хэсэг барьж авагдсан. Тэр үед 2 бол текст байсан — хаяг бол текст, түүнд өөр төрөл байдаггүй. Гэхдээ таны функц int гэж зарласан тул FastAPI тэр текстийг тоо болгож хувиргасан. Дараа нь функцийг book_id=2 гэсэн утгатайгаар дуудсан. Функц books[2] буюу хоёр дахь номыг буцаасан.
Энэ хувиргалт нь чухал. Хэрэв type hint байхгүй бол book_id нь "2" (текст) байх байсан, тэгвэл books["2"] нь алдаа өгөх байсан, учир нь books dictionary-ийн түлхүүрүүд нь тоо. FastAPI танд ажлыг тань хийж өгсөн.
Өөр зөв хүсэлт
http://127.0.0.1:8000/books/3Хариу:
json
{"id":3,"title":"Цагаан хэрэм","year":1971}Ижил функц, өөр утга, өөр хариу. Нэг мөр код гурван номд (эсвэл мянган номд) үйлчилж байна.
Энэ бол хичээлийн зүрх. Browser дээрээ дараах хаягийг бичнэ:
http://127.0.0.1:8000/books/abcabc бол тоо биш. Таны функц int шаардаж байгаа. Юу болох вэ?
Хариу:
json
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"book_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "abc"
}
]
}Мөн browser-ийн хөгжүүлэгчийн хэрэгсэл дээр статус код нь 422 гэж харагдана. /docs дээр туршвал энэ тоо тод харагдана.
Хамгийн чухал зүйл: таны функц огт дуудагдаагүй. print() тавьсан ч гарахгүй байсан. FastAPI хүсэлтийг хаалган дээр нь зогсоож, дотогш оруулаагүй. Таны код гар хүрээгүй, эвдрээгүй, санаа зовоогүй.
Одоо энэ JSON-ыг эхнээс нь дуустал уншиж, талбар бүр юу хэлж байгааг ойлгоё. Энэ хариуг та сургалтын турш маш олон удаа харах болно, тиймээс нэг удаа сайн ойлговол цаашид хэзээ ч төөрөхгүй.
detail
json
"detail": [ ... ]Гадна талын түлхүүр нь detail, түүний утга нь жагсаалт (дөрвөлжин хаалт). Яагаад жагсаалт гэж? Учир нь нэг хүсэлтэд олон алдаа зэрэг гарч болно. Жишээ нь Бүлэг 4-т та олон талбартай маягт илгээх бөгөөд гурван талбар нэгэн зэрэг буруу байж болно. Тэр тохиолдолд энэ жагсаалт гурван элементтэй байх болно.
Одоохондоо ганц алдаа байгаа тул жагсаалтад ганц элемент байна.
type
json
"type": "int_parsing"Энэ бол алдааны төрөл, машинд зориулсан код. int_parsing гэдэг нь "бүхэл тоо болгож задлах гэж оролдоод чадсангүй" гэсэн утгатай. Хожим та missing (талбар дутуу), string_type (текст байх ёстой), greater_than (хэт бага тоо) гэх мэт бусад төрлүүдийг харна.
Энэ талбар нь хүнд зориулагдаагүй — энэ нь программд зориулагдсан. Хэрэв та энэ API-г ашиглаж байгаа программ бичиж байсан бол type талбарыг хараад алдааны төрлийг таньж, түүнд тохирсон үйлдэл хийж болно.
loc
json
"loc": ["path", "book_id"]loc бол location — алдаа хаана гарсныг заана. Энэ нь жагсаалт бөгөөд ерөнхийгөөс тодорхой руу явна.
Эхний элемент "path" — алдаа хаягны зам дээр гарсан гэсэн үг. Хожим та "query" (хайлтын параметр дээр), "body" (илгээсэн өгөгдөл дээр), "header" (толгой хэсэг дээр) гэсэн утгуудыг харна.
Хоёр дахь элемент "book_id" — яг аль параметр дээр гарсныг заана.
Хамтдаа уншвал: "алдаа нь хаягны зам дээрх book_id гэдэг параметр дээр гарсан." Маш тодорхой. Олон талбартай маягт илгээхэд энэ loc талбар яг аль талбар буруу байсныг хэлж өгдөг тул үнэлж баршгүй ач холбогдолтой.
msg
json
"msg": "Input should be a valid integer, unable to parse string as an integer"Энэ бол хүнд зориулсан тайлбар. Монголоор: "Оролт нь хүчинтэй бүхэл тоо байх ёстой; текстийг бүхэл тоо болгож задлах боломжгүй байна."
Энэ өгүүлбэрийг хоёр хэсэгт хуваан унших нь тустай. Эхний хэсэг (Input should be a valid integer) нь юу байх ёстойг хэлж байна. Хоёр дахь хэсэг (unable to parse string as an integer) нь яагаад болоогүйг хэлж байна: ирсэн зүйл нь текст байсан бөгөөд түүнийг тоо болгож чадаагүй.
Ийм алдааны мессежийг та шууд хэрэглэгчид харуулж болно. Энэ бол FastAPI-ийн өгсөн бэлэг: та алдааны мессеж бичих шаардлагагүй, тэд аль хэдийн бэлэн, тодорхой, эелдэг.
input
json
"input": "abc"Энэ бол бодит байдал дээр ирсэн утга. Хэрэглэгч юу илгээснийг яг харуулж байна.
Энэ талбар нь debug хийхэд асар их тустай. Заримдаа та "би зөв утга илгээсэн шүү дээ" гэж бодох боловч input талбар нь өөр зүйл харуулдаг — жишээ нь нэмэлт хоосон зай, буруу тэмдэгт, эсвэл огт өөр утга. Энэ талбар худал ярьдаггүй.
Заримдаа FastAPI/Pydantic хувилбараас хамааран нэмэлт url талбар харагдаж болно — тэр нь Pydantic-ийн онлайн тайлбар руу заасан холбоос юм. Байх ч болно, байхгүй ч болно; аль ч тохиолдолд бусад дөрвөн талбар нь чухал.
Энэ JSON нэг өгүүлбэрээр ингэж хэлж байна:
"Хаягны зам дээрх
book_idгэдэг параметртabcгэсэн утга ирсэн. Гэхдээ тэнд бүхэл тоо байх ёстой. Текстийг тоо болгож задалж чадсангүй."
Та энэ мессежийг бичээгүй. Та зөвхөн book_id: int гэж бичсэн. Курс 2-ын Бүлэг 4-т "type hint зөвхөн уншихад тусална" гэж сурч байсан зүйл энд гэнэт ажлын хэрэгсэл болж хувирлаа. FastAPI таны type hint-ийг уншиж, түүнээс шалгалт үүсгэж, түүнээс алдааны мессеж үүсгэж, түүнээс /docs дээрх баримт бичиг үүсгэсэн.
Энэ бол FastAPI гэдэг framework-ийн үндсэн санаа: та Python-оо сайн бичихэд бусад нь өөрөө болно.
Курс 2-ын Бүлэг 11-т та status code-той танилцсан: 200 бол амжилт, 404 бол олдсонгүй, 500 бол серверийн алдаа. Одоо шинэ тоо гарч ирлээ: 422.
422 нь ойролцоогоор ингэж орчуулагдана: "Таны хүсэлтийн бүтэц зөв боловч агуулга нь боловсруулах боломжгүй байна." Өөрөөр хэлбэл, хүсэлт нь ойлгомжтой ирсэн, гэхдээ дотор нь буруу өгөгдөл байна.
Энэ бол хэрэглэгчийн алдаа, серверийн алдаа биш. 4-өөр эхэлсэн бүх код (400, 401, 404, 422) нь "та буруу зүйл хийлээ" гэсэн утгатай. 5-аар эхэлсэн код (500) нь "би буруу зүйл хийлээ" гэсэн утгатай. Энэ ялгаа нь чухал: 422 харах нь таны код эвдэрсэн гэсэн үг биш, харин ч эсрэгээрээ — таны код зөв ажиллаж, буруу өгөгдлийг барьж авсан гэсэн үг юм.
/docs хуудсаа нээнэ. GET /books/{book_id} мөр дээр товшино.
Одоо Parameters хэсэг хоосон биш байна. Тэнд ийм зүйл харагдана:
book_id * required
integer
(path)Гурван мөрийг задалъя. book_id бол нэр. Од (*) болон required нь заавал өгөх ёстойг заана. integer бол төрөл — таны type hint-ээс шууд ирсэн. (path) нь хаанаас ирэхийг заана.
Доор нь оруулах талбар байна. Try it out дарж, талбарт 2 бичээд Execute дарна уу. Хариу 200 статустай, номын JSON гарч ирнэ.
Одоо ижил талбарт abc бичээд Execute дарна уу. Хоёр зүйл болно. Нэгд, Swagger UI өөрөө улаанаар "тоо оруулна уу" гэж анхааруулж магадгүй — учир нь энэ талбар integer гэдгийг мэдэж байгаа. Хэрэв тэгсэн бол энэ нь browser дээрх шалгалт, серверт хүрээгүй. Хоёрт, хэрэв та түүнийг тойрч (эсвэл хаягийн мөрөөр шууд) илгээвэл дээр үзсэн 422 хариу ирнэ — энэ нь сервер дээрх шалгалт.
Хоёр шалгалт өөр өөр газар болж байгааг ялгах нь чухал. Browser дээрх шалгалтад хэзээ ч найдаж болохгүй, учир нь хэн ч түүнийг тойрч гарч чадна. Сервер дээрх шалгалт л жинхэнэ хамгаалалт юм. Таны API-д хандаж байгаа хүн browser ашиглаж байгаа гэсэн баталгаа байхгүй — тэр requests кодоор, terminal-аас, эсвэл өөр программаас хандаж болно. FastAPI-ийн validation нь тэр бүх тохиолдолд ажиллана.
Path parameter заавал тоо байх албагүй. Дараах endpoint-ийг нэмнэ:
python
# main.py (нэмэлт хэсэг)
@app.get("/authors/{name}")
def get_author(name: str):
return {"author": name, "message": f"{name} гэдэг зохиолчийн хуудас"}Хүсэлт:
http://127.0.0.1:8000/authors/ЧойномХариу:
json
{"author":"Чойном","message":"Чойном гэдэг зохиолчийн хуудас"}Энд name: str гэж зарласан тул ямар ч текст хүлээн авагдана — кирилл үсэг ч багтана. Текст параметрт бараг бүх зүйл тохирдог тул 422 алдаа ховор гардаг.
Гэхдээ нэг зүйлийг анзаараарай: /authors/123 гэж илгээвэл ямар ч алдаа гарахгүй, name нь "123" гэсэн текст болно. str бол өргөн хаалга.
Нэг зам дээр олон параметр байж болно:
python
# main.py (нэмэлт хэсэг)
@app.get("/books/{book_id}/chapters/{chapter_number}")
def get_chapter(book_id: int, chapter_number: int):
return {
"book_id": book_id,
"chapter": chapter_number,
"message": f"{book_id}-р номын {chapter_number}-р бүлэг",
}Хүсэлт:
http://127.0.0.1:8000/books/2/chapters/5Хариу:
json
{"book_id":2,"chapter":5,"message":"2-р номын 5-р бүлэг"}Хоёр утга барьж авагдаж, хоёулаа тоо болж хувирч, хоёулаа функцэд дамжсан. Дүрэм ижил хэвээр: дөрвөлжин хаалт доторх нэр функцийн параметрийн нэртэй таарах ёстой.
Нэр таарахгүй байна
python
@app.get("/books/{book_id}")
def get_book(id: int):
return books[id]Терминал дээр:
fastapi.exceptions.FastAPIError: Invalid args for response field! Hint: check that <class 'int'> is a valid Pydantic field type.Эсвэл хувилбараас хамааран өөр мессеж гарч болно, гэхдээ утга нь ижил: {book_id} гэж бичсэн атлаа функц id гэдэг параметр хүлээж байна. Хоёр нэр яг таарах ёстой. {book_id} -> book_id: int.
Байхгүй ном хайх
http://127.0.0.1:8000/books/99Терминал дээр улаан traceback гарна:
KeyError: 99Browser дээр:
json
{"detail":"Internal Server Error"}Статус нь 500.
Юу болов? 99 нь тоо тул validation амжилттай өнгөрсөн. Функц дуудагдсан. Функц дотор books[99] гэж хайсан — гэхдээ тийм түлхүүр байхгүй. Python KeyError шидсэн. FastAPI түүнийг барьж чадаагүй тул 500 буцаасан.
Энэ бол муу хариу. Хэрэглэгч "серверт ямар нэг зүйл эвдэрсэн" гэж ойлгоно, гэтэл үнэн хэрэгтээ тэр зүгээр л байхгүй номыг хайсан. Зөв хариу нь 404 байх ёстой: "тийм ном байхгүй".
Одоохондоо үүнийг засахгүй. Энэ бол Бүлэг 3-ын Хичээл 3-ын яг сэдэв бөгөөд тэнд бид HTTPException ашиглан үүнийг зөв болгоно. Одоохондоо энэ дутагдлыг мэдэж байгаад орхиё — түүнийг мэдэж байх нь өөрөө сурах явц юм.
ModuleNotFoundError
Сануулга: терминалын мөрийн эхэнд (venv) харагдаж байна уу? Хэрэв ModuleNotFoundError гарвал — venv идэвхтэй эсэхээ шалгаарай.
Хүсвэл book_id-гийн type hint-ийг str болгож үзээрэй:
python
@app.get("/books/{book_id}")
def get_book(book_id: str):
return {"received": book_id, "type": str(type(book_id))}Дараа нь /books/abc руу хандаж үзээрэй. Одоо 422 гарахгүй — abc бол хүчинтэй текст. Хариу нь ирсэн утга болон түүний төрлийг харуулна. Энэ туршилт нь type hint яг юуг хянаж байгааг тодорхой болгоно.
Сонирхвол float төрөл ч туршиж болно. /books/2.5 гэсэн хаяг book_id: float дээр ажиллах бөгөөд book_id: int дээр 422 өгнө.
Path parameter — хаягны хувьсах хэсэг: @app.get("/books/{book_id}").
Дөрвөлжин хаалт доторх нэр функцийн параметрийн нэртэй яг таарах ёстой.
Параметр дээрх type hint нь FastAPI-д заавар болдог: хувиргалт хийж, шалгалт хийж, баримт бичиг үүсгэдэг.
Буруу төрөл ирвэл функц огт дуудагдахгүй; FastAPI 422 статустай дэлгэрэнгүй JSON буцаана.
422 хариуны талбарууд: type (машинд), loc (хаана), msg (хүнд), input (юу ирсэн).
4-өөр эхэлсэн статус = хэрэглэгчийн алдаа. 5-аар эхэлсэн = серверийн алдаа.
Одоохондоо байхгүй ном хайвал 500 гарна — энэ бол засах ёстой дутагдал (Бүлэг 3).
Одоо та хаягны замаас утга авч чадна. Гэхдээ бүх параметрийг замд байрлуулах нь тохиромжгүй үе бий: хайлтын үг, эрэмбэлэх дараалал, хуудасны дугаар зэрэг нэмэлт, заавал биш утгуудыг замд шахах нь хаягийг гаргүй болгодог. Тэдгээрт зориулсан өөр механизм байдаг. Дараагийн хичээлд бид query parameter — асуултын тэмдгийн ард ирдэг тэр утгуудыг сурна.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.