شناخت و درک Middleware در ASP.NET Core

پنجشنبه 10 مهر 1399

در این مقاله به توضیح امکانات و مفاهیم Middleware در ASP.NET Core می پردازیم. در پایان این مقاله شما به درک روشنی از معانی زیر خواهید رسید:

شناخت و درک Middleware در ASP.NET Core

● Middleware  چیست؟

● چرا مرتب سازی Middleware مهم است؟

● درک نحوه اجرا، استفاده وMap Method

● چگونه می توان یک Middleware سفارشی ساخت؟

● چگونه می توان directory browsing را به وسیله Middleware فعال کرد؟

Middleware چیست؟

Middleware یک قطعه کد در application pipeline است که برای کنترل درخواست ها و پاسخ ها استفاده می شود.

به عنوان مثال، ممکن است یک middleware برای تایید یک کاربر، یک  middlewareبرای کنترل خطاها ویک  middlewareدیگربرای به کار گرفتن static file ها مانند فایل های JavaScript، فایل های CSS، تصاویر و... داشته باشیم.

Middleware می تواند به عنوان بخشی از چارچوب .NET Core تعبیه شود که از طریق NuGet packages اضافه می شود یا می تواند middleware سفارشی باشد. این کامپوننت‌های middleware به عنوان بخشی از startup class برنامه در configure method پیکربندی شده اند.  Configure methodها یک درخواست پردازش pipeline برای یک اپلیکیشن ASP.NET Core را تنظیم می کنند که شامل رشته ای ازدرخواست های delegate است که یکی پس از دیگری اجرا می شوند.

تصویر زیر نحوه پردازش یک درخواست از طریق اجزای middleware را نشان می دهد.

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

اما یک  middlewareمی تواند تصمیم بگیرد که قطعه بعدی middleware را در pipeline  فراخوانی نکند. آن را اتصال کوتاه (short-circuiting) یا خاتمه فرآیند پردازش درخواست (terminate the request  pipeline) می نامند.

بیایید یک برنامه ASP.NET Core Web ایجاد کنیم و پیکربندی پیش فرض middleware را در Configure method ی Startup class ببینیم.


    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)    
    {    
        if (env.IsDevelopment())    
        {    
            //This middleware is used reports app runtime errors in development environment.  
            app.UseDeveloperExceptionPage();    
        }    
        else    
        {    
            //This middleware is catches exceptions thrown in production environment.   
            app.UseExceptionHandler("/Error");   
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.    
            app.UseHsts(); //adds the Strict-Transport-Security header.    
        }    
        //This middleware is used to redirects HTTP requests to HTTPS.  
        app.UseHttpsRedirection();   
        
        //This middleware is used to returns static files and short-circuits further request processing.   
        app.UseStaticFiles();  
        
        //This middleware is used to route requests.   
        app.UseRouting();   
        
        //This middleware is used to authorizes a user to access secure resources.  
        app.UseAuthorization();    
        
        //This middleware is used to add Razor Pages endpoints to the request pipeline.    
        app.UseEndpoints(endpoints =>    
        {    
            endpoints.MapRazorPages();               
        });    
    } 

چارچوب ASP.NET Core برخی از کامپوننت های  middleware درونی را فراهم می کند که می توانیم به راحتی از آنها در Configure method استفاده کنیم.

ترتیب Middleware

کامپوننت های middleware به ترتیبِ اضافه شده به pipeline اجرامی شوند و باید مراقبت شود که middleware به ترتیب صحیح اضافه شود درغیر این صورت ممکن است برنامه مطابق انتظارعمل نکند. این ترتیب برای امنیت، کارایی و عملکرد بسیار مهم است.

کامپوننت های middleware زیر برای سناریوهای رایج برنامه به ترتیب توصیه شده اند:

اولین  middlewareپیکربندی شده درخواست را دریافت کرده، آن را اصلاح کرده (در صورت لزوم) و کنترل را به میان افزاربعدی منتقل می کند. به همین ترتیب، در صورت پردازش پاسخ، اگر echo به پایین tube بازگردد، اولین middleware در آخرین مرحله اجرا می شود. به همین دلیل است که Exception-handling delegateها اوایل pipeline فراخوانی می شوند، بنابراین می توانند نتیجه را اعتبارسنجی کنند و یک exception احتمالی را به روش مرورگر و مشتری پسند نشان دهند.

درک نحوه اجرا، استفاده و Map Method

()app.Run

