خرید بک لینک

«FastAPI» یکی از فریم‌ورک‌های مدرن پایتون است که برای ساخت شتابان و ساده «API» و اپلیکیشن‌های بک‌اند به کار برده می‌شود. از زمان ورود پایتون به حوزه توسعه سمت سرور، محبوبیت آن به شدت رشد یافت. پایتون اکنون با فناوری‌های قدیمی‌تر و شناخته‌شده‌ای مانند «PHP» و «NET.» در طراحی بک‌اند سایت و وب‌اپلیکیشن‌ها رقابت می‌کند. ترکیب سادگی و قدرت پایتون باعث شده‌اند که فرایند توسعه برای برنامه‌نویسان ساده‌تر و کارآمدتر شود. یکی از مهم‌ترین ابزارها برای ایجاد این ترکیب (سادگی و قدرت) فریم‌ورک FastAPI است. دانستن اینکه FastAPI چیست و چطور باید از آن استفاده کرد، کمک می‌کند به شکل خیلی حرفه‌ای‌تری با API-ها کار کنیم.

در این گزارش از مجله فرادرس، فریم‌ورک FastAPI را تحلیل می‌کنیم. ابتدا شرح داده‌ایم که FastAPI چیست و چه کاربرد‌هایی دارد. سپس نکات مهم مربوط به مراحل ساخت API با این فریم ورک را با کمک مثال‌های ساده‌ای تحلیل خواهیم کرد. در پایان کار هم این فریم ورک را با رقبای مطرحی مانند جنگو، فلسک و پیرامید مقایسه می‌کنیم.

فریمورک مدرن FastAPI با این هدف طراحی شده است که برنامه نویسی «بک اند» (Back End)، وب‌اپلیکیشن‌ها و ساخت API با پایتون را ساده‌تر کند. کدهای نوشته شده با فریم‌ورک FastAPI علاوه بر سرعت و سادگی، کیفیت و کارایی بالایی هم دارند.

در فهرست پایین، چند مورد از مهم‌ترین خصوصیت‌های FastAPI را شرح داده‌ایم.

فریم‌ورک‌های مختلفی مانند «جنگو» (Django) و «فلسک» (Flask) با هدف ساخت «واسط‌های برنامه‌نویسی کاربردی وب» (Web API) توسعه یافته‌اند. بااین‌حال، سرعت همچنان یکی از چالش‌های مهم در توسعه وب با پایتون بود. به همین دلیل، فریم‌ورک دیگری به نام FastAPI برای پایتون ایجاد شد که هدف آن بهبود نحوه عملکرد و رفع بخشی از این محدودیت‌هاست.

FastAPI سادگی فلسک را نیز حفظ کرده است و ساختار آن شباهت زیادی به این فریمورک دارد. بااین‌حال، قابلیتات مفیدی مانند اعتبارسنجی داده‌ها، مستندسازی خودکار و رمزگذاری پاسخ‌ها را نیز به صورت پیش‌فرض در اختیار توسعه‌دهندگان قرار می‌دهد.

تا این قسمت از مطلب، به طور کلی متوجه شدیم که فریم‌ورک FastAPI چیست و چه کاربردی دارد. در بخش‌های بعد روش کار با این فریم‌ورک کاربردی و مدرن را در کنار چند ترفند و نکته مهم همراه با مثال‌های ساده‌ای می‌آموزید. در صورت تمایل به مطالعه مطالبی مانند این مورد، پیشنهاد می‌کنیم که حتما اپلیکیشن مجله فرادرس را بر روی دستگاه‌های همراه خود نصب کنید.

برای نصب اپلیکیشن بدون هزینه مجله فرادرس، کلیک کنید.

راه‌اندازی پروژه با FastAPI ساده است و در چند مرحله کوتاه انجام می‌شود.

برای شروع کار به کار و نصب بسته‌های معرفی شده در بالا دستور زیر را در خط فرمان یا پاورشل ویندوز یا ترمینال لینوکس نوشته و دکمه Enter را فشار می‌دهیم.

وجود پکیج‌های [fdboutput]{"content":"fastapi%20","type":"inline"}[/fdboutput] و [fdboutput]{"content":"uvicorn%20","type":"inline"}[/fdboutput] برای راه‌اندازی پروژه‌های FastAPI ضروری است.

برای ساخت اولین روت [fdboutput]{"content":"fastapi%20","type":"inline"}[/fdboutput] با همان روش پایتونی همیشگی شروع کار می‌کنیم. یعنی اول از همه فایلی با نام «main.py» می‌سازیم که تمام کدهای پروژه را در آن بنویسیم. فایل را در ویرایشگر کد خود باز کرده و دستورات زیر را داخل آن می‌نویسیم.

from fastapi import FastAPI

fastapi = FastAPI()

@fastapi.get("/")
async def home():
    return {"data": "Hello World"}

نکته: برای نوشتن مثال‌های این مطلب از ویرایشگر کد ویژوال استودیو کد استفاده می‌کنیم. اما شما می‌توانید با هر ویرایشگر دیگری مانند پیرامید، «پای‌چارم» (PyCharm) و غیره کار کنید. نوع ویرایشگر در کدها، پکیج‌ها و سینتکس برنامه هیچ تاثیری ندارد.

این دکوراتور برای توابعی به کار می‌رود که با هدف ساخت نقاط پایان کاری طراحی شده‌اند. با استفاده از این روش، می‌توان دستورات اصلی REST API مانند [fdboutput]{"content":"PUT%20","type":"inline"}[/fdboutput] و [fdboutput]{"content":"DELETE%20","type":"inline"}[/fdboutput] و [fdboutput]{"content":"PATCH%20","type":"inline"}[/fdboutput] و [fdboutput]{"content":"GET%20","type":"inline"}[/fdboutput] و [fdboutput]{"content":"POST%20","type":"inline"}[/fdboutput] را به کار برد و مسیر منبع را نیز تنظیم کرد.

بعد از نوشتن دستورات بالا با اجرای کد زیر در خط فرمان ویندوز یا ترمینال لینوکس می‌توانید سرور خود را راه‌اندازی کنید.

