404 Not Found

404 Not Found


nginx

معاملات المسار ومعاملات الاستعلام — تصميم التوجيه الدقيق

تصميم التوجيه كتخطيط طرق المدينة — معاملات المسار كعناوين الشوارع (تحديد دقيق)، ومعاملات الاستعلام كمعايير التصفية (تضييق النطاق)؛ فقط بتعاونهما معاً يمكنك الوصول إلى وجهتك بسرعة.

1. ما ستتعلمه


2. القصة الحقيقية لـ Alice

(1) نقطة الألم: معاملات API استعلام المنتجات مربكة

يحتاج PriceTracker الخاص بـ Alice لدعم طرق استعلام متعددة: استعلامات دقيقة حسب معرّف المنتج، والتصفية حسب الفئة ونطاق السعر، والتصفح المُصفَّى بالترتيب. أرسل Bob من الواجهة الأمامية category=electronics&min_price=10&max_price=999، لكن كود Flask الخاص بـ Alice يحلل كل معامل يدوياً. تحويلات الأنواع عرضة للأخطاء، ولا يوجد تحقق من الأسعار السالبة أو حقول الترتيب غير الصالحة، مما يؤدي إلى حوادث متكررة في الإنتاج.

(2) حلول التحقق من المعاملات في FastAPI

يستخدم FastAPI تلميحات الأنواع لتحليل المعاملات والتحقق منها تلقائياً. يوفر Path() وQuery() قيوداً تصريحية، والمعاملات غير الصالحة تُطلق تلقائياً خطأ 422.

PYTHON
from fastapi import FastAPI, Path, Query

app = FastAPI()

@app.get("/products/{product_id}")
async def get_product(
    product_id: int = Path(gt=0, description="معرّف المنتج يجب أن يكون موجباً"),
    category: str | None = Query(None, max_length=50),
):
    return {"product_id": product_id, "category": category}

(3) العائد

انخفض كود التحقق من المعاملات من 30 سطراً إلى 3 أسطر، وأصبحت استجابات خطأ 422 تتضمن الآن تلقائياً تفاصيل محددة حول فشل التحقق، مما يسمح لـ Bob بتحديد أي معامل غير صالح فوراً. كما يعرض توثيق API تلقائياً جميع القيود.


3. شرح مفصّل لمعاملات المسار

(1) معاملات المسار الأساسية

معاملات المسار جزء من مسار URL وتُعرَّف باستخدام بناء {param}؛ يحوّلها FastAPI تلقائياً بناءً على تلميحات الأنواع.

100%
sequenceDiagram
    participant Client
    participant Router as موجه FastAPI
    participant Converter as محوّل الأنواع
    participant Validator as مُحقِّق المسار
    participant Handler as دالة العرض

    Client->>Router: GET /products/42
    Router->>Converter: استخراج "42" من المسار
    Converter->>Converter: int("42") → 42
    Converter->>Validator: product_id=42 (int)
    Validator->>Validator: فحص gt=0 → 42 > 0 ✓
    Validator->>Handler: get_product(product_id=42)
    Handler-->>Client: {"product_id": 42}
نوع معامل المسار مثال URL نوع Python التحويل التلقائي
عدد صحيح /products/42 int "42"42
عدد عشري /prices/9.99 float "9.99"9.99
سلسلة نصية /categories/electronics str كما هي
مسار /files/src/main.py Path سلسلة نصية تحتوي على /

(1) ▶ مثال: معاملات المسار الأساسية وتحويل الأنواع

PYTHON
from fastapi import FastAPI

app = FastAPI()

@app.get("/products/{product_id}")
async def get_product(product_id: int):
    # FastAPI يحوّل تلقائياً "42" إلى int(42)
    # إذا أرسل المستخدم /products/abc ← خطأ 422
    return {"product_id": product_id, "type": str(type(product_id))}

الناتج:

TEXT
INFO:     127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK

Output (/products/42):

TEXT
{"product_id": 42, "type": "<class 'int'>"}

(2) التحقق من قيود Path()

يضيف Path() قيوداً مثل النطاقات العددية وأطوال السلاسل النصية لمعاملات المسار؛ وتنعكس هذه تلقائياً في توثيق OpenAPI.

معامل القيد النوع المطبَّق المعنى
gt int/float أكبر من (>)
ge int/float أكبر من أو يساوي (>=)
lt int/float أصغر من (<)
le int/float أصغر من أو يساوي (<=)
min_length str الحد الأدنى للطول
max_length str الحد الأقصى للطول
pattern str مطابقة تعبير منتظم
description الكل وصف OpenAPI

(2) ▶ مثال: قيود الأرقام في Path()

PYTHON
from fastapi import FastAPI, Path

app = FastAPI()

@app.get("/products/{product_id}")
async def get_product(
    product_id: int = Path(
        gt=0,
        le=1000000,
        description="معرّف المنتج: عدد صحيح موجب، الحد الأقصى مليون",
    ),
):
    return {"product_id": product_id}

الناتج:

TEXT
INFO:     127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK

Output (/products/-1 يُرجع 422):

