oruji.github.io
oruji.github.ioPersian Tutorials
ویرایش: 1396/11/13 19:21
A A

آموزش Middleware در جنگو (Django)

در مواردی، نیاز به اجرای یک قطعه کد در هر درخواستی که جنگو کنترل می کند خواهید داشت. این کد ممکن است نیاز باشد درخواست را قبل از آن که view آن را کنترل کند، ویرایش کند. این کد ممکن است نیاز باشد اطلاعات درباره ی درخواست برای اهداف debug و غیره ثبت کند.

می توان این کار را با فریم ورک یا چارچوب middleware انجام داد، که مجموعه ای از hook ها درون پردازش request/response می باشند. این چارچوب یک سیستم "plug-in" سطح پایین و light قادر به تغییر سراسری ورودی و خروجی جنگو (Django) می باشد.

هر جزء middleware مسئول انجام برخی کارهای خاص می باشد. در صورتی که این کتاب را کامل خوانده باشید، middleware را بارها و بارها دیده اید:

این آموزش از کتاب، ماهیت middleware و نحوه ی کارکرد آن را به طور عمیق تر بحث کرده و نحوه ی نوشتن middleware خودتان را توضیح می دهد.

Middleware چیست؟

اجازه دهید یا یک مثال خیلی ساده شروع کنیم.

سایت های پر ترافیک اغلب نیاز به قرار دادن جنگو در پشت یک پروکسی load-balancing دارند. این می تواند موجب پیچیدگی های کوچکی شود و یکی از این پیچیدگی ها این است که IP از راه دور هر درخواست (request.META["REMOTE_IP"]) متعادل کننده ی بار آن خواهد بود، نه در واقع IP ای که request ایجاد می کند. متعادل کننده های بار (load balancers) توسط یک header ویژه به نام X-Forwarded-For جهت درخواست آدرس IP واقعی با این موضوع سر و کار دارند.

(در دست ترجمه ...):

class SetRemoteAddrFromForwardedFor(object): def process_request(self, request): try: real_ip = request.META['HTTP_X_FORWARDED_FOR'] except KeyError: pass else: # HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs. # Take just the first one. real_ip = real_ip.split(",")[0] request.META['REMOTE_ADDR'] = real_ip

(توجه: اگر چه HTTP header با نام X-Forwarded-For وجود دارد، جنگو آن را به صورت request.META['HTTP_X_FORWARDED_FOR'] قابل دسترس می کند. به استثنای content-length و content-type، هر HTTP header ای درون درخواست با تبدیل تمام کاراکتر به حروف بزرگ، به کلید های request.META تبدیل می شود، به جای هر hyphen با خط تیره و یک پیشوند HTTP_ به نام آن.)

در صورتی که این middleware نصب شده باشد، مقدار هر X‑Forwarded‑For به طور خودکار درون request.META['REMOTE_ADDR'] درج می شود. این بدین معنی است که برنامه ی جنگوی شما نسبت به این موضوع که آیا آن ها پشت یک پروکسی load-blancing هستند یا خیر نگرانی نخواهد داشت، آن ها می توانند به سادگی به request.META['REMOT_ADDR'] دسترسی پیدا کنند و آن در صورتی که یک پروکسی استفاده شده باشد یا خیر کار خواهد کرد.

در واقع، این یک نیاز به اندازه ی کافی رایج می باشد که این قسمت از middleware در یک بخش داخلی جنگو (Django) باشد. آن در django.middleware.http قرار دارد.

نصب Middleware

در صورتی که این کتاب را به صورت کامل خوانده باشید، مثال هایی از نصب middleware را تاکنون مشاهده کرده اید؛ بسیاری از مثال ها در آموزش های قبلی کتاب، دارای بعضی middleware های ضروری می باشد. برای کامل کردن، در زیر نحوه ی نصب middleware وجود دارد.

جهت فعال کردن یک جزء middleware، آیتم یا جزء مورد نظر را به تاپی MIDDLEWARE_CLASSES موجود در ماژول تنظیمات (settings.py) اضافه کنید. درون MIDDLEWARE_CLASSES، هر جزء middleware توسط یک رشته نشان داده شده است: مسیر کامل نام کلاس middleware. برای مثال، در زیر MIDDLEWARE_CLASSES پیشفرض ساخته شده توسط django‑admin startproject وجود دارد:

MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', )

نصب جنگو نیاز به هیچ middleware ای ندارد – در صورت تمایل، MIDDLEWARE_CLASSES می تواند خالی باشد – ولی توصیه می شود که CommonMiddleware را که به طور مختصر توضیح می دهیم فعال کنید.