این دستور سرور را اجرا می‌کند. سپس از طریق مرورگر و با مراجعه به آدرس [fdboutput]{"content":"http%3A%2F%2F127.0.0.1%3A8000%20","type":"inline"}[/fdboutput] می‌توانید به برنامه ساخته شده دسترسی داشته باشید.

def handler():
    return { "data": "from handler"}

بعد از نوشتن تابع باید دکوراتور خود را با کمک نمونه ساخته شده از روی کلاس [fdboutput]{"content":"FastAPI%20","type":"inline"}[/fdboutput] به تابع اضافه کنیم. دکوراتور را به شکل زیر می‌نویسیم.

@fastapi.post("/home-page")

با اضافه شدن این دکوریتور به تابع، بلوک کد از تابع به نقطه خاتمهی API تبدیل می‌شود.

برای ساخت نقاط پایان کاری مختلف می‌توانیم از دکوریتور‌های زیر استفاده کنیم.

فرادرس، محتوای آموزشی بسیار متنوع و دقیقی را با هدف کمک به یادگیری مفصل پایتون، منتشر کرده است. این محتوا در دو بخش (یعنی مطالب متنی و فیلم‌های آموزشی) تشکیل شده است. به منظور دسترسی به مطالب علمی می‌توانید از مجله فرادرس کمک بگیرید. اما برای استفاده از فیلم‌های آموزشی باید به وب‌سایت فرادرس مراجعه کنید. در این وب‌سایت، فیلم‌های آموزشی بسیار دقیق و حرفه‌ای را پیدا می‌کنید. این فیلم‌ها به وسیله اساتید باتجربه و حرفه‌ای تهیه شده‌اند. هر کدام از فیلم‌های آموزشی فرادرس که به حساب کاربری اضافه شوند، برای همیشه در دسترس خواهند بود.

پایتون، گزینه‌ای بسیار مناسب برای توسعه نرم‌افزارهای مدرن و پیچیده به خصوص در حوزه هوش مصنوعی است. فرادرس هم با توجه به این مسئله، مجموعه‌ای غنی از فیلم‌های آموزشی پایتون را تولید کرده است. این فیلم‌ها از ابتدایی‌ترین موارد تا نکات بسیار پیچیده و حرفه‌ای را پوشش می‌دهند. برای همین با استفاده از فیلم‌های فرادرس می‌توانید به مهارت بسیار خوبی در حوزه برنامه نویسی با پایتون برسید.

در پایین، چند مورد از فیلم‌های آموزش پایتون را نام برده‌ایم. این فیلم‌ها برای آموزش سطوح مبتدی تا پیشرفته‌ پایتون مفید هستند.

در بخش بعد، روش مدیریت بدنه درخواست‌ها و پاسخ‌ها را با استفاده از FastAPI مرور می‌کنیم.

در این بخش از مطلب، بیان داده‌ایم که روش افزودن داده‌ها به درخواست‌ها و دریافت شرح موجود در پاسخ‌ها با استفاده از FastAPI چیست. ارسال و دریافت داده‌ها از «نقطه خاتمهی» (Endpoint)، جزو مهم‌ترین قسمت‌ها در توسعه API است. برای ارسال داده به نقطه پایان کاری می‌توان از روش‌های مختلفی استفاده کرد. در فهرست پایین بعضی از مهم‌ترین روش‌های انجام این کار را نام برده‌ایم.

در ادامه این بخش تمام تکنیک‌های بالا را یک به یک همراه با مثال‌های ساده‌ای ارزیابی می‌کنیم.

در این روش، داده‌های کوتاه به صورت مستقیم به مسیر «آدرس وب» (URL) اضافه می‌شوند. برای استفاده از این قابلیت در نقاط پایان کاری FastAPI، می‌توانید از نمونه زیر استفاده کنید.

# other data goes here
@fastapi.get("/{name}")
async def get_name(name: str):
    return { "name": name }

در این مثال، [fdboutput]{"content":"name","type":"inline"}[/fdboutput]، پارامتر مسیر است. مقدار آن از آدرس وب دریافت شده و به‌عنوان پارامتر به تابع [fdboutput]{"content":"get_name%20","type":"inline"}[/fdboutput] ارسال می‌شود. در حاصل، به‌سادگی می‌توان به داده‌ی ارسال شده از طریق مسیر آدرس، دسترسی داشت.

«پارامترهای پرس‌وجو» (Query Parameters) مانند پارامترهای مسیر (با هدف ارسال داده به نقطه اتمامی) استفاده می‌شوند، اما در بخش متفاوتی از URL قرار می‌گیرند. این پارامترها بعد از علامت سوال ([fdboutput]{"content":"%3F","type":"inline"}[/fdboutput]) در آدرس وب قرار می‌گیرند.

برای استفاده از آن‌ها در [fdboutput]{"content":"fastapi","type":"inline"}[/fdboutput]، می‌توانید از نمونه زیر استفاده کنید.

@fastapi.get("/")
async def get_api_data(data_type: str, skip: int = 0, limit: int = 10):
    return { "data_type": data_type, "skip": skip, "limit": limit }

در این مثال، [fdboutput]{"content":"skip%20","type":"inline"}[/fdboutput] و [fdboutput]{"content":"limit%20","type":"inline"}[/fdboutput] پارامترهای پرس‌وجو هستند. این پارامترها بعد از مسیر نقطه خاتمهی ([fdboutput]{"content":"%2F","type":"inline"}[/fdboutput] ) و به صورت جفت‌های [fdboutput]{"content":"key%3Dvalue%20","type":"inline"}[/fdboutput] در URL قرار می‌گیرند. مقدار پیش‌فرض [fdboutput]{"content":"skip%20","type":"inline"}[/fdboutput] برابر [fdboutput]{"content":"0","type":"inline"}[/fdboutput] و مقدار پیش‌فرض [fdboutput]{"content":"limit%20","type":"inline"}[/fdboutput] برابر [fdboutput]{"content":"10","type":"inline"}[/fdboutput] است. با وارد کردن مقادیر متفاوت در URL می‌توان این مقادیر را تغییر داد.

