نحوه کار میانافزار (How Middleware Works)
پیشتر در کتاب مطلبی را گفتم که میخواهم در این فصل آن را گسترش دهم:
"میتوانید یک برنامه وب Go را به عنوان زنجیرهای از متدهای
ServeHTTP()در نظر بگیرید که یکی پس از دیگری فراخوانی میشوند."
در حال حاضر، در برنامه ما، وقتی سرور یک درخواست HTTP جدید دریافت میکند، متد ServeHTTP() مربوط به servemux را فراخوانی میکند. این متد بر اساس متد درخواست و مسیر URL، هندلر مربوطه را پیدا کرده و به نوبه خود متد ServeHTTP() آن هندلر را فراخوانی میکند.
ایده اصلی میانافزار، وارد کردن یک هندلر دیگر در این زنجیره است. هندلر میانافزار منطقی را اجرا میکند، مانند ثبت یک درخواست، و سپس متد ServeHTTP() هندلر بعدی را در زنجیره فراخوانی میکند.
در واقع، ما در حال حاضر از یک میانافزار در برنامهمان استفاده میکنیم - تابع http.StripPrefix() از سرو فایلهای استاتیک، که پیشوند خاصی را از مسیر URL درخواست قبل از ارسال درخواست به فایل سرور حذف میکند.
الگو
الگوی استاندارد برای ایجاد میانافزار خودتان به این شکل است:
func myMiddleware(next http.Handler) http.Handler { fn := func(w http.ResponseWriter, r *http.Request) { // TODO: Execute our middleware logic here... next.ServeHTTP(w, r) } return http.HandlerFunc(fn) }
کد خودش نسبتاً مختصر است، اما نکات زیادی برای درک در آن وجود دارد.
تابع
myMiddleware()اساساً یک wrapper برای هندلرnextاست که به عنوان پارامتر به آن منتقل میکنیم.این تابع یک تابع
fnرا ایجاد میکند که هندلرnextرا برای تشکیل یک closure در بر میگیرد. وقتیfnاجرا میشود، منطق میانافزار ما را اجرا کرده و سپس کنترل را به هندلرnextبا فراخوانی متدServeHTTP()آن منتقل میکند.صرف نظر از اینکه با closure چه میکنید، همیشه میتواند به متغیرهایی که در محدوده ایجاد شدهاش محلی هستند دسترسی داشته باشد - که در این مورد به این معنی است که
fnهمیشه به متغیرnextدسترسی خواهد داشت.در آخرین خط کد، ما این closure را به یک
http.Handlerتبدیل کرده و با استفاده از آداپتورhttp.HandlerFunc()آن را برمیگردانیم.
اگر این موضوع گیجکننده به نظر میرسد، میتوانید سادهتر به آن فکر کنید: myMiddleware() تابعی است که هندلر بعدی را در یک زنجیره به عنوان پارامتر میپذیرد. این تابع یک هندلر را برمیگرداند که منطقی را اجرا کرده و سپس هندلر بعدی را فراخوانی میکند.
سادهسازی میانافزار
یک تغییر در این الگو، استفاده از تابع بینام در داخل میانافزار myMiddleware() است، به این صورت:
func myMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // TODO: Execute our middleware logic here... next.ServeHTTP(w, r) }) }
این الگو در دنیای واقعی بسیار رایج است و احتمالاً اگر کد منبع برنامههای دیگر یا پکیجهای شخص ثالث را میخوانید، بیشتر با این الگو مواجه خواهید شد.
موقعیتدهی میانافزار
مهم است توضیح دهیم که موقعیت میانافزار در زنجیره هندلرها بر رفتار برنامه شما تأثیر خواهد گذاشت.
اگر میانافزار خود را قبل از servemux در زنجیره قرار دهید، روی هر درخواستی که برنامه شما دریافت میکند تأثیر خواهد گذاشت.
myMiddleware → servemux → application handler
مثال خوبی از جایی که این مورد مفید خواهد بود، میانافزار برای ثبت درخواستها است - زیرا این معمولاً چیزی است که میخواهید برای تمام درخواستها انجام دهید.
به طور جایگزین، میتوانید میانافزار را بعد از servemux در زنجیره قرار دهید - با پوشاندن یک هندلر برنامه خاص. این باعث میشود میانافزار شما فقط برای یک مسیر خاص اجرا شود.
servemux → myMiddleware → application handler
مثالی از این مورد میتواند میانافزار احراز هویت باشد که ممکن است بخواهید فقط روی مسیرهای خاصی اجرا شود.
همانطور که در طول کتاب پیش میرویم، نحوه انجام هر دوی این موارد را در عمل نشان خواهیم داد.
واژهنامه اصطلاحات فنی
| اصطلاح فارسی | معادل انگلیسی | توضیح |
|---|---|---|
| میانافزار | Middleware | کدی که بین درخواست و پاسخ HTTP اجرا میشود |
| تابع بستهبندی | Wrapper Function | تابعی که یک تابع دیگر را دریافت و تغییر میدهد |
| هندلر HTTP | HTTP Handler | تابعی که درخواستهای HTTP را پردازش میکند |
| میانافزار ثبت | Logging Middleware | میانافزاری که درخواستها را ثبت میکند |
| زنجیرههای میانافزار | Middleware Chains | ترکیب چندین میانافزار به صورت پشت سر هم |
| مسیریاب | Router | بخشی که درخواستها را به هندلرها هدایت میکند |
| پردازش درخواست | Request Processing | عملیات انجام شده روی درخواستهای دریافتی |
| پاسخدهنده | Response Writer | ابزاری برای نوشتن پاسخ HTTP |
| درخواستدهنده | Request | شیء حاوی اطلاعات درخواست HTTP |
| مدیریت مسیر | Route Handling | پردازش درخواستها بر اساس مسیر URL |