Middlewares

המשך התוכנית ממאמר ההקדמה.

הופנקציה Run נכנסת למשפחת ה-Middlewares.

כל בקשה שמגיעה לשרת נכנסת דרך Middlewares שהתפקיד שלהם לבצע פעולות על הבקשה. כל אחד מבצע פעולה ואז נקרא ה-Middleware הבא.

למשל, אם אני רוצה שיהיה לי לוג כניסה ויציאה של בקשה שמגיעה אפשר לבנות Middleware שיכתוב ללוג בכניסה וביציאה. ואז אפשר גם לדעת כמה זמן לקחה הפעולה.

יש Middlewares קיימים, למשל של Auth.

Middlewares כותבים בקבצים נפרדים בצורה מסודרת. כאן בשביל הדוגמא נכתוב בקובץ program.

app.Use

app.Use מקבל callback שמקבל context ו-next. בתוך ה-callback נבצע פעולות כדי לראות את פעולת ה-middleware. השימוש ב-Run הוא בסוף של תהליך, Use הוא חלק מהתהליך, יש אחריו פעולות המשך.

קובץ program.cs

app.Use(async (context, next) => {
    await context.Response.WriteAsync("MID 1 Start  ");
    await next();
    await context.Response.WriteAsync("MID 1 End    ");
});

app.Use(async (context, next) => {
    await context.Response.WriteAsync("MID 2 Start  ");
    await next();
    await context.Response.WriteAsync("MID 2 End    ");
});

app.Run(async (context) => {
    await context.Response.WriteAsync("Hello World  ");
});

כל בקשה מתחילה לעבור תהליך, עוברת דרך ה-Middlewares וחוזרת דרכם. ה-Middleware הראשון מתחיל לעבוד, אחר כך יש את next שאומרת תפעיל את ה-Middleware הבא, יש לנו מעבר ל-Middleware השני, הבא יש לו גם next והוא יפעיל את ה-Run.

ה-Run סיים, יחזור ל-Middleware השני והוא יחזור לראשון. כל Middleware מקבל כפרמטר את ה-Middleware הבא והוא יכול לקבל החלטה האם להפעיל את ה-Middleware הבא או לא.

וזה מה שנראה:

הזדהות ו-auth זה משהו קלאסי לשימוש ב-Middleware, ככה יש לי אפשרות להעביר את כל הבקשות דרך האימות הזה.

Built-In Custom Middleware

נוסיף לפרויקט תיקייה בשם wwwroot, השם הוא מוסכמה לתיקייה של קבצים סטטיים. לתיקייה נוסיף new item של html page.

נוסיף כותרת כלשהי למסמך ונריץ את הפרוייקט.

נקבל את הטקסט שהדפסנו ב-middlewares, ואם נכניס את כתובת העמוד שלנו, שבמקרה הזה היא htmlpage.html, נקבל את אותו הדבר. לא נקבל את הדף, כי כרגע אין שום דבר בבקשה שלנו שמטפל בהצגת דף ה-html.

כדי לקבל את הדף נכניס בראש רשימת ה-middlewares את השימוש ב-staticfiles.

app.UseStaticFiles();

הפונקציה app.UseStaticFiles יודעת לטפל בהנגשת הקבצים הסטטיים כאשר הם נמצאים בתיקיית wwwroot. ואם עכשיו נריץ את הפרוייקט ונלך לעמוד ה-html שבנינו, נקבל את התוכן שלו.

אם נשים כתובת של קובץ שלא קיים, נחזור לראות את הדפסת ה-middlewares. מכיוון שלא היה קובץ, ה-UseStaticFiles ממשיך לבצע את מה שבא אחריו.

כל middleware יכול להחליט האם להתקדם ל-middleware או לא, ומכיוון שיש לנו דרך הלוך חזור דרכם, אנחנו יכולים ליצור middleware שיתפוס את השגיאות של כל האחרים שבדרך.

app.Use(async (context, next) => {
    try {
        await next();
    }
    catch {
        throw;
    }
});

Custom Middleware

כדי לכתוב middleware בצורה נכונה, נוסיף אותו לפרוייקט דרך קליק ימני על שם הפרוייקט -> add item ושם לבחור Middleware class. לתת שם ל-middleware. כדאי לרכז את כל ה-middlewares בתיקיית middlewares.

אנחנו מקבלים class מוכן לפעולה ופונקציית CustomMiddlewareExtensions כדי להוסיף את ה-Middleware בצורה פשוטה לתוכנית. כדי להוסיף למשל את ה-CustomMiddleware שיצרתי, כל מה שצריך זה:

קובץ program.cs

app.UseCustomMiddleware();

וצריך רק להוסיף לקובץ ה-global using את:

קובץ GlobalUsings.cs

global using FirstWebApp;

זה קובץ ה-middleware שנוצר. אנחנו מקבלים ב-constructor את ה-middleware הבא שאמורים להפעיל.

קובץ CustomMiddleware.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace FirstWebApp
{
    // You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
    public class CustomMiddleware
    {
        private readonly RequestDelegate _next;

        public CustomMiddleware(RequestDelegate next) {
            _next = next;
        }

        public Task Invoke(HttpContext httpContext) {

            return _next(httpContext);
        }
    }

    // Extension method used to add the middleware to the HTTP request pipeline.
    public static class CustomMiddlewareExtensions
    {
        public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder builder) {
            return builder.UseMiddleware<CustomMiddleware>();
        }
    }
}

ומה שיפעל עבור כל בקשה זאת הפונקציה invoke. כרגע היא רק קוראת ל-middleware הבא אבל יש לי אפשרות להוסיף פונקציונליות.

public Task Invoke(HttpContext httpContext) {
    return _next(httpContext);
}

נוסיף פעולה לפני ואחרי הקריאה הבאה:

public async Task Invoke(HttpContext httpContext) {
    await httpContext.Response.WriteAsync("Text from CustomMiddleware start ");
    await _next(httpContext);
    await httpContext.Response.WriteAsync("Text from CustomMiddleware end ");
}

ונקבל את התוצאה הזאת:

ברור שיש חשיבות לסדר שבו אנחנו קוראים ל-middlewares.

Custom header with Middleware

נראה דוגמא של שימוש ב-middleware כדי לשנות את נ-header של הדף. נניח שאני רוצה לכל בקשה חוזרת להוסיף תוכן ל-header.

public class CustomMiddleware
{
    private readonly RequestDelegate _next;

    public CustomMiddleware(RequestDelegate next) {
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext) {
        httpContext.Response.Headers.Add("TestHeaserKey", DateTime.Now.ToLongTimeString());
        await httpContext.Response.WriteAsync("Text from CustomMiddleware start ");
        await _next(httpContext);
        await httpContext.Response.WriteAsync("Text from CustomMiddleware end ");
    }
}

לכל דף שיעלה עכשיו יתווסף ה-header שהוספנו.

כמו שהוספנו header אפשר גם להסיר לא רצויים.

public Task Invoke(HttpContext httpContext) {
    var headersToRemove = new string[] { "Powered-by", "hosting-server" };
    foreach (var headerkey in headersToRemove) {
        httpContext.Response.Headers.Remove(headerkey);
    }
    return _next(httpContext);
}

ניווט במאמר

מאמרים אחרונים

Weekly Tutorial