این تکنیک با دو روش قبلی تفاوت دارد. در این حالت، داده‌ها در بدنه درخواست قرار می‌گیرند و همراه با درخواست به نقطه پایان کاری ارسال می‌شوند. در کادر پایین، روش کلی را برای تعریف پارامتر‌های بدنه نوشته‌ایم.

from pydantic import BaseModel
from fastapi import FastAPI

fastapi = FastAPI()
class Item(BaseModel):
    name: str
    price: float

@fastapi.post("/items")
async def create_item(item: Item):
    return {"item": item}

در مثال بالا کلاس [fdboutput]{"content":"Item%20","type":"inline"}[/fdboutput] را تعریف کرده‌ایم. این کلاس از کلاس [fdboutput]{"content":"BaseModel%20","type":"inline"}[/fdboutput] ارث‌بری می‌کند. کلاس [fdboutput]{"content":"BaseModel%20","type":"inline"}[/fdboutput] (در خط اول) از درون ماژول [fdboutput]{"content":"Pydantic%20","type":"inline"}[/fdboutput] به محیط برنامه نویسی وارد شده است. [fdboutput]{"content":"BaseModel","type":"inline"}[/fdboutput] تمام پارامتر‌های بدنه درخواست‌ها را فراهم می‌کند و اطلاعات بیشتری درباره این پارامتر‌ها به [fdboutput]{"content":"fastapi%20","type":"inline"}[/fdboutput] می‌دهد.

یکی از مهم‌ترین روش‌ها برای ارسال داده‌ها بیشتر درباره محتوا به سرور این است که از هدر‌ها و کوکی‌ها استفاده کنیم. به منظور انجام این کار با کمک [fdboutput]{"content":"fastapi%20","type":"inline"}[/fdboutput] فقط کافی است که از الگوی نوشته شده در کادر‌های پایین پیروی کنید.

ابتدا روش دریافت اطلاعات با کمک «هدر‌ها» (Headers) را ارزیابی می‌کنیم.

from fastapi import FastAPI, Header

app = FastAPI()

@app.get("/items")
async def read_items(user_agent: str = Header(None)):
    return {"User-Agent": user_agent}

در مثال بالا، پارامتر [fdboutput]{"content":"user_agent%20","type":"inline"}[/fdboutput] مقدار [fdboutput]{"content":"Header(None)%20","type":"inline"}[/fdboutput] را به عنوان پیش‌فرض دارد. این عبارت به [fdboutput]{"content":"fastapi%20","type":"inline"}[/fdboutput] می‌گوید که مقداری را از هدر درخواست ارسال شده، دریافت کند. حالا اگر داخل درخواست فعلی، هدر وجود داشت، متغیر [fdboutput]{"content":"user_agent%20","type":"inline"}[/fdboutput] مقدار خود را از هدر دریافت می‌کند. در غیر این صورت، مقدار متغیر [fdboutput]{"content":"user_agent%20","type":"inline"}[/fdboutput] برابر با [fdboutput]{"content":"None%20","type":"inline"}[/fdboutput] قرار می‌گیرد.

در کادر زیر هم روش دریافت بیانات با کمک «کوکی‌ها»‌ (Cookies) را ارزیابی می‌کنیم.

from fastapi import FastAPI, Cookie

app = FastAPI()

@app.get("/items/")
async def read_items(session_token: str = Cookie(None)):
    return {"session_token": session_token}

در مثال بالا هم مانند مثال قبلی پارامتری با مقدار پیش‌فرض ساخته‌ایم. این بار، نام پارامتر [fdboutput]{"content":"session_token%20","type":"inline"}[/fdboutput] بوده و مقدار پیش‌فرض برابر با [fdboutput]{"content":"Cookie(None)%20","type":"inline"}[/fdboutput] است. عبارت [fdboutput]{"content":"session_token%3A%20str%20%3D%20Cookie(None)","type":"inline"}[/fdboutput] به [fdboutput]{"content":"fastapi%20","type":"inline"}[/fdboutput] می‌گوید که داده‌های موجود در بخش کوکی‌های درخواست رسیده را دریافت کند و در پارامتر [fdboutput]{"content":"session_token%20","type":"inline"}[/fdboutput] قرار بدهد. اگر درخواست رسیده هیچ کوکی نداشته باشد، مقدار پارامتر [fdboutput]{"content":"session_token%20","type":"inline"}[/fdboutput] برابر با [fdboutput]{"content":"None%20","type":"inline"}[/fdboutput] خواهد شد.

وقتی که سرور در حال اجرا است، در مرورگر به آدرس [fdboutput]{"content":"http%3A%2F%2F127.0.0.1%3A8000%2Fdocs","type":"inline"}[/fdboutput] بروید.

در این آدرس تمام API-ها و نقاط اتمامی مربوط به آن‌ها همراه با بیانات دقیق قرار داده شده‌‌اند. در واقع با کمک این آدرس به مستندات دقیق API-های خود دسترسی دارید. در قسمت بعد روش استفاده از FastAPI را در پروژه واقعی ارزیابی می‌کنیم.

در این قسمت از مطلب می‌خواهیم با کمک پیاده‌سازی REST API در پروژه کاربردی به درک بهتری از FastAPI برسیم. به منظور نمایش قدرت FastAPI، می‌خواهیم برای اپلیکیشن فرضی ذخیره داده (عکس) REST API بسازیم. این API باید بتواند به پایگاه داده متصل شود، از عملیات بارگذاری عکس‌ها پشتیبانی کند و روت‌های امنی داشته باشد.

API مورد نظر باید اندپوینت‌ها یا نقاط خاتمهی زیر را داشته باشد.

سپس می‌توانیم نقاط اتمامی بیشتری هم برای مدیریت فایل‌ها بسازیم. علاوه بر این پایگاه داده و نوع خاصی از سازوکار احراز هویت را پیاده‌سازی می‌کنیم. این سیستم احراز هویت با استفاده از «میان‌افزار» (Middleware) بر روی بعضی از نقاط اتمامی اجرا خواهد شد.