این کامپوننت middleware قادر است متدهایRun[Middleware] ها را که در انتهای خط لوله اجرا می شوند، نشان دهد. به طورکلی، به عنوان یک terminal middleware عمل می کند و در انتهای فرآیند پردازش درخواست ((request  pipeline اضافه می شود، زیرا نمی تواند middleware بعدی را فراخوانی کند.

()app.Use

app.Use() برای پیکربندی multiple middleware استفاده می شود. بر خلاف ()app.Run، ما می توانیم پارامتر بعدی را درآن قراردهیم، که درخواست delegate بعدی را در pipeline فراخوانی می کند. ما همچنین می توانیم pipeline را با فراخوانی نکردن پارامتربعدی، اتصال کوتاه (terminate) کنیم.

مثال زیربا app.Use() وapp.Run() را بررسی کنید وهمچنین output/response را مشاهده کنید:


    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)    
    {    
        app.Use(async (context, next) =>    
        {    
            await context.Response.WriteAsync("Before Invoke from 1st app.Use()\n");    
            await next();    
            await context.Response.WriteAsync("After Invoke from 1st app.Use()\n");    
        });    
        
        app.Use(async (context, next) =>    
        {    
            await context.Response.WriteAsync("Before Invoke from 2nd app.Use()\n");    
            await next();    
            await context.Response.WriteAsync("After Invoke from 2nd app.Use()\n");    
        });    
        
        app.Run(async (context) =>    
        {    
            await context.Response.WriteAsync("Hello from 1st app.Run()\n");    
        });    
        
        // the following will never be executed    
        app.Run(async (context) =>    
        {    
            await context.Response.WriteAsync("Hello from 2nd app.Run()\n");    
        });    
    }    

اولین app.Run()،pipeline  را خاتمه می‌دهد. دراین مثال، فقط delegate اول  (“Hello from 1st app.Run()”) اجرا می شود و request هرگز به Run method دوم نمی رسد.

()app.Map

این اکستنشن ها به عنوان قراردادی برای سیستم چندخطی pipeline استفاده می شوند. map فرایند پردازش درخواست را براساس مسیر درخواست گسترش می دهد. اگر مسیر درخواست با مسیر داده شده شروع شود، شاخه جدید ازMiddleware ها اجرا خواهد شد. در حقیقت شما می توانید فرایند پردازش درخواست را به چند شاخه تقسیم کنید که بر اساس مسیر درخواست، هرکدام از این شاخه ها ممکن است اجرا شوند.

مثال زیر را با app.Map() بررسی کنید وهمچنین output/response را مشاهده کنید:


    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)  
    {  
        app.Map("/m1", HandleMapOne);  
        app.Map("/m2", appMap => {  
            appMap.Run(async context =>  
            {  
                await context.Response.WriteAsync("Hello from 2nd app.Map()");  
            });  
        });  
        app.Run(async (context) =>  
        {  
            await context.Response.WriteAsync("Hello from app.Run()");  
        });  
    }  
    private static void HandleMapOne(IApplicationBuilder app)  
    {  
        app.Run(async context =>  
        {  
            await context.Response.WriteAsync("Hello from 1st app.Map()");  
        });   
    }  

جدول زیر درخواست ها و پاسخ های localhost را با استفاده از کد بالا نشان می دهد.

ResponseRequest
()Hello from app.Runhttps://localhost:44362/
()Hello from 1st app.Maphttps://localhost:44362/m1
()Hello from 1st app.Maphttps://localhost:44362/m1/xyz
()Hello from 2nd app.Maphttps://localhost:44362/m2
()Hello from app.Runhttps://localhost:44362/m500

ساخت یک middleware سفارشی

Middleware به طور کلی در یک کلاس اینکپسوله شده و با اکستنشن متد در معرض دید قرار می گیرد. middleware سفارشی را می توان با یک کلاس به وسیله متدInvokeAsync()  و پارامتر نوع RequestDelegate در کانستراکتور ساخت. به منظور اجرای پشت سرهم middleware بعدی، نوع RequestDelegate لازم است.

بیایید مثالی را درنظر بگیریم که برای ثبت درخواست URL در یکweb application   نیاز به ایجاد middleware سفارشی داریم.


    public class LogURLMiddleware  
    {  
        private readonly RequestDelegate _next;  
        private readonly ILogger<LogURLMiddleware> _logger;  
        public LogURLMiddleware(RequestDelegate next, ILoggerFactory loggerFactory)  
        {  
            _next = next;  
            _logger = loggerFactory?.CreateLogger<LogURLMiddleware>() ??  
            throw new ArgumentNullException(nameof(loggerFactory));  
        }  
        public async Task InvokeAsync(HttpContext context)  
        {  
            _logger.LogInformation($"Request URL: {Microsoft.AspNetCore.Http.Extensions.UriHelper.GetDisplayUrl(context.Request)}");  
            await this._next(context);  
        }
    }

    public static class LogURLMiddlewareExtensions  
    {  
        public static IApplicationBuilder UseLogUrl(this IApplicationBuilder app)  
        {  
            return app.UseMiddleware<LogURLMiddleware>();  
        }  
    }  

در متد configure :

app.UseLogUrl();  

فعال کردن directory browsing به وسیله middleware

Directory browsing به کاربران برنامه وب شما اجازه می دهد که فهرست و فایل های دایرکتوری را در یک دایرکتوری مشخص مشاهده کنند.

directory browsing به دلایل امنیتی به طور پیش فرض غیرفعال است.

بیایید مثالی را در نظر بگیریم که می خواهیم لیستی از تصاویر موجود درمرورگررا در زیر پوشه images در wwwroot بپذیریم. میان افزار UseDirectoryBrowser می تواند آن تصاویر را برای آن request کنترل کرده و به آنها ارائه دهد و سپس بقیه pipeline را اتصال کوتاه کند.


    app.UseDirectoryBrowser(new DirectoryBrowserOptions  
    {  
        FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images")),  
        RequestPath = "/images"  
    });  

خلاصه

بنابراین Middleware ها در ASP.NET Core نحوه پاسخگویی برنامه ما به درخواستهای HTTP را کنترل می کنند.

به طور خلاصه، هرکامپوننت middleware در ASP.NET Core:

● هم به درخواست های ورودی و هم به پاسخ خروجی دسترسی دارد.

●  ممکن است تنها درخواست را به قسمت بعدی middleware موجود در pipeline منتقل کند.

● ممکن است برخی از منطق پردازش را انجام دهد و سپس این درخواست را برای پردازش بیشتر به middleware بعدی منتقل کند.

● هرزمان که لازم باشد ممکن است فرآیند پردازش درخواست را خاتمه دهد (اتصال کوتاه).

● به ترتیب اضافه شده به pipeline اجرا می شود.

امیدواریم که در مورد این موضوع اطلاعاتی کسب کرده باشید!

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان

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

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید