Ачааллж байна...
Та одоо хоёр төрлийн параметрийг тусад нь мэднэ. Path parameter нь аль гэдгийг заадаг, заавал байх ёстой, замын бүтцийн хэсэг байдаг. Query parameter нь хэрхэн гэдгийг заадаг, ихэвчлэн заавал биш, асуултын тэмдгийн ард ирдэг.
Бодит API-д тэдгээр нь бараг үргэлж хамтдаа ажилладаг. Энэ хичээлд бид тэднийг хослуулах бөгөөд дараа нь нэг далд зангатай уулзана. Тэр занга нь код бичиж эхлэх бүрт олон хүнийг гайхшруулдаг бөгөөд хамгийн муу нь — алдааны мессеж өгдөггүй. Бүх зүйл ажиллаж байгаа мэт харагдана, зөвхөн буруу хариу ирдэг. Ийм алдаа хамгийн аюултай нь юм, тиймээс бид түүнийг зориудаар үүсгэж, дараа нь засна.
Сайн мэдээ: тусгай дүрэм байхгүй. FastAPI-ийн дүрэм өмнөх шигээ хэвээр — параметрийн нэр замын дөрвөлжин хаалтад байвал path, байхгүй бол query. Та зүгээр л функцийн параметрийн жагсаалтдаа хоёуланг нь бичнэ, FastAPI өөрөө ялгана.
main.py файлаа дараах кодоор солино:
python
# main.py
from fastapi import FastAPI
app = FastAPI(title="Номын API")
books = [
{"id": 1, "title": "Монголын нууц товчоо", "year": 1240, "author": "Тодорхойгүй"},
{"id": 2, "title": "Цог хийморь", "year": 1962, "author": "Ч. Лодойдамба"},
{"id": 3, "title": "Цагаан хэрэм", "year": 1971, "author": "С. Эрдэнэ"},
{"id": 4, "title": "Хөх судар", "year": 1971, "author": "Б. Ринчен"},
{"id": 5, "title": "Тунгалаг тамир", "year": 1962, "author": "Ч. Лодойдамба"},
]
@app.get("/books")
def get_books(limit: int = 10):
return books[:limit]
@app.get("/books/{book_id}")
def get_book(book_id: int, full: bool = False):
for book in books:
if book["id"] == book_id:
if full:
return book
return {"id": book["id"], "title": book["title"]}
return {"error": "Ном олдсонгүй"}Хадгална.
Кодын задаргаа
python
def get_book(book_id: int, full: bool = False):Хоёр параметр байна. book_id нь замд {book_id} гэж байгаа тул path parameter. full нь замд байхгүй тул query parameter. Түүнд default утга (False) байгаа тул заавал биш.
Функцийн логик энгийн: жагсаалтаас тохирох id-тай номыг хайна. Олдвол full тохируулгаас хамаарч эсвэл бүх мэдээллийг, эсвэл зөвхөн id болон гарчгийг буцаана.
Төгсгөлийн return {"error": "Ном олдсонгүй"} бол түр зуурын шийдэл. Өмнөх хичээлд бид KeyError -> 500 гэсэн муу зан төлөвийг харсан; энэ нь түүнээс арай дээр боловч бас л зөв биш, учир нь статус нь 200 буцна — өөрөөр хэлбэл "бүх зүйл сайн" гэж хэлээд алдаа өгч байна. Бүлэг 3-т HTTPException ашиглан үүнийг зөв 404 болгоно.
Query параметргүй
http://127.0.0.1:8000/books/3Хариу:
json
{"id":3,"title":"Цагаан хэрэм"}full өгөгдөөгүй тул default (False) хэрэгжсэн, зөвхөн товч мэдээлэл ирлээ.
Query параметртэй
http://127.0.0.1:8000/books/3?full=trueХариу:
json
{"id":3,"title":"Цагаан хэрэм","year":1971,"author":"С. Эрдэнэ"}Бүх мэдээлэл ирлээ.
Хаягны бүтцийг сайтар ажиглана уу: /books/3?full=true. Замын хэсэг (/books/3) асуултын тэмдгээс өмнө байна, query хэсэг (full=true) хойно байна. Энэ дараалал заавал — асуултын тэмдэг үргэлж замын төгсгөлийг заана.
Хоёулаа буруу байвал
http://127.0.0.1:8000/books/abc?full=magadguiХариу (статус 422):
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"
},
{
"type": "bool_parsing",
"loc": [
"query",
"full"
],
"msg": "Input should be a valid boolean, unable to interpret input",
"input": "magadgui"
}
]
}Энд чухал зүйл болж байна. Хоёр дахь хичээлд би "detail нь жагсаалт, учир нь олон алдаа зэрэг гарч болно" гэж хэлсэн. Одоо та түүнийг бодитоор харж байна.
Жагсаалтад хоёр элемент байна. Эхнийх нь loc: ["path", "book_id"] — замын алдаа. Хоёр дахь нь loc: ["query", "full"] — query-ийн алдаа. FastAPI эхний алдаан дээр зогсоогүй; бүх параметрийг шалгаж, олсон бүх алдаагаа нэг дор мэдээлсэн.
Энэ зан төлөв нь маш ашигтай. Хэрэв FastAPI эхний алдаа дээр зогсдог байсан бол хэрэглэгч алдаагаа нэг нэгээр нь засаж, дахин дахин илгээх шаардлагатай болно. Одоо тэр бүх алдаагаа нэг дор хараад нэг удаад засна. Бүлэг 4-т та олон талбартай маягт илгээх бөгөөд тэнд энэ зан төлөв илүү үнэ цэнэтэй болно.
/docs хуудсаа нээж, GET /books/{book_id} дээр товшино.
Parameters хэсэгт хоёр мөр харагдана:
book_id * integer (path)
full boolean (query) Default: falseЯлгааг тодорхой харж болно. book_id дээр од (*) байна — заавал. Хажууд нь (path) гэж бичсэн. full дээр од байхгүй, (query) гэж бичсэн, default утга харагдаж байна.
Та ямар ч тайлбар бичээгүй. FastAPI таны кодны бүтцээс энэ бүхнийг уншсан.
Дараах endpoint-ийг нэмнэ. Зорилго нь: "хамгийн сүүлийн үеийн ном" гэсэн тусгай хаяг үүсгэх.
python
# main.py (файлын төгсгөлд нэмнэ)
@app.get("/books/latest")
def get_latest_book():
return max(books, key=lambda b: b["year"])Логик нь энгийн: max() функц year талбараар хамгийн их утгатай номыг олно. Курс 2-ын Бүлэг 3-ын lambda дахин ажиллаж байна.
Хадгална. Одоо энэ хаяг руу хандъя:
http://127.0.0.1:8000/books/latestХариу (статус 422):
json
{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"book_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "latest"
}
]
}Хачирхалтай. Та /books/latest гэсэн endpoint бичсэн. Сервер reload болсон. /docs дээр ч харагдаж байгаа. Гэтэл түүн рүү хандахад 422 ирж, book_id гэдэг параметрийн тухай ярьж байна — таны шинэ функцэд тийм параметр байхгүй байхад.
Юу болов?
Бүлэг 2-ын эхний хичээлд би нэг зүйлийг хэлсэн: FastAPI бүртгэлийн дэвтрээ дээрээс доош уншиж, эхний тохирлыг олоод зогсдог.
Таны файлын дараалал ийм байна:
python
@app.get("/books") # 1-р бүртгэл
@app.get("/books/{book_id}") # 2-р бүртгэл
@app.get("/books/latest") # 3-р бүртгэл/books/latest гэсэн хүсэлт ирэхэд FastAPI дэвтрээ уншина.
Эхний мөр: /books — таарахгүй, зам илүү урт.
Хоёр дахь мөр: /books/{book_id} — таарч байна! Учир нь {book_id} гэдэг нь "энд ямар ч зүйл байж болно" гэсэн үг. latest бол ямар нэг зүйл. Тиймээс FastAPI "олдлоо" гэж шийдээд зогсоно.
Гурав дахь мөр хүртэл хэзээ ч хүрэхгүй.
Дараа нь FastAPI latest гэсэн текстийг int болгож хувиргах гэж оролдоод чадахгүй, 422 буцаана. Таны get_latest_book функц огт дуудагдаагүй. Тэр код амьдралдаа нэг ч удаа ажиллахгүй.
Зөөгчид гурван дүрэм өгсөн гэж бодъё:
Зочин "цэс" гэвэл цэс өг.
Зочин ямар нэг тоо хэлвэл тэр дугаартай хоолыг авчир.
Зочин "өнөөдрийн онцгой хоол" гэвэл онцгой хоолыг авчир.
Зочин "өнөөдрийн онцгой хоол" гэж хэлэв. Зөөгч дүрмээ дээрээс доош уншина. Эхний дүрэм таарахгүй. Хоёр дахь дүрэм... "ямар нэг тоо" гэсэн байна. Гэхдээ зөөгч маань хэт үг дуулгавартай бөгөөд "өнөөдрийн онцгой хоол" гэдгийг тоо гэж үзэж, түүнийг тоо болгож хувиргах гэж оролдож, бүтэлгүйтээд зогсоно.
Гурав дахь дүрэм — яг зөв дүрэм — хэзээ ч уншигдахгүй.
Асуудал нь дүрмийн дараалал юм. Хамгийн өргөн дүрэм хамгийн доор байх ёстой.
Шийдэл нь маш энгийн: тусгай зам ерөнхий замын өмнө байх ёстой.
main.py файлаа дараах кодоор солино — зөвхөн endpoint-үүдийн дараалал өөрчлөгдсөн:
python
# main.py
from fastapi import FastAPI
app = FastAPI(title="Номын API")
books = [
{"id": 1, "title": "Монголын нууц товчоо", "year": 1240, "author": "Тодорхойгүй"},
{"id": 2, "title": "Цог хийморь", "year": 1962, "author": "Ч. Лодойдамба"},
{"id": 3, "title": "Цагаан хэрэм", "year": 1971, "author": "С. Эрдэнэ"},
{"id": 4, "title": "Хөх судар", "year": 1971, "author": "Б. Ринчен"},
{"id": 5, "title": "Тунгалаг тамир", "year": 1962, "author": "Ч. Лодойдамба"},
]
@app.get("/books")
def get_books(limit: int = 10):
return books[:limit]
@app.get("/books/latest")
def get_latest_book():
return max(books, key=lambda b: b["year"])
@app.get("/books/{book_id}")
def get_book(book_id: int, full: bool = False):
for book in books:
if book["id"] == book_id:
if full:
return book
return {"id": book["id"], "title": book["title"]}
return {"error": "Ном олдсонгүй"}/books/latest одоо /books/{book_id}-аас дээр байна.
Хадгална.
Дахин турших
http://127.0.0.1:8000/books/latestХариу:
json
{"id":3,"title":"Цагаан хэрэм","year":1971,"author":"С. Эрдэнэ"}Ажиллалаа. (Гурав дахь ном 1971 онтой, дөрөв дэх нь ч 1971 онтой — max() эхнийхийг сонгодог. Энэ бол алдаа биш, зүгээр л max()-ийн зан төлөв.)
Одоо хуучин хаягууд эвдэрсэн эсэхийг шалгая:
http://127.0.0.1:8000/books/2Хариу:
json
{"id":2,"title":"Цог хийморь"}Ажиллаж байна. FastAPI дэвтрээ уншихдаа /books/latest мөрийг эхэлж харна — 2 нь latest биш тул таарахгүй. Дараагийн мөр /books/{book_id} — таарна. Зөв функц дуудагдана.
Дараалал одоо зөв тул хоёулаа ажиллаж байна.
Энэ зангаас гарах ерөнхий дүрэм:
Тогтмол зам (/books/latest) нь хувьсагчтай зам (/books/{book_id})-аас ӨМНӨ бичигдэх ёстой.
Хувьсагчтай зам бол "ямар ч зүйл" гэсэн утгатай тул тэр нь тогтмол замыг залгидаг. Тогтмол зам нь илүү нарийн, илүү тодорхой — тиймээс түүнд эхлээд боломж өгөх ёстой.
Практикт үүнийг хэрхэн санах вэ: ерөнхийгөөс тодорхой руу биш, тодорхойгоос ерөнхий рүү бич. Файл дээрээс доош унших тусам замууд улам "өргөн" болж байх ёстой.
Энэ дүрэм нь FastAPI-д зөвхөн хамаарахгүй — Flask, Django, Express, бараг бүх вэб framework-д ижил асуудал байдаг. Нэг удаа ойлговол насаараа ашиглана.
Шударга асуулт. FastAPI ухаалаг байж, "latest гэдэг нь тоо биш тул тодорхой замыг эхэлж шалгая" гэж бодож болох байсан.
Гэхдээ энэ нь өөр асуудал үүсгэнэ: урьдчилан таамаглах боломжгүй байдал. Хэрэв framework өөрөө дарааллыг өөрчилдөг байсан бол таны кодыг уншиж байгаа хүн ямар дараалал бодитоор ажиллахыг мэдэхгүй болно. Одоогийн зан төлөв нь "дээрээс доош, эхний тохирол" — энгийн, урьдчилан таамаглахуйц, тайлбарлахад хялбар.
Ил тод байдал нь ухаалаг байдлаас илүү үнэ цэнэтэй байдаг. Энэ бол сайн framework-ийн зарчим бөгөөд та цаашид олон удаа тааралдана.
Занга дахин: тусгай зам доор байна
Энэ бол дээр үзсэн алдаа, гэхдээ бодит төсөлд өөр хэлбэрээр гарч ирдэг:
python
@app.get("/users/{user_id}")
def get_user(user_id: int):
...
@app.get("/users/me")
def get_current_user():
.../users/me — өөрийн профайл харах хаяг. Маш түгээмэл загвар. Гэхдээ энд доор байгаа тул хэзээ ч ажиллахгүй: me гэдэг текстийг user_id: int болгож хувиргах гэж оролдоод 422 гарна.
Засвар: /users/me-г дээш зөөнө.
Энэ алдаа таныг гайхшруулах магадлал өндөр, учир нь алдааны мессеж таныг буруу зүг рүү чиглүүлдэг. Та user_id-гийн тухай 422 харна — гэтэл асуудал user_id-д огт биш, дараалалд байна. Тиймээс дүрмийг санаж байх нь чухал: сонин 422 харвал эхлээд дарааллаа шалга.
Хоёр тогтмол зам ижил
python
@app.get("/books/latest")
def get_latest():
return {"a": 1}
@app.get("/books/latest")
def get_newest():
return {"b": 2}Алдаа гарахгүй, гэхдээ зөвхөн эхнийх нь ажиллана. Хоёр дахь нь үхсэн код. Энэ бол Бүлэг 2-ын эхний хичээлд дурдсан зан төлөвийн бас нэг илрэл.
Query parameter-ийг замд бичих
python
@app.get("/books/{limit}")
def get_books(limit: int = 10):
return books[:limit]Энэ ажиллана, гэхдээ limit одоо path parameter болсон — тиймээс заавал болсон. = 10 гэсэн default утга нь орхигдоно, учир нь path parameter default утгатай байж чадахгүй (хаягны хэсэг дутуу байж болохгүй).
/books гэж хандвал энэ endpoint огт таарахгүй. /books/3 гэж хандах ёстой болно. Энэ нь ихэвчлэн таны хүсээгүй зүйл — хязгаарлалт бол тохируулга, тохируулга бол query.
Дүрмийг санаарай: тодорхой юмыг заадаг бол path, тохируулга бол query.
Хүсвэл зангыг өөрөө үүсгэж үзээрэй. /books/latest-ыг дахин доош зөөж, хадгалаад, /books/latest руу хандаж 422-ыг харна уу. Дараа нь дээш зөөж, дахин хандаж ажиллахыг харна уу. Алдааг өөрийн гараар үүсгэж, дараа нь засах нь зөвхөн уншихаас хамаагүй илүү үлддэг.
Сонирхвол /books/oldest гэсэн шинэ endpoint нэмж, min() ашиглан хамгийн эртний номыг буцааж үзээрэй. Түүнийг хаана байрлуулах ёстойг та одоо мэднэ.
Path болон query parameter-ийг хослуулахад тусгай дүрэм байхгүй — зүгээр л хоёуланг нь функцийн параметрт бичнэ.
Дүрэм өмнөх шигээ: нэр нь замын дөрвөлжин хаалтад байвал path, байхгүй бол query.
Олон параметр буруу байвал 422-ийн detail жагсаалтад бүх алдаа нэг дор ирнэ.
Route ordering занга: FastAPI дэвтрээ дээрээс доош уншиж, эхний тохирлыг ашиглана.
{book_id} гэсэн хувьсагчтай зам нь latest гэх мэт тогтмол текстийг залгидаг.
Дүрэм: тогтмол зам хувьсагчтай замын өмнө бичигдэнэ. Тодорхойгоос ерөнхий рүү.
Сонин 422 харвал (танд байхгүй параметрийн тухай) — эхлээд дарааллаа шалгана.
Бүлэг 2 өндөрлөж байна. Та одоо хаягнаас утга авч, тохируулга хүлээж авч, буруу өгөгдлийг автоматаар татгалзаж чадна. Гэхдээ таны API одоо ч гэсэн нэг том дутагдалтай: алдааг зөв мэдээлж чаддаггүй. Байхгүй ном хайвал эсвэл 500 гарна (эвдэрсэн мэт), эсвэл 200 статустай "error" гэсэн текст ирнэ (амжилттай мэт). Хоёулаа худал ярьж байна.
Дараагийн бүлэг бүхэлдээ энэ асуудалд зориулагдана. Бид JSON хариугаа гүнзгийрүүлж, status code-ыг серверийн талаас удирдаж, HTTPException ашиглан алдааг үнэнчээр мэдээлж сурна. Тэгээд Түвшин 1-ийн эцэст бид эдгээрийг бүгдийг нэгтгэж, бүрэн ажиллагаатай Мэдээллийн API-г хамтдаа бүтээнэ.
Бүртгэлтэй болсноор энэ сургалтын бүх хичээлд хандах эрх авна.