برای اجرای این پروژه باید حداقل دانش قابل قبول را درباره کار با موارد زیر داشته باشید.

در ضمن به پایتون ادیشن ۳.۱۰ هم به صورت نصب شده در کامپیوتر نیاز داریم.

مراحل مربوط به تنظیمات اولیه پروژه را در فهرست پایین نوشته‌ایم.

نکته: البته اصلا نیازی به قرار دادن دایرکتوری پروژه داخل دایکتوری مربوط به فضای مجازی نیست. همین‌که فضای مجازی فعال شود می‌توانیم از پروژه خود در محیطی ایزوله استفاده کنیم. یعنی توصیه شده که کدهای پروژه حتما بیرون از دایرکتوری مربوط به محیط مجازی قرار بگیرند.

برای اجرای این پروژه به نصب وابستگی‌های ضروری مانند [fdboutput]{"content":"uvicorn%20","type":"inline"}[/fdboutput] و [fdboutput]{"content":"fastapi%20","type":"inline"}[/fdboutput] احتیاج داریم. هر چند با اضافه کردن اتصال به پایگاه داده و سیستم بارگذاری عکس در این پروژه می‌خواهیم توانایی‌های سرور را ارتقا بدهیم. بنابراین به وابستگی‌های دیگری هم نیاز خواهیم داشت. برای مثال، پایگاه داده مناسب و SQLAlchemy را هم باید نصب کنیم.

با هدف نصب وابستگی‌های مورد نیاز این پروژه، دستور زیر را در خط فرمان نوشته و دکمه Enter را فشار دهید.

نکته: این دستور مربوط به محیط لینوکسو MacOS است. در صورت کار با ویندوز باید کاراکتر‌های کوتیشن را از دستور حذف کنید.

ابتدا وارد دایرکتوری src بشوید. سپس فایل نوینی به نام main.py در این دایرکتوری ایجاد کنید. این کار را به عنوان نقطه شروع کاری برای پیاده‌سازی پروژه در نظر بگیرید. البته به غیر از کار با این فریم‌ورک، روش‌های مختلفی برای ساخت API با پایتون وجود دارد. در صورت تمایل به آشنایی با این روش‌ها پیشنهاد می‌کنیم که مطلب مربوط به این مسئله را در مجله فرادرس مطالعه کنید.

اکنون کدهای زیر را در فایل main.py بنویسید (یا از همین‌جا کپی و پیست کنید.).

from fastapi import FastAPI, Depends
from fastapi import HTTPException
from .utils import find_item
from pydantic import BaseModel, Field
from typing import Optional

fastapi = FastAPI()

inventory = [
  { "id": 1, "name": "Treasure", "quantity": 3 }
]


class Item(BaseModel):
  name: str
  quantity: int

class ItemUpdate(BaseModel):
  name: Optional[str] = Field(None, description="Optional name of the item")
  quantity: Optional[int] = Field(None, description="Optional quantity of the item")

@fastapi.get("/items")
async def get_items():
  return {"items": inventory}


@fastapi.get("/items/{item_id}")
async def get_item(item_id: int):
  item, idx = find_item(inventory, lambda x: x["id"] == item_id)
  return { "item": item }

@fastapi.delete("/items/{item_id}")
async def delete_item(item_id: int, authenticated: bool = Depends(authenticate)):
  item, idx = find_item(inventory, lambda x: x["id"] == item_id)
  if idx == -1: raise HTTPException(404, "item not found")
  inventory.pop(idx)
  return { "item": item }



@fastapi.post("/items")
async def add_item(data: Item):
  item = {
    "id": len(inventory) + 1,
    "name": data.name,
    "quantity": data.quantity
  }
  inventory.append(item)
  return item

@fastapi.patch("/items/{item_id}")
async def update_item(item_id: int, item_update: ItemUpdate):
  item, idx = find_item(inventory, lambda x: x["id"] == item_id)
  if idx == -1:
    raise HTTPException(status_code=404, detail="Item not found")

  if item_update.name is not None:
    item["name"] = item_update.name

  if item_update.quantity is not None:
    item["quantity"] = item_update.quantity

  inventory[idx] = item

  return item

در تکه کدهای بالا ۵ هندلر برای نقاط پایان کاری تعریف کرده‌ایم.

در ادامه توابع کاربردی را در فایلی به نام «utils.py» قرار خواهیم داد. این فایل را در دایرکتوری src می‌سازیم.


def find_item(list_dict, cb: lambda x: True) -> (dict, int):
  for (idx, item) in enumerate(list_dict):
    if cb(item):
      return item, idx

  return {}, -1

اکنون می‌توانیم سرور را اجرا کرده و تمام نقاط خاتمهی تعریف شده را با توجه به مستندات API آزمایش کنیم.

«مدیریت خطا»‌ (Error Handling) در [fdboutput]{"content":"fastapi%20","type":"inline"}[/fdboutput] بسیار ساده و قابل درک است. [fdboutput]{"content":"fastapi%20","type":"inline"}[/fdboutput] برای انجام بهتر این کار، کلاس درونی را با نام [fdboutput]{"content":"HTTPException%20","type":"inline"}[/fdboutput] فراهم کرده است. با کمک این کلاس، هر وقت که بخشی از سیستم به مشکل خورد می‌توانیم پاسخ‌های مناسبی را به سادگی برگردانیم.

مراحل ساخت API با FastAPI بسیار ساده است. اما نکات ریزی مانند همین مدیریت خطا و غیره وجود دارند که با رعایت آن‌ها می‌توانیم محصولی پیشرفته و حرفه‌ای به کاربران ارائه دهیم. برای یادگیری روش کار با این فریم‌ورک پیشنهاد می‌کنیم که فیلم آموزش ساخت وب سرویس API با FastAPI در پایتون، دوره مقدماتی + گواهینامه را در فرادرس مشاهده کنید. به منظور کمک به مخاطبان مجله، لینک دسترسی مستقیم به این فیلم را در پایین نیز قرار داده‌ایم.

