~7 دقیقه مطالعه • بروزرسانی ۲۳ اسفند ۱۴۰۴
مقدمه
Middleware در جنگو یک سیستم افزونهای سطح پایین است که امکان تغییر جهانی ورودیها و خروجیهای برنامه را فراهم میکند. هر Middleware مسئول انجام یک وظیفه مشخص است. برای مثال، AuthenticationMiddleware کاربر را از طریق سشن به درخواست متصل میکند.
این مقاله نحوه کار Middleware، روش فعالسازی آن و نحوه نوشتن Middleware سفارشی را توضیح میدهد.
نوشتن Middleware سفارشی
یک Middleware در جنگو یک callable است که یک request دریافت کرده و یک response بازمیگرداند. Middleware میتواند به صورت تابع یا کلاس نوشته شود.
Middleware به صورت تابع
def simple_middleware(get_response):
# One-time configuration and initialization.
def middleware(request):
# Code before view is called.
response = get_response(request)
# Code after view is called.
return response
return middlewareMiddleware به صورت کلاس
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration.
def __call__(self, request):
# Code before view.
response = self.get_response(request)
# Code after view.
return responseget_response ممکن است ویو واقعی باشد یا Middleware بعدی در زنجیره. Middleware نیازی ندارد بداند دقیقاً چه چیزی در ادامه قرار دارد.
نکات مربوط به __init__
- فقط یک آرگومان
get_responseدریافت میکند. - فقط یکبار هنگام شروع سرور اجرا میشود.
غیرفعال کردن Middleware
اگر در __init__ استثنای MiddlewareNotUsed پرتاب شود، جنگو آن Middleware را حذف میکند.
فعالسازی Middleware
برای فعالسازی، مسیر کامل Middleware را در لیست MIDDLEWARE تنظیمات قرار دهید:
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]ترتیب Middleware بسیار مهم است، زیرا برخی از آنها به Middlewareهای قبلی وابسته هستند.
ترتیب اجرا و مدل لایهای
در مرحله درخواست، Middlewareها از بالا به پایین اجرا میشوند. در مرحله پاسخ، به ترتیب معکوس اجرا میشوند.
این ساختار مانند یک پیاز است: هر Middleware یک لایه است که درخواست را به لایه بعدی میفرستد و پاسخ را از لایه داخلی دریافت میکند.
متدهای ویژه در Middlewareهای کلاسی
متد process_view()
قبل از اجرای ویو فراخوانی میشود.
request: شیء درخواست.view_func: تابع ویو.view_args: آرگومانهای موقعیتی.view_kwargs: آرگومانهای کلیدی.
اگر None برگرداند، پردازش ادامه مییابد. اگر HttpResponse برگرداند، ویو اجرا نمیشود.
نکته: دسترسی به request.POST در این مرحله میتواند مانع تغییر upload_handlers شود.
متد process_exception()
وقتی ویو استثنا پرتاب کند، این متد فراخوانی میشود.
اگر HttpResponse برگرداند، همان پاسخ به مرورگر ارسال میشود. در غیر این صورت، هندلینگ پیشفرض اجرا میشود.
متد process_template_response()
اگر پاسخ یک TemplateResponse باشد، این متد پس از اجرای ویو فراخوانی میشود.
میتواند پاسخ را تغییر دهد یا یک پاسخ جدید ایجاد کند.
مدیریت پاسخهای استریمی
StreamingHttpResponse ویژگی content ندارد. بنابراین Middleware باید بررسی کند که پاسخ استریمی است یا خیر:
if response.streaming:
response.streaming_content = wrap_streaming_content(response.streaming_content)
else:
response.content = alter_content(response.content)محتوای استریمی ممکن است بسیار بزرگ باشد، بنابراین Middleware نباید آن را یکباره مصرف کند. تنها باید آن را wrap کند:
def wrap_streaming_content(content):
for chunk in content:
yield alter_content(chunk)جمعبندی
Middleware یکی از قدرتمندترین بخشهای معماری جنگو است که امکان کنترل کامل چرخه درخواست و پاسخ را فراهم میکند. با درک نحوه اجرای لایهای، متدهای ویژه و نحوه مدیریت پاسخهای استریمی، میتوان Middlewareهای سفارشی و کارآمدی برای نیازهای مختلف توسعه داد.
مدیریت پاسخهای استریمی
StreamingHttpResponse از هر دو نوع iterator همزمان و غیرهمزمان پشتیبانی میکند. بنابراین Middleware که قصد wrap کردن محتوای استریمی را دارد باید نوع iterator را تشخیص دهد. اگر Middleware شما باید هر دو حالت را پشتیبانی کند، از StreamingHttpResponse.is_async استفاده کنید.
مدیریت استثناها در Middleware
جنگو استثناهای ایجادشده توسط ویو یا Middleware را به پاسخ HTTP مناسب تبدیل میکند. استثناهای شناختهشده به کدهای 4xx تبدیل میشوند و استثناهای ناشناخته به 500.
این تبدیل قبل و بعد از هر Middleware انجام میشود، مانند یک لایه نازک بین لایههای «پیاز». بنابراین:
- Middleware همیشه یک
HttpResponseدریافت میکند، نه یک استثنا. - اگر Middleware بعدی
Http404پرتاب کند، Middleware شما فقط یک پاسخ باstatus_code = 404دریافت میکند.
برای غیرفعال کردن این تبدیل و مشاهده مستقیم استثناها، مقدار DEBUG_PROPAGATE_EXCEPTIONS را برابر True قرار دهید.
پشتیبانی از Async در Middleware
Middleware میتواند فقط همزمان، فقط غیرهمزمان یا ترکیبی باشد. جنگو درخواستها را برای سازگاری با Middleware تطبیق میدهد، اما این کار ممکن است باعث کاهش کارایی شود.
پرچمهای قابلیت
برای مشخص کردن نوع پشتیبانی، این ویژگیها را روی Middleware تنظیم کنید:
sync_capable: پیشفرضTrue.async_capable: پیشفرضFalse.
اگر هر دو مقدار True باشند، جنگو درخواست را بدون تبدیل ارسال میکند. برای تشخیص async بودن، بررسی کنید که آیا get_response یک coroutine است یا نه.
دکوراتورهای کمکی
جنگو دکوراتورهای زیر را برای تعیین قابلیتها ارائه میدهد:
sync_only_middleware()async_only_middleware()sync_and_async_middleware()
نمونه Middleware ترکیبی
from asgiref.sync import iscoroutinefunction
from django.utils.decorators import sync_and_async_middleware
@sync_and_async_middleware
def simple_middleware(get_response):
if iscoroutinefunction(get_response):
async def middleware(request):
response = await get_response(request)
return response
else:
def middleware(request):
response = get_response(request)
return response
return middlewareنکته: حتی اگر ویو async باشد، ممکن است Middleware شما در حالت sync فراخوانی شود اگر Middlewareهای همزمان بین شما و ویو وجود داشته باشند.
Middleware غیرهمزمان به صورت کلاس
from asgiref.sync import iscoroutinefunction, markcoroutinefunction
class AsyncMiddleware:
async_capable = True
sync_capable = False
def __init__(self, get_response):
self.get_response = get_response
if iscoroutinefunction(self.get_response):
markcoroutinefunction(self)
async def __call__(self, request):
response = await self.get_response(request)
return responseارتقای Middlewareهای قدیمی
جنگو برای سازگاری با Middlewareهای قبل از نسخه 1.10، کلاس MiddlewareMixin را ارائه میدهد.
قابلیتهای MiddlewareMixin
- متد
__init__()کهget_responseرا ذخیره میکند. - متد
__call__()که:
process_request() را اجرا میکند
get_response() را فراخوانی میکند
process_response() را اجرا میکنددر حالت MIDDLEWARE_CLASSES، متد __call__() نادیده گرفته میشود.
تفاوتهای رفتاری بین MIDDLEWARE و MIDDLEWARE_CLASSES
۱. جریان پاسخ
در MIDDLEWARE، فقط Middlewareهایی که درخواست را دیدهاند، پاسخ را نیز خواهند دید.
در MIDDLEWARE_CLASSES، همه process_responseها اجرا میشوند.
۲. مدیریت استثنا
در MIDDLEWARE، process_exception فقط استثناهای ویو را مدیریت میکند.
در MIDDLEWARE_CLASSES، استثناهای process_request نیز مدیریت میشوند.
۳. استثنا در process_response
در MIDDLEWARE_CLASSES، استثنا باعث پرش همه Middlewareهای قبلی و بازگشت 500 میشود.
در MIDDLEWARE، استثنا به پاسخ مناسب تبدیل شده و به Middleware بعدی ارسال میشود.
جمعبندی
Middleware در جنگو یک ابزار قدرتمند برای کنترل چرخه درخواست و پاسخ است. با درک رفتار استریمی، مدیریت استثنا، پشتیبانی از async و سازگاری با نسخههای قدیمی، میتوان Middlewareهای پیشرفته و بهینهای برای نیازهای مختلف توسعه ایجاد کرد.
نوشته و پژوهش شده توسط دکتر شاهین صیامی