按部就班學.Net Core Web Api開發系列【13】:中間件(Middleware)

系列目錄html

按部就班學.Net Core Web Api開發系列目錄git

 本系列涉及到的源碼下載地址:https://github.com/seabluescn/Blog_WebApigithub

 

1、概述編程

本篇介紹如何使用中間件(Middleware)。緩存

 

2、初步演練服務器

先寫幾個中間件app

public class DemoAMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        public DemoAMiddleware(RequestDelegate next, ILogger<DemoAMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task Invoke(HttpContext context)
        {
            _logger.LogInformation("(1) DemoAMiddleware.Invoke front");  
            await _next(context);
            _logger.LogInformation("[1] DemoAMiddleware:Invoke back");
        }       
    }

  public class DemoBMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        public DemoBMiddleware(RequestDelegate next, ILogger<DemoBMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task Invoke(HttpContext context)
        {

            _logger.LogInformation("(2) DemoBMiddleware.Invoke part1");  
            await _next(context);
            _logger.LogInformation("[2] DemoBMiddleware:Invoke part2");
        }      
    }

    public class RequestRecordMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        public RequestRecordMiddleware(RequestDelegate next, ILogger<RequestRecordMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task Invoke(HttpContext context)
        {
            _logger.LogInformation("(3) RequestRecordMiddleware.Invoke");

            String URL = context.Request.Path.ToString();
            _logger.LogInformation($"URL : {URL}");            

            await _next(context);

            _logger.LogInformation("[3] RequestRecordMiddleware:Invoke next");
            _logger.LogInformation($"StatusCode : {context.Response.StatusCode}");
        }       
    }

以上中間件前兩個沒有作什麼正經工做,就打印一些日誌信息,第三個幹了一點工做,它打印了用戶輸入的url,同時打印了返回給客戶端的狀態碼。async

要使中間件工做,須要啓用中間件。ui

   public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {            
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            app.UseUnifyException();

            app.UseMiddleware<DemoAMiddleware>();
            app.UseMiddleware<DemoBMiddleware>();           
            app.UseMiddleware<RequestRecordMiddleware>();         

            app.UseStaticFiles();
            app.UseMvcWithDefaultRoute();
        }

經過擴展方法,咱們對中間件的啓用代碼進行改造:this

public static class RequestRecordMiddlewareExtensions
    {
        public static IApplicationBuilder UseRequestRecord(this IApplicationBuilder builder)
        {
            if (builder == null)
            {
                throw new ArgumentNullException("builder is null");
            }

            return builder.UseMiddleware<RequestRecordMiddleware>();
        }
    }

此時啓用代碼由:app.UseMiddleware<RequestRecordMiddleware>(); 

能夠修改成:   app.UseRequestRecord();

實現效果沒有變化。可見下面代碼都是中間件的使用。

app.UseStaticFiles();
app.UseMvcWithDefaultRoute();

  

個人理解,中間件相似車間流水線上的工人,操做的零件就是HttpContext,每一個人負責兩道工序,我把它們定義爲「前道工序」和「後道工序」,經過代碼 _next(context); 把兩道工序隔離開,處理的順序須要特別注意,按照中間件註冊的順序依次處理「前道工序」,處理完成後,再按相反的順序處理「後道工序」,若是某個中間件沒有調用_next(context),那麼就不會調用後續的中間件,因此中間件啓用的順序是須要特別考慮的。

以上代碼中三個中間件輸出到控制檯的信息順序以下:

(1)

(2)

(3)

【3】

【2】

【1】

我的認爲,「前道工序」應重點處理Request,「後道工序」應重點處理Response。

 

3、作一個相似MVC的中間件

咱們作一箇中間件,讓其返回一些內容給客戶端:

public class MyMvcMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        public MyMvcMiddleware(RequestDelegate next, ILogger<DemoAMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task Invoke(HttpContext context)
        {  var str = "hello,world!";
            await  context.Response.WriteAsync(str);          
        }       
    }

這個中間件只是返回固定的字符串,咱們還能夠調用某個Controller的提供的方法。

   public class MyMvcMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        public MyMvcMiddleware(RequestDelegate next, ILogger<DemoAMiddleware> logger)
        {
            _next = next;
            _logger = logger;
        }

        public async Task Invoke(HttpContext context)
        {
var obj = context.RequestServices.GetRequiredService<ArticleController>().GetArticleList(); var str = JsonConvert.SerializeObject(obj); await context.Response.WriteAsync(str); } }
ArticleController的定義以下:
    public class ArticleController : Controller
    {
        private readonly SalesContext _context;
        private readonly ILogger _logger;
        private readonly IMemoryCache _cache;

        public ArticleController(SalesContext context, ILogger<ArticleController> logger, IMemoryCache memoryCache)
        {
            _context = context;
            _logger = logger;
            _cache = memoryCache;
        }

        [HttpGet]
        public ResultObject GetArticleList()
        {
            _logger.LogInformation("==GetArticleList==");

            List<Article> articles = _context.Articles
                .AsNoTracking()
                .ToList();

            return new ResultObject
            {               
                result = articles
            };
        }  
    }

要在中間件中經過依賴使用該Controller,須要將其註冊到DI容器:

 public void ConfigureServices(IServiceCollection services)
{ 
      services.AddScoped<ArticleController>();
}

以上中間件實現了一個文章信息查詢的功能,若是在此中間件內先判斷路徑,再根據不一樣的路徑調用不一樣的Contorller提供的服務,就能夠造成一個簡單的MVC中間件了。

 

4、中間件的用途

 中間件的使用體現了AOP(面向切片)的編程思想,在不修改現有代碼的狀況下,經過增長一些中間件實現一些特定邏輯,能夠作的事情不少,好比:

URL重寫

緩存處理

異常處理

用戶認證

 

5、中間件的註冊順序

前文提到中間件的註冊順序是比較重要的,建議的順序以下:

1. 異常/錯誤處理2. 靜態文件服務器3. 身份驗證4. MVC

相關文章
相關標籤/搜索