در کادر پایین، مثال ساده‌ای را درباره استفاده از کلاس [fdboutput]{"content":"HTTPException%20","type":"inline"}[/fdboutput] برای مدیریت داده‌های ورودی نامعتبر پیاده‌سازی کرده‌ایم.

from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    if item_id <= 0:
        # Raise an error if the item_id is invalid
        raise HTTPException(
            status_code=400,
            detail="Invalid ID. ID must be greater than 0."
        )
    return {"item_id": item_id}

اکنون هر وقت که کاربری مقدار [fdboutput]{"content":"item_id%20","type":"inline"}[/fdboutput] را در درخواست ارسال شده کمتر یا مساوی صفر قرار دهد، با پاسخ زیر روبه‌رو خواهد شد.

{
  "detail": "Invalid ID. ID must be greater than 0."
}

روش سفارشی سازی پاسخ‌های خطا در FastAPI چیست؟

برنامه نویسان می‌توانند پاسخ‌های خطا را در [fdboutput]{"content":"fastapi%20","type":"inline"}[/fdboutput] با سلیقه خودشان به شکل سفارشی تغییر بدهند. بیشتر اوقات این کار برای افزودن اطلاعات بیشتر به پاسخ انجام می‌شود.

@app.get("/users/{user_id}")
async def read_user(user_id: int):
    if user_id > 100:
        raise HTTPException(
            status_code=404,
            detail={'error': 'User not found', 'user_id': user_id}
        )
    return {"user_id": user_id}

اکنون اگر شماره ID کاربر نامعتبر باشد، پاسخ زیر برگردانده می‌شود.

{
  "error": "User not found",
  "user_id": 150
}

دریافت خطاهای سرور

خطاهای غیر منتظره را می‌توان با کمک هندلر‌های استثنایی زیر مدیریت کرد.

from fastapi import Request
from fastapi.responses import JSONResponse

@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
    return JSONResponse(
        status_code=500,
        content={"message": "An unexpected error occurred. Please try again."}
    )

کدهای بالا تضمین می‌کنند که اگر مشکل ناخواسته‌ای در سرور رویداد، حتما پیام مناسبی به سمت کاربران ارسال شود.

به طور پیش‌فرض، سرعت کار فریم‌ورک FastAPI بالا است. اما راه‌هایی وجود دارد که برنامه نویسان با کمک آن‌ها می‌توانند باز هم سرعت کار این فریم‌ورک را بالاتر ببرند.

چند مورد از مهم‌ترین روش‌های مربوط به اجرای سریع‌تر‌تر پروژه‌های FastAPI را در فهرست پایین نام برده‌ایم.

در ادامه این بخش از مطلب، تمام کد‌های مربوط به ترفندهای ساده بالا را یک به یک ارزیابی خواهیم کرد.

برای پاسخ به درخواست‌های HTTP از کتابخانه‌های «ناهمگام» یا «async» مانند [fdboutput]{"content":"httpx%20","type":"inline"}[/fdboutput] استفاده کنید. در این صورت انتظار برای پاسخ HTTP بقیه کارها را مسدود نمی‌کند.

import httpx
from fastapi import FastAPI

app = FastAPI()

@app.get("/data")
async def get_data():
    async with httpx.AsyncClient() as client:
        response = await client.get("https://api.example.com/data")
        return response.json()

پیاده‌سازی کش

اغلب اوقات سرور‌ها به طور تکراری با پایگاه داده و API-های یکسان کار می‌کنند. کش کردن داده‌ها می‌تواند تعداد فراخوانی‌های پایگاه داده یا API را کم شدن دهد. Redis یکی از بهترین ابزارها برای کش کردن پاسخ‌های دریافت شده است.

import aioredis
from fastapi import FastAPI

app = FastAPI()
redis = aioredis.from_url("redis://localhost")

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    # Check if item is cached
    cached_item = await redis.get(f"item:{item_id}")
    if cached_item:
        return {"item": cached_item.decode("utf-8")}

    # Simulate database fetch
    item = f"Item {item_id}"
    await redis.set(f"item:{item_id}", item)
    return {"item": item}

استفاده از سرور‌های توزیع متوازن باز

سرویس‌های «توزیع متوازن بار» یا همان «لود بالانسر» (Load Balancer) مانند «Nginx» یا «Traefik» را برای مدیریت بیشتر کار‌های مربوط به درخواست‌ها پیکربندی کنید. برای مثال در کادر پایین، نمونه‌ای از فایل پیکربندی Nginx را نوشته‌ایم.

server {
    listen 80;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Coection "upgrade";
        proxy_set_header Host $host;
    }
}

ارتقای بهره‌وری کوئری‌ها

منظور از عبارت «ارتقای بهره‌وری کوئری‌ها» این است که با استفاده از کارهایی مانند اندیس‌گذاری داده‌ها و جلوگیری از واکشی غیرضروری اطلاعات یا داده‌های بدون کاربرد، کارایی و کارایی کوئری‌های ارسال شده با پایگاه داده را بیشتر کنیم.

from sqlalchemy.orm import Session
from fastapi import Depends

@app.get("/users")
def get_users(limit: int = 10, db: Session = Depends(get_db)):
    return db.query(User).limit(limit).all()

فشرده‌سازی Gzip

با استفاده از فشرده‌سازی Gzip می‌‌توانیم اندازه پاسخ‌های برگشتی را کوچک‌تر بکنیم. برای استفاده از این تکنیک، ابتدا باید ماژول مربوط به فشرده‌سازی در FastAPI را نصب کنیم.

from fastapi import FastAPI
from fastapi_compression import CompressionMiddleware

app = FastAPI()
app.add_middleware(CompressionMiddleware)

با استفاده از کدهای بالا اندازه پاسخ‌های ارسالی با رعایت استاندارد‌های دقیق افت پیدا می‌کند.

رعایت ترفند‌ها و نکات گفته شده در این بخش به با سرعت‌تر و کارآمد‌تر شدن اپلیکیشن FastAPI کمک می‌کند. در ضمن این اپلیکیشن الان می‌توانید وظایف سنگینی را به راحتی مدیریت کند.

بیشتر اوقات API-ها به همان سادگی نیستند که در بخش پروژه عملی برای ذخیره کردن عکس ساختیم. گاهی لازم است پیش از مدیریت درخواست‌ها یا فایل‌های ارسالی، هویت کاربر یا سیستم ارسال‌کننده درخواست تایید شود. در برخی موارد نیز باید داده‌ها را در پایگاه داده ذخیره کنیم.

برای مثال، در ادامه می‌خواهیم «میان‌افزاری» (Middleware) برای اعتبارسنجی اپلیکیشن ذخیره‌سازی [fdboutput]{"content":"inventory","type":"inline"}[/fdboutput] (که در بخش‌های قبل تعریف کردیم) اضافه کنیم. علاوه بر این قابلیت بارگذاری فایل‌ها و اتصال به پایگاه داده را نیز به این اپلیکیشن اضافه خواهیم کرد.

در دنیای استفاده از API-ها، میان‌افزار مانند دریچه یا سوپاپ عمل می‌کند. این ابزارها کاربرد‌های بسیار زیاد و متنوعی دارند. برای مثال می‌توان به محدود‌کردن دسترسی برای بعضی کاربران، افزودن گزارش‌ها اضافی به درخواست‌ها و غیره اشاره کرد. برای نمایش روش‌ کار این ابزارها، در این بخش می‌خواهیم میان‌افزاری درست کنیم که فقط به کاربرانی با مجوز خاص فرصت دسترسی به داده‌ها را می‌دهد. سپس این Middleware را به چند مورد از نقاط اتمامی اپلیکیشن ذخیره‌سازی (تعریف شده در بخش بالا) اضافه می‌کنیم.

قبل از هر کاری باید فایل نوینی با نام middleware.py در دایرکتوری src بسازیم و کدهای زیر را در آن بنویسیم.

from fastapi import HTTPException, Depends
from fastapi.security import HTTPBasic, HTTPBasicCredentials

security = HTTPBasic()

def authenticate(credentials: HTTPBasicCredentials = Depends(security)):
    correct_username = 'admin'
    correct_password = 'password'
    if credentials.username != correct_username or credentials.password != correct_password:
        raise HTTPException(status_code=401, detail="Unauthorized")
    return True

در کدهای بالا Middleware-ی با نام [fdboutput]{"content":"authenticate","type":"inline"}[/fdboutput] تعریف کرده‌ایم. این میان‌افزار «سیستم تایید هویت» (Authentication) ساده‌ای را پیاده‌سازی می‌کند. برای اضافه کردن میان‌افزار [fdboutput]{"content":"authenticate","type":"inline"}[/fdboutput] به هندلر روت مورد نظرمان ابتدا فایل main.py را باز می‌کنیم. اکنون باید محل نقاط پایان کاری را پیدا کنیم که می‌خواهیم سیستم تایید هویت خود را بر روی آن‌ها فعال کنیم. سپس پارامتر مورد نظر را به صورت زیر به این نقطه خاتمهی اضافه می‌کنیم.

authenticated: bool = Depends(authenticate)

بعد از اجرای این عملیات، کدهای هندلر باید شبیه به مورد زیر باشند.

@fastapi.post("/items")
async def add_item(data: Item, authenticated: bool = Depends(authenticate)):
  item = {
    "name": data.name,
    "id": len(inventory) + 1,
    "quantity": data.quantity
  }
  inventory.append(item)
  return item

نکته: برای اجرای کدهای بالا باید از قبل تابع [fdboutput]{"content":"authenticate%20","type":"inline"}[/fdboutput] را از فایل middleware.py و [fdboutput]{"content":"Depends%20","type":"inline"}[/fdboutput] را از فریم‌ورک [fdboutput]{"content":"fastapi%20","type":"inline"}[/fdboutput] به محیط برنامه نویسی در فایل main.py ایمپورت کرده باشید.

برای اضافه کردن پایگاه داده به اپلیکیشن [fdboutput]{"content":"inventory%20","type":"inline"}[/fdboutput] قبل از هر چیزی باید مطمئن شویم که [fdboutput]{"content":"sqlalchemy%20","type":"inline"}[/fdboutput] و پایگاه داده مورد نظر نصب شده‌اند. سپس فایلی با نام «database.py» در دایرکتوری src ایجاد می‌کنیم. در بالا‌ترین قسمت از این فایل باید ایمپورت‌های مهم و لازم را بنویسیم.

from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

در این گزارش از پایگاه داده SQLite استفاده می‌کنیم. روش کار با این پایگاه داده بسیار ساده است.

در بخش بعد با اضافه کردن کدهای زیر به فایل، موتور پایگاه داده را می‌سازیم.

DATABASE_URL = "sqlite:///./database.db"
engine = create_engine(DATABASE_URL)

کدهای بالا پایه‌ای‌ترین پیکربندی‌های پایگاه داده هستند. بعد از نوشتن این کدها باید شی [fdboutput]{"content":"SessionLocal%20","type":"inline"}[/fdboutput] را از روی کلاس [fdboutput]{"content":"sessionmaker","type":"inline"}[/fdboutput] تعریف کنیم. این کدها وظیفه ساخت سشن پایگاه داده را دارند. با هر بار فراخوانی این شی، یکبار سشن ایجاد می‌شود. سپس کلاس [fdboutput]{"content":"Base%20","type":"inline"}[/fdboutput] را تعریف می‌کنیم. این کلاس هم نقش مدل پایه و اصلی «[fdboutput]{"content":"BaseModel","type":"inline"}[/fdboutput]» برای تمام مدل‌های پایگاه داده بازی می‌کند. یعنی تمام مدل‌های بعدی پایگاه داده که تعریف می‌کنیم باید از این مدل ارث‌بری کنند.

اکنون برای انجام وظایف بالا، کدهای زیر را به فایل database.py اضافه می‌کنیم.

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

در مرحله بعد با اضافه کردن کدهای زیر به فایل، مدلی از پایگاه داده را ایجاد می‌کنیم.

class DBItem(Base):
    __tablename__ = "items"

    id = Column(Integer, primary_key=True, index=True)
    quantity = Column(Integer)
    name = Column(String)

اکنون کارمان در این بخش به اتمام رسیده است. برای ادامه کار باید [fdboutput]{"content":"DBItem%20","type":"inline"}[/fdboutput] و [fdboutput]{"content":"SessionLocal%20","type":"inline"}[/fdboutput] را از فایل database.py به فایل main.py ایمپورت بکنیم.

در ضمن باید توجه کنید که لازم است هندلر‌های روت را هم به همین‌ترتیب به‌روز رسانی کنیم.

@fastapi.post("/items")
def create_item(item: Item, authenticated: bool = Depends(authenticate)):
    db = SessionLocal()
    new_item = DBItem(name=item.name, quantity=item.quantity)
    db.add(new_item)
    db.commit()
    db.refresh(new_item)
    return {"item" :new_item}

@fastapi.get("/items")
async def get_items():
  db = SessionLocal()
  items = db.query(DBItem).all()
  return { "items": items }


@fastapi.get("/items/{item_id}")
def get_item(item_id: int):
    db = SessionLocal()
    item = db.query(DBItem).filter(DBItem.id == item_id).first()
    if not item:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"item": item}

@fastapi.patch("/items/{item_id}")
def update_item(item_id: int, item: ItemUpdate, authenticated: bool = Depends(authenticate)):
    db = SessionLocal()
    db_item = db.query(DBItem).filter(DBItem.id == item_id).first()
    if not db_item:
        raise HTTPException(status_code=404, detail="Item not found")
    db_item.name = item.name
    db_item.quantity = item.quantity
    db.commit()
    db.refresh(db_item)
    return { "item": db_item}

@fastapi.delete("/items/{item_id}")
def delete_item(item_id: int, authenticated: bool = Depends(authenticate)):
    db = SessionLocal()
    db_item = db.query(DBItem).filter(DBItem.id == item_id).first()
    if not db_item:
        raise HTTPException(status_code=404, detail="Item not found")
    db.delete(db_item)
    db.commit()
    return {"message": "Item deleted"}

در کدهای بالا ابتدا نمونه‌ای از [fdboutput]{"content":"SessionLocal%20","type":"inline"}[/fdboutput] را برای هر نقطه اتمامی ایجاد کرده و سپس از این نمونه برای اجرای کوئری‌های پایگاه داده استفاده می‌کنیم. پس از اجرای کوئری، می‌توانیم اصلاحات ایجاد شده را برای ذخیره در پایگاه داده، ثبت کنیم.

با استفاده از [fdboutput]{"content":"fastapi%20","type":"inline"}[/fdboutput] به راحتی می‌توانیم فایل بر روی اپلیکیشن بارگذاری کنیم. اکنون از API موجود برای نمایش روش بار‌گذاری و ارائه فایل‌ها استفاده خواهیم کرد. قبل از هرکاری باید با اضافه کردن کدهای زیر، [fdboutput]{"content":"DBItem%20","type":"inline"}[/fdboutput] را در فایل database.py ارتقا کنیم.

class DBItem(Base):
    # other database fields goes here
    image_src = Column(String)

اکنون باید چند ایمپورت مهم در فایل main.py انجام بدهیم.

بعد از نوشتن ایمپورت‌های بالا، کدهای زیر را به فایل اضافه کنید:

@fastapi.patch("/item-image/{item_id}")
async def upload_file(item_id: int, file: UploadFile = File()):
    db = SessionLocal()
    db_item = db.query(DBItem).filter(DBItem.id == item_id).first()
    if not db_item:
        raise HTTPException(status_code=404, detail="Item not found")

    file_path = os.path.join("uploads", file.filename)
    with open(file_path, "wb") as f:
        f.write(await file.read())

    db_item.image_src = file.filename
    db.commit()
    db.refresh(db_item)

    return {"item": db_item}

@fastapi.get("/static/{file}")
async def serve_file(file: str):
    return FileResponse(os.path.join("uploads", file))
  • اولین روت هندلری که در کدهای بالا نوشته‌ایم وظیفه آپدیت فیلد [fdboutput]{"content":"image_src%20","type":"inline"}[/fdboutput] از آیتم روشن شده و بارگذاری فایل بر روی سرور را برعهده دارد.
  • دومین روت هندلر، با نام [fdboutput]{"content":"serve_file","type":"inline"}[/fdboutput] ، وظیفه دریافت فایل از سرور را برعهده دارد.

یادگیری عملی پایتون با کمک فرادرس

به دلیل شباهت زیاد سینتکس پایتون به زبان انسان، یادگیری آن نسبت به دیگر زبان‌ها آسان‌تر است. بهترین روش برای درک درست مفاهیم پایتون، کار با آن‌ها برای اجرای پروژه‌های واقعی است. بیشتر اوقات در پروژه‌های واقعی از تکنیک‌های متنوع به صورت ترکیبی و در کنار هم استفاده می‌کنیم. از طرف دیگر اجرای پروژه‌های ساده‌ای مانند مرور چند نمونه سوال حلقه ‎for‎ در پایتون به یادگیری این دستور کمک می‌کند. فرادرس، مجموعه‌ای از فیلم‌های آموزشی را با عنوان «مجموعه آموزش پروژه محور برنامه نویسی پایتون» تولید و منتشر کرده است. در این مجموعه، روش پیاده‌سازی پروژه‌های واقعی و گوناگون آموزش داده شده است.

در پایین، چند فیلم‌ آموزشی پروژه محور برای زبان پایتون را معرفی کرده‌ایم.

برای مشاهده فیلم‌های بیشتر بر روی تصویر زیر، کلیک کرده و به صفحه اصلی این مجموعه آموزش هدایت شوید.

FastAPI یکی از فریمورک‌های رایج و کاربردی در حوزه توسعه API-های سمت سرور است. این ابزار با فریمورک‌های شناخته شده پایتون مانند فلسک و جنگو رقابت می‌کند. از آنجا که جنگو و فلسک سابقه بیشتری در این حوزه دارند، مقایسه آن‌ها با FastAPI می‌تواند مفید باشد.