ترتیب قرار گیری مهم می باشد. درون درخواست و فازهای view، جنگو middlewre را با توجه به ترتیب داده شده ی آن ها در MIDDLEWARE_CLASSES بکار می برد، و در پاسخ و فازهای exception، جنگو middleware را با طور برعکس ترتیب بکار می برد. بدین معنی که، جنگو با MIDDLEWARE_CLASSES به صورت یک نوعی از "wrapper" در اطراف تابع view رفتار می کند: در درخواست به سمت پایین لیست به view حرکت کرده، و در پاسخ بر می گردد.

متدهای Middleware

اکنون که ماهیت middleware و نحوه عمکرد آن را می دانید، اجازه دهید به تمام متدهایی که کلاس های middleware می توانند تعریف کنند نگاهی بیاندازیم.

مقدار ده اولیه: __init__(self)

از __init__() جهت راه اندازی systemwide برای یک کلاس middleware داده شده استفاده کنید.

(در دست ترجمه ...)، هر کلاس middleware فعال شده ای تنها یک بار در هر پردازش سرور instantiated شده است. این بدین معنی است که __init__() تنها یک بار فراخونی می شود – در زمان راه اندازی سرور – نه برای درخواست های فردی.

دلیل رایج برای اجرای یک متد __init__() بررسی این موضوع می باشد که middleware واقعا ضروری می باشد یا خیر. در صورتی که __init__()، django.core.exceptions.MiddlewareNotUsed را بوجود آورد، در آن هنگام جنگو middleware را از توده ی middleware حذف خواهد کرد. ممکن است از خصوصیت برای بررسی برخی قسمت های نرم افزار که کلاس middleware ضروری می باشد استفاده کنید، یا بررسی این که آیا سرور در حالت debug اجرا می شود یا خیر، (در دست ترجمه ...).

در صورتی که یک کلاس middleware یک متد __init__() تعریف کند، متد نباید هیچ آرگومانی بیشتر از self دریافت کند.

پردازشگر درخواست: process_request(self, request)

این متد به محض این که درخواست دریافت شود فراخوانی می شود – قبل از آن که جنگو URL را برای تعیین view ای که باید اجرا شود تجزیه کند. این متد شیء HttpRequest را ارسال می کند، که شما ممکن است در صورت تمایل آن را اصلاح کنید.

process_request() باید یا یک شیء HttpResponse و یا None بر گرداند.

پردازشگر View: process_view(self, request, view, args, kwargs)

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

آرگومان های ارسال شده به این view در جدول شماره ی 1-17 نشان داده شده اند.

جدول

جدول شماره 1-2

آرگومانتوضیح
requestشیء HttpRequest.
viewتابع پایتون که جنگو برای کنترل این request فراخوانی خواهد کرد. این آرگومان واقعا خود شیء تابع می باشد، نه نام تابع به صورت یک رشته.
argsلیست آرگومان های موضعی ه به view ارسال خواهد شد، شامل آرگومان request نمی شود (که همواره اولین آرگومان view می باشد).
kwargsدیکشنری آرگومان های کیورد که به view ارسال خواهند شد

درست مثل process_request()، process_view() باید یک شیء HttpResponse یا None بر گرداند.

پردازشگر پاسخ: process_response(self, request,response)

این متد بعد از فراخوانی شدن تابع view و تولید شدن response فراخوانی می شود. در اینجا، پردازشگر محتوای یک پاسخ را تغییر داده یا اصلاح می کند. یک مورد استفاده ی آشکار، فشرده سازی محتوا می باشد، از قبیل gzip کردن HTML درخواست.

پارامترها باید کاملا واضح و آشکار باشند: request شیء درخواست می باشد، و response شیء پاسخ برگردانده شده از view است.

بر خلاف درخواست و پردازشگرهای view که ممکن است None برگردانند، process_response() باید یک شیء HttpResponse برگرداند. آن reponse می تواند همان اصل ارسال شده به تابع (احتمالا اصلاح شده) باشد یا یک نمونه ی جدید باشد.

پردازشگر خطا: process_exception(self, request,exception)

این متد تنها در صورتی که چیزی اشتباه شود و view یک خطای cache نشده ایجاد کند فراخوانی می شود. می توان از این hook برای ارسال تذکرهای خطا استفاده کرد،

پارامترهای این تابع همسان با شیء های request ای می باشد که تا کنون با آن سر و کار داشته ایم، و exception شیء واقعی Exception ایجاد توسط تابع view می باشد.