TEXT
{
  "detail": [
    {
      "loc": ["path", "product_id"],
      "msg": "Input should be greater than 0",
      "type": "greater_than"
    }
  ]
}

4. شرح مفصّل لمعاملات الاستعلام

(1) معاملات الاستعلام الأساسية

معاملات الاستعلام هي أزواج المفتاح-القيمة التي تلي ? في URL. تُعرَّف كمعاملات دالة، والمعاملات ذات القيم الافتراضية تكون اختيارية.

نوع معامل الاستعلام طريقة التعريف مطلوب
مطلوب category: str نعم
اختياري (افتراضي) category: str = "all" لا
اختياري (None) `category: str None = None`

(1) ▶ مثال: أساسيات معاملات الاستعلام

PYTHON
from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/prices")
async def search_prices(
    category: str | None = Query(None, max_length=50, description="فئة المنتج"),
    min_price: float = Query(0.0, ge=0, description="الحد الأدنى للسعر بالدولار"),
    max_price: float = Query(999999.0, le=999999, description="الحد الأقصى للسعر بالدولار"),
):
    return {
        "category": category,
        "price_range": f"${min_price} - ${max_price}",
    }

الناتج:

TEXT
INFO:     127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK

Output (/prices?category=electronics&min_price=10&max_price=500):

TEXT
{"category": "electronics", "price_range": "$10.0 - $500.0"}

(2) خيارات Query() المتقدمة

الخيار الوظيفة مثال
alias أسماء مستعارة للمعامل (مثل تحويل camelCase إلى snake_case) Query(alias="minPrice")
deprecated وضع علامة مهمل Query(deprecated=True)
title عنوان OpenAPI Query(title="Category Filter")
description وصف OpenAPI Query(description="...")
examples قيمة نموذجية Query(examples=["electronics"])

(2) ▶ مثال: alias وdeprecated

PYTHON
from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/products")
async def list_products(
    sort_by: str = Query(
        "name",
        alias="sortBy",
        description="حقل الترتيب: name، price، created_at",
    ),
    old_filter: str | None = Query(
        None,
        deprecated=True,
        description="استخدم sort_by بدلاً من ذلك",
    ),
):
    return {"sort_by": sort_by}

الناتج:

TEXT
# تم تعريف الدالة بنجاح

5. معاملات التعداد

(1) تقييد القيم الممكنة في تعداد سلسلة نصية

عندما لا يمكن أن يأخذ معامل إلا مجموعة ثابتة من القيم، استخدم قيد Enum، وسيعرض FastAPI تلقائياً قائمة منسدلة في التوثيق.

(1) ▶ مثال: تعداد معاملات المسار

PYTHON
from enum import Enum
from fastapi import FastAPI

class Category(str, Enum):
    electronics = "electronics"
    clothing = "clothing"
    food = "food"
    books = "books"

app = FastAPI()

@app.get("/categories/{category}")
async def get_category(category: Category):
    return {
        "category": category,
        "value": category.value,
        "label": category.name,
    }

الناتج:

TEXT
INFO:     127.0.0.1:50123 - "GET /api/items HTTP/1.1" 200 OK

Output (/categories/electronics):

TEXT
{"category": "electronics", "value": "electronics", "label": "electronics"}

(2) ▶ مثال: تعداد معاملات الاستعلام والترتيب

PYTHON
from enum import Enum
from fastapi import FastAPI, Query

class SortOrder(str, Enum):
    asc = "asc"
    desc = "desc"

app = FastAPI()

@app.get("/products")
async def list_products(
    sort_order: SortOrder = Query(SortOrder.asc),
    limit: int = Query(20, ge=1, le=100),
    offset: int = Query(0, ge=0),
):
    return {
        "sort": sort_order.value,
        "limit": limit,
        "offset": offset,
    }

الناتج:

TEXT
# تم تعريف الدالة بنجاح

6. دمج المعاملات المتعددة والأخطاء الشائعة

(1) قواعد ترتيب تعريف المعاملات

قواعد FastAPI لتحديد أنواع المعاملات: أي {param} في المسار هو معامل مسار؛ وإلا فهو معامل استعلام (المعاملات ذات تعليقات الأنواع تكون مطلوبة، وتلك ذات القيم الافتراضية تكون اختيارية).

الترتيب نوع المعامل المعيار
1 معامل المسار URL يحتوي على {param}
2 معاملات الاستعلام (مطلوبة) بدون قيمة افتراضية؛ غير مضمنة في المسار
3 معاملات الاستعلام (اختيارية) لها قيمة افتراضية أو None

(1) ▶ مثال: دمج معاملات متعددة — بحث منتجات PriceTracker

PYTHON
from fastapi import FastAPI, Path, Query
from enum import Enum

class Category(str, Enum):
    electronics = "electronics"
    clothing = "clothing"
    food = "food"

app = FastAPI()

@app.get("/products/{product_id}/prices")
async def get_product_prices(
    product_id: int = Path(gt=0, description="معرّف المنتج"),
    category: Category | None = Query(None, description="تصفية حسب الفئة"),
    min_price: float = Query(0.0, ge=0, description="الحد الأدنى للسعر بالدولار"),
    max_price: float = Query(99999.0, ge=0, description="الحد الأقصى للسعر بالدولار"),
    sort: str = Query("date", pattern="^(date|price)$"),
    limit: int = Query(20, ge=1, le=100),
    offset: int = Query(0, ge=0),
):
    return {
        "product_id": product_id,
        "category": category,
        "price_range": [min_price, max_price],
        "sort": sort,
        "pagination": {"limit": limit, "offset": offset},
    }

الناتج:

TEXT
# تم تعريف الدالة بنجاح

(2) أخطاء شائعة

الخطأ الصياغة الخاطئة الصياغة الصحيحة
معامل المسار اختياري product_id: int = None معامل المسار مطلوب دائماً
القيمة الافتراضية تتعارض مع Query limit: int = 20, Query(ge=1) limit: int = Query(20, ge=1)
المعاملات الاختيارية: None category: str = None `category: str
التعدادات لا تستخدم الفئة الأساسية str class Cat(Enum): class Cat(str, Enum):

7. مثال شامل

معاملات المسار ومعاملات الاستعلام وقيود التعداد هي أساس بناء واجهات API مرنة. فيما يلي ندمج قيود المسار وتصفح الاستعلام وتصفية التعداد.

PYTHON
from fastapi import FastAPI, Path, Query
from enum import Enum

app = FastAPI()

class SortOrder(str, Enum):
    asc = "asc"
    desc = "desc"

PRODUCTS = [{"id": i, "name": f"Product-{i}", "price": i * 10.0} for i in range(1, 101)]

@app.get("/products/{product_id}")
async def get_product(
    product_id: int = Path(gt=0, description="معرّف المنتج"),
    sort: SortOrder = Query(SortOrder.asc),
    limit: int = Query(10, ge=1, le=100),
    offset: int = Query(0, ge=0),
):
    return {
        "product_id": product_id,
        "sort": sort.value,
        "limit": limit,
        "offset": offset,
    }

الناتج:

TEXT
GET /products/5?sort=desc&limit=20&offset=10 → {"product_id":5,"sort":"desc","limit":20,"offset":10}
GET /products/0 → 422 Validation Error (product_id must be > 0)

❓أسئلة شائعة

س هل يمكن أن يكون لمعاملات المسار ومعاملات الاستعلام نفس الاسم؟
ج لا. سيطلق FastAPI خطأ لأنه لا يستطيع التمييز بين معاملات بنفس الاسم.
س كيف أجعل معامل استعلام مطلوباً؟
ج فقط لا توفّر قيمة افتراضية. مثلاً، category: str مطلوب، بينما category: str = "all" اختياري. يمكنك أيضاً وضع علامة صريحة على المعامل كمطلوب باستخدام Query(...).
س هل يمكن أن تكشف رسائل الخطأ التفصيلية (مثل خطأ 422) معلومات حساسة؟
ج رسائل الخطأ التفصيلية مفيدة أثناء التطوير. في بيئة الإنتاج، يمكنك استخدام معالج استثناءات مخصص لتبسيط الاستجابة وإرجاع "فشل التحقق من المعاملات" فقط.
س هل يمكن لمعاملات Enum قبول الأحرف الصغيرة؟
ج افتراضياً، حساسة لحالة الأحرف. إذا أردتها غير حساسة، يجب تعريف مُحقِّق مخصص أو استخدام أحرف صغيرة في قيم Enum.
س ما الفرق بين "gt" و"ge" في Path()؟
ج gt=0 يعني أن القيمة يجب أن تكون > 0 (باستثناء 0)، بينما ge=0 يعني >= 0 (بما في ذلك 0). معاملات نوع المعرّف تستخدم عموماً gt=0، ومعاملات نوع السعر تستخدم ge=0.
س كيف أقيّد طول معاملات الاستعلام؟
ج استخدم Query(max_length=N) لتقييد طول السلاسل النصية، وQuery(ge=N, le=M) لتقييد النطاقات العددية.

📖ملخص


📝تمارين

  1. مسألة أساسية (الصعوبة ⭐): أنشئ نقطة نهاية GET في /items/{item_id} تأخذ item_id (عدد صحيح موجب) كمدخل وتُرجع {"item_id": item_id}. تلميح: item_id: int = Path(gt=0)
  2. مسألة متقدمة (الصعوبة ⭐⭐): أنشئ نقطة نهاية استعلام /products لـ PriceTracker، تدعم ثلاثة معاملات استعلام: category (سلسلة نصية اختيارية، حتى 50 حرفاً)، min_price (≥0)، وmax_price (≤999999). تلميح: Query(None, max_length=50)
  3. مسألة تحدٍ (الصعوبة: ⭐⭐⭐): أنشئ نقطة نهاية /products/{product_id}/prices وباستخدام معاملات المسار (product_id > 0) ومعاملات استعلام التعداد (SortOrder: asc/desc) ومعاملات التصفح (limit 1-100، offset ≥ 0)، تحقق أن الإدخال غير الصالح يُرجع خطأ 422. تلميح: عرّف SortOrder(str, Enum) ومعاملات Query() متعددة.

---|

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%