D-16 中介软件 ? middleware

自定义中介软件

昨天小光认识了路由,所以很好奇为什麽可以做到这样的功能,然後又回想老K说明的请求流水线这时发现了Custom Middlewares这个东西,所以一大早就跟大头请教这跟路由有甚麽关联。

本文同步放置於此

中介软件 Middleware

「前辈,路由跟中介软件有甚麽关系阿。」
一大早小光就迫不及待的问大头这个问题,然後今天大头似乎把手上的需求都做完了,所以很悠哉的在上网,所以一听到小光这麽说他就这样回答他。
「哈哈哈,你眼睛蛮锐利的喔,其实老K那天在说明请求流水线时讲的那些都是中介软件的一种,只不过是dotnetcore内建的中介软件。」
听到大头这样回答,小光思考了一下就这麽说。
「如果是这样的话,那如果我想要做每个请求都要做的事我可以自己定义一个中介软件吗?」
「哈哈哈,你想要自己做一个吗,那我们就来学习一下怎麽做一个自订义中介软件。」

中介软件的介绍

所以我们今天来学习一下如何自订义一个中介软件,但是在那之前我们再来看一次中介软件这篇文章,这篇文章内介绍的除了请求流水线之外还有介绍关於中介软件的处理方式,这里请先看下图。

MSDN 中介软件管线 MSDN 中介软件管线

就如同上次说明请求流水线时一样,请求会依序通过一层层中介软件,直到通过最後一个中介软件後才会到达Action的处理,待Action的处理完成後才会在一层层的返还到最後才会把反应回馈给使用者,如此就是中介软件的作用,接下来大家可以先看一下自订义一个中介软件这篇文章,然後下面我们会一步一步来学习如何自订义中介软件。

App.Use

首先是较简易的一个方法,就是直接在StartUp内加入中介层的逻辑,加入的方式如下。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.Use((context, next) =>
    {
        // 处理资料
	
        // 可以针对context做处理
        context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
		
        // 丢给下一个middleware或是进入MVC的action内
        return next();
    });
}

这边的Use可以一直串下去,第一个做完了会做下一个,如下列所示。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.Use((context, next) =>
    {
        context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
        return next();
    });
    
    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("Hello World!");
    });

    app.Use((context, next) =>
    {
        context.Response.Headers.Add("X-Xss-Protection", "1");
        return next();
    });
}

不过上述例子只是执行请求进去的行动,如果这边中介层要处理进去跟返还使用者的资讯时要如下所示。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("Middleware in. \r\n");
        await next.Invoke();
        await context.Response.WriteAsync("Middleware out. \r\n");
    });
}

App.Run

接下来要介绍的跟Use很类似,不同的地方是这个方法只会被执行一次,而且後续的动作也都不会执行,请看下列例子

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("Middleware in. \r\n");
        await next.Invoke();
        await context.Response.WriteAsync("Middleware out. \r\n");
    });

    app.Run(async context =>
    {
        await context.Response.WriteAsync("Hello from 2nd delegate.\r\n");
    });

    app.Use(async (context, next) =>
    {
        await context.Response.WriteAsync("Middleware Second in. \r\n");
        await next.Invoke();
        await context.Response.WriteAsync("Middleware Second out. \r\n");
    });
}

所以上述例子的结果会是如下

Middleware in. 
Hello from 2nd delegate.
Middleware out.  

而第二个Use的动作会完全因为Run而被取消了。

App.Map / App.MapWhen

接下来要处理的是Map跟MapWhen,这都是会因为使用特定的Url而进入的中介层,接下来让大家看下列的例子来说明两个的差异。

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // 针对特定url
    app.Map("/blog", myApp =>
    {
        myApp.Run(async context =>
        {
             await context.Response.WriteAsync("Hello World!");
        });
    });

    // 条件成立则执行
    app.MapWhen(context => context.Request.Query.ContainsKey("blog"), myApp =>
    {
        myApp.Run(async context =>
        {
             await context.Response.WriteAsync("Hello World!");
        });
    });
}

由上述例子可以知道两个的差别。

自订义中介层类别

自订义中介层类别可以分成两个步骤,第一个是宣告并实作类别,第二个是使用该类别,接下来分别说明。

宣告及实作

宣告及实作中介层类别的例子如下

public class CustomMiddleware
{
    private readonly RequestDelegate _next;

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

    public Task Invoke(HttpContext httpContext)
    {
        // 做某些事情
        if (!CheckSomething())
        {
            // 结束不再往下走
            return;
        }
         
        // 继续往下走
        return _next(httpContext);
    }
}

使用

使用自订义中介层类别的方式有两种,一种是注册全域的中介层类别,另一种是针对特定类别挂上属性attribute的方式。首先下列例子是注册全域的方式。

app.UseMiddleware<CustomMiddleware>();

这样所有的请求都会进到自订义的中介层。然後接下来看一下如何针对特定类别挂上属性的方式来使用中介层。

[MiddlewareFilter(typeof(ControllerMiddleware))]
public class HomeController : Controller
{
    [MiddlewareFilter(typeof(ActionMiddleware))]
    public IActionResult Index()
    {
        // ...
    }
}

上述例子中,进入此Controller的所有Action都会经过ControllerMiddleware中介层,而进入Index这个Action时会经过ActionMiddleware这个中介层。

扩充函式的方式

相信读者们应该看很多UseXXX的使用中介层的方式,这其实只是因应dotnetcore的习惯透过extension的方式来注册中介层,其实实际背後里是执行下列的动作。

public static class CustomMiddlewareExtensions
{
    public static IApplicationBuilder UseCustomMiddleware(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<CustomMiddleware>();
    }
}

如此在使用中介层也只需要使用下列方式

app.UseCustomMiddleware();

後记

今天小光跟大头学习到如何自订义一个中介软件,以及如何使用一个自订义的中介软件,并且认识了使用中介软件时有甚麽注意事项。


<<:  2021-Day24. Serverless(十 二): Amazon Elastic Container Registry

>>:  【Day 14】OSM 浅谈 part 2

Singleton 单例模式

首先,先来看看一个简单、特殊的创造物件的模式。 In software engineering, t...

DAY12 - 踩坑纪录 : Bitbucket

前言 今天是铁人赛的第十二天,内容是如何解决实作上发现的问题 自学的人如何解决问题,原本就是打算要写...

Day 16 留言是种互动!

好奇是知识的萌芽,萌芽之後,就要给予养分,让知识茁壮,没有养分的知识,只是一个没有办法萌芽的种子而已...

Day 08 借箸代筹(2):自动转型、运算子及其後

自动转型 续前文所述,当我们使用两个等号(==)作比较运算时,深受JavaScript「自动转型」的...

Day03 - Amazon ECS Anywhere 基础说明与建置(上)

这系列主要就是讲Amazon ECS Anywhere 所以先来看看阳春版怎麽建立出来 基础运行元件...