جنگو یکی از فریمورک‌های پایتون است که برای توسعه کدهای سمت سرور به کار برده می‌شود. فریمورک جنگو مجموعه‌ای از کتابخانه‌ها و ابزارهای مورد نیاز برای پروژه‌های مختلف را در اختیار توسعه‌دهندگان قرار می‌دهد. جنگو قابلیت‌های قدرتمندی مانند «نگاشت شیء-رابطه‌ای» (Object-Relational Mapping | ORM)، سیستم احراز هویت و مسیردهی دارد. به همین دلیل، برای ساخت برنامه‌های وب بزرگ و پیچیده مناسب است.

در مقابل، FastAPI مایکروفریمورک سبک‌وزنی است که با تمرکز بر سادگی و کارایی بالا طراحی شده. این فریمورک اکوسیستم داخلی کوچک‌تری نسبت به جنگو دارد، اما فرصت استفاده از ابزارها و کتابخانه‌های مختلف پایتون را فراهم می‌کند. FastAPI علاوه بر این با استفاده از قابلیت‌های مدرن پایتون و برنامه‌نویسی ناهمگام، می‌تواند بازده بالایی ارائه دهد.

توسعه‌دهندگان پایتون از فریمورک سبک‌وزن فلسک برای ساخت با سرعت برنامه‌های وب استفاده می‌کنند. این فریمورک، کنترل و انعطاف‌پذیری زیادی در اختیار توسعه‌دهندگان قرار می‌دهد. در برآیند، آن‌ها می‌توانند ساختار برنامه را مطابق نیازها و الزامات پروژه تعیین کنند.

FastAPI نیز فریمورکی سبک است، اما بیشتر بر ساخت API-های با سرعت و مقیاس‌پذیر تمرکز دارد. این فریمورک قابلیتاتی مانند اعتبارسنجی داده‌ها و مستندسازی خودکار را در اختیار توسعه‌دهندگان قرار می‌دهد. به همین دلیل، برای پروژه‌هایی که سرعت اجرا و توسعه API در آن‌ها اهمیت زیادی دارد، گزینه مناسبی به شمار می‌رود.

«پیرامید» (Pyramid) یکی دیگر از فریمورک‌های محبوب پایتون برای توسعه سمت سرور است. این فریمورک از فلسفه «فقط از آنچه نیاز دارید استفاده کنید» پیروی می‌کند. به همین دلیل، هسته‌ای کوچک دارد که می‌توان آن را با افزونه‌ها و کتابخانه‌های مختلف گسترش داد. این ساختار ماژولار به توسعه‌دهندگان اجازه می‌دهد اجزای مورد نیاز خود را مطابق کاربرد و نیاز پروژه انتخاب کنند. در برآیند، پیرامید فریمورکی سبک‌وزن و قابل شخصی‌سازی است.

در مقابل، FastAPI بیشتر بر بهره‌وری توسعه‌دهندگان و سهولت استفاده تمرکز دارد. این فریمورک ساختاری ساده و قابل درک دارد و مستندات آن نیز شفاف و همراه با نمونه‌های متعدد است. FastAPI از سوی دیگر ابزارهای داخلی مفیدی مانند تولید خودکار مستندات را در اختیار توسعه‌دهندگان قرار می‌دهد.

FastAPI مزایا و خصوصیت‌های مفید زیادی دارد. در ادامه، برخی از مهم‌ترین مزایای این فریمورک را تحلیل می‌کنیم:

در این گزارش از مجله فرادرس، به شکل ساده و جامع بیان داده‌ایم که فریمورک مدرن و شتابان FastAPI چیست و نحوه ساخت Web API در پایتون را مرور کردیم. برای راه‌اندازی سرور و ساخت نقاط پایان کاری به کمک این فریم‌ورک لازم است که پکیج‌های Uvicorn و FastAPI را نصب کنیم. برای مدیریت ورودی‌ها می‌توانیم از پارامترهای موجود در ریکوئست‌ها مانند پارامتر‌های مسیر، پرس‌وجو، بدنه درخواست، هدرها و کوکی‌ها استفاده کنیم. برای مدیریت خطاهای مربوط به API-ها در این فریم‌ورک هم می‌توانیم از کلاس [fdboutput]{"content":"HTTPException%20","type":"inline"}[/fdboutput] استفاده کنیم.

برای کمک به درک بهتر مطلب، روند پیاده‌سازی پروژه‌ای ساده و واقعی را ارزیابی کردیم. در این پروژه برای ذخیره عکس REST API ساختیم. سپس آن را با استفاده از SQLAlchemy به پایگاه‌داده SQLite متصل کردیم و میان‌افزار احراز هویت و قابلیت مدیریت بارگذاری فایل‌ها را به آن افزودیم. در نهایت هم، ترفندهای بهینه‌سازی سرعت، مانند برنامه‌نویسی ناهمگام، فشرده‌سازی Gzip و استفاده از Redis را مرور کردیم. در ادامه FastAPI را با گزینه‌هایی مانند جنگو و فلسک مقایسه کردیم تا بتوانید ابزار مناسب را برای پروژه‌های خود انتخاب کنید.

نوشته FastAPI چیست؟ – آموزش از صفر برای ساخت API اولین بار در فرادرس - مجله‌. پدیدار شد.



سوالات متداول

FastAPI چیست؟ – آموزش از صفر برای ساخت API چیست؟

در این مطلب اطلاعات، جزئیات و نکات مرتبط با FastAPI چیست؟ – آموزش از صفر برای ساخت API بررسی شده است.

مهم‌ترین نکات درباره استفاده چیست؟

جزئیات مهم و نکات قابل توجه درباره استفاده در متن مقاله بررسی شده است.

چرا FastAPI چیست؟ – آموزش از صفر برای ساخت API اهمیت دارد؟

این موضوع به دلیل اطلاعات و کاربردهای مرتبط، مورد توجه کاربران قرار گرفته است.

برچسب: نویسنده: خنجی تاريخ: شنبه 18 مهر 1405 ساعت: 13:11

صفحه بندی

خبرنامه