process_exception() باید یا یک None و یا یک شیء HttpResponse برگرداند.

http://code.djangoproject.com/wiki/ContributeMiddleware

Middleware داخلی

فریم ورک یا چارچوب جنگو (Django) برخی middleware های داخلی را که با مشکلات رایج سر و کار دارند را ارائه میکند، که در بخش های زیر بحث می کنیم.

Middleware پشتیبانی Authentication

کلاس Middleware: django.contrib.ath.middleware.AuthenticationMiddleware.

این middleware پشتیبانی authentication را فعال می کند. این middleware اتریبیوت request.user نشان داده شده در کاربر وارد شده ی فعلی به هر شیء HttpRequest ورودی اضافه می کند.

برای اطلاعات بیشتر به بخش کاربران عضویت و session مراجعه کنید.

"Common" Middleware

کلاس middleware: django.middleware.common.CommonMiddleware.

این middleware، تهسیلاتی برای افراد کمال گرا اضافه کرده است:

فشرده سازی Middleware

کلاس middleware: django.middleware.gzip.GZipMiddleware.

این middleware به طور خودکار محتوا را برای مرورگرهایی که فشرده سازی gzip را می فهمند (تمام مرورگرهای مدرن) فشرده می سازد. این middleware می تواند تا حد زیادی مقدار bandwidth ای که یک وب سرور مصرف می کند را کاهش دهد. در عوض مقداری زمان جهت فشرده سازی صفحات خواهد برد.

ما معمولا سرعت را bandwith ترجیح می دهیم، ولی در صورتی که شما بر عکس این را ترجیح می دهید، تنها کافیست این middleware را فعال کنید.

GET Middleware شرطی

کلاس middleware: django.middleware.http.ConditionalGetMiddleware.

این middleware اعمال شرطی GET را پشتیبانی می کند. در صورتی که پاسخ دارای یک Last-Modified یا Etag یا header باشد، و درخواست دارای If-None-Match یا If-Modified-Since باشد، پاسخ توسط یک پاسخ 304 ("Not modified") جایگزین می شود. پشتیبانی ETag وابسته به تنظیم USE_ETAGS می باشد و انتظار دارد که header پاسخ Etag قبلا قرار گرفته باشد. همانطور که در بالا بحث شد، هدر ETag توسط common middleware قرار گرفته است.

همچنین محتوای هر پاسخ به یک درخواست HEAD را نیز حذف می کند و هدرهای پاسخ DATE و Content-Length را برای هر درخواستی قرار می دهد.

پشتیبانی پروکسی معکوس (X-Forwarded-For Middleware)

کلاس middleware: django.middleware.http.SetRemoteAddrFromForwardedFor.

این مثالی است که در بخش "Middleware چیست؟" بررسی شد. این middleware، request.META['REMOTE_ADDR'] بر اساس request.META['HTTP_X_FORWARDED_FOR'] قرار می دهد، در صورتی که دومی قرار بگیرد. این در صورتی که در پشت یک پروکسی معکوس نشسته باشین که موجب شود هر REMOTE_ADDR درخواست به 127.0.0.1 قرار گرفته باشد مفید است.

Middleware پشتیبانی Session

کلاس middleware: django.contrib.sessions.middleware.SessionMiddleware.

این middleware پشتیبانی session را فعال می کند. برای جزئیات بیشتر به بخش کاربران عضویت و session مراجعه کنید.

Sitewide Cache Middleware

کلاس middleware: django.middleware.cache.UpdateCacheMiddleware و django.middleware.cache.FetchFromCacheMiddleware.

این middleware ها با یکدیگر برای cache هر صفحه ی تولید شده توسط جنگو کار می کنند. این موضوع به تفصیل در مبحث caching بحث شده است.

Transaction Middleware

کلاس middleware: django.middleware.transaction.TransactionMiddleware.

این middleware، COMMIT یا ROLLBACK پایگاه داده را به فاز request/response پیوند می دهد. در صورتی که یک تابع view با موفقیت اجرا شود، یک COMMIT صادر می شود. در صورتی که view یک خطا ایجاد کند، یک ROLLBACK صادر می شود.

ترتیب این middleware درون لیست اهمیت دارد. ماژول های middleware خارج از اجرای آن با commit‑on‑save اجرا می شوند – رفتار پیشفرض جنگو. ماژول های middleware داخل آن (دیرتر درون لیست می آیند) که در زیر کنترل transaction همسان به صورت توابع view خواهد بود اجرا می شوند.