.NET Core第4天_middleware是舍麽?

中介软件为组成应用程序管线的软件,用以处理要求与回应,
.net core中定义的中介则可以说是用来取代以前HTTP Handler(HTTP 处理常式)新技术。
ASP.NET Core 采用Request Pipeline的模型设计,
要求管线由要求委派序列组成,并会一个接着一个呼叫。
下图说明此概念。 执行绪遵循黑色箭号执行。
https://ithelp.ithome.com.tw/upload/images/20210904/20107452yGkeEowQ22.png

也可以白话说就是用来重应用程序管线来处理请求、回应的元件,用Pipeline 来
比喻Middleware的模型就是强调各项Middleware串联在一起。
每次的Request跟Response都会像大队接力一样,传送至下一棒。
也像跑电子公文单审核经费超过特定门槛是否往上加签的概念
(类似设计模式中的Chain of Responsibility Pattern责任链模式)
管线内每一元件都可去抉择是否将request交给下一元件,并且在管线中呼叫下一元件之前
和之後执行某些业务逻辑功能。

和Http Module差异在於
HttpModules 是透过Web.config或global.asax来做配置,无法让人透过程序来控制,且执行顺序是基於Application life cycle events,每次Request、Response都为固定的。
开发者只能在特定Event去写程序,不可更改违背其本身设计原则。

至於Middleware 则完全是透过Startup.cs code 而非config档案来配置
开发人员有更大弹性去自行设计想要做的启动配置流程。

https://ithelp.ithome.com.tw/upload/images/20210904/20107452ghvszkVFZd.png

请求委派被创建来建立请求管线,请求委派负责处里每个HTTP Request,
主要透过IApplicationBuilder 型别的Run , Map , 和Use扩充method进行设定。

通常Register这些Middleware方式都是於Startup.cs 的 Configure
对 IApplicationBuilder 型别的app使用 Use方法

public static Microsoft.AspNetCore.Builder.IApplicationBuilder Use (this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Func<Microsoft.AspNetCore.Http.HttpContext,Func<System.Threading.Tasks.Task>,System.Threading.Tasks.Task> middleware);

每个单独的请求委派可以透过匿名方法内嵌方式或者定义一个可以ReUse的Class中
至於这些可被ReUse的Class就是Middleware了

每个位於Request Pipeline中的Middleware负责接力(呼叫下一个元件)或者适时短路(不往下呼叫)。

内建扩充的普遍以 Use 开头的方法注册
预设用MVC template建立的.net core 专案
就有用到
app.UseStaticFiles(); //启用静态档案存取
app.UseRouting(); //启用路由
app.UseAuthorization(); //身分授权
app.UseEndpoints();//启用路由端点
这麽多内建扩充的Middleware
其他还有像是
app.UseAuthentication();//身分验证
app.UseSession();//启用Session
app.UseHttpsRedirect();//http转向https
都是属於内建的middleware

https://ithelp.ithome.com.tw/upload/images/20210904/20107452PEekqhzMQp.png

这里自行写一段用Use方法的测试
在初始脚本
Test Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace MyCore0
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //注册 Middleware 的方法
            app.Use(async (context, next) =>
            {
                await context.Response.WriteAsync("1st Middleware in. \r\n");
                await next.Invoke();//跳至下一个Middleware的方法
                await context.Response.WriteAsync("1st Middleware out. \r\n");
            });

            app.Use(async (context, next) =>
            {
                await context.Response.WriteAsync("2nd Middleware in. \r\n");
                await next.Invoke();//跳至下一个Middleware的方法
                await context.Response.WriteAsync("2nd Middleware out. \r\n");
            });

            //注册 Middleware 的方法,和Use差别在於不会依序执行下一个 Middleware。
            app.Run(async (context) =>
            {
                await context.Response.WriteAsync("Test 1 \r\n");
            });

            app.Use(async (context, next) =>
            {
                await context.Response.WriteAsync("3rd Middleware in. \r\n");
                await next.Invoke();//跳至下一个Middleware的方法
                await context.Response.WriteAsync("3rd Middleware out. \r\n");
            });

            //注册 Middleware 的方法,和Use差别在於不会依序执行下一个 Middleware。
            //app.Run(async (context) =>
            //{
            //    await context.Response.WriteAsync("Test 1 \r\n");
            //});

            //if (env.IsDevelopment())
            //{
            //    app.UseDeveloperExceptionPage();
            //}

            //app.UseRouting();

            //app.UseEndpoints(endpoints =>
            //{
            //    endpoints.MapGet("/", async context =>
            //    {
            //        await context.Response.WriteAsync("Hello World!");
            //    });
            //});
        }
    }
}

执行结果
https://ithelp.ithome.com.tw/upload/images/20210904/20107452XCQwcYUlaF.png

这里可以看到第三个Middleware没有执行到主要原因是前面呼叫到
IApplicationBuilder.Run()会造成管线短路。
此外也可观察到注册顺序的重要性,资料传递顺序是先进後出。

IApplicationBuilder.Run():
Adds a terminal middleware delegate to the application's request pipeline.

public static void Run(this IApplicationBuilder app, RequestDelegate handler)

https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.runextensions.run?view=aspnetcore-3.1

RequestDelegate Delegate
A function that can process an HTTP request.

public delegate Task RequestDelegate(HttpContext context);

https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.requestdelegate?view=aspnetcore-3.1

IApplicationBuilder.Run() 跟IApplicationBuilder.Use()最大差异就在於
Run方法会造成管线短路(因为并未需要传入next的请求委派)
所以通常Run只在管线最底部被调用。

但是Use方法中即便会传入next但只要在方法里头根本没使用到next也等同於Run!!!

在这里练习自己写自订的Middleware
(PS:我们在Startup.cs 中Configure 注册的 Middleware
属於Global范围注册可套用到所有的 Request。)

MyCustomMiddleware.cs:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MyCore0
{
    public class MyCustomMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;

        public MyCustomMiddleware(RequestDelegate next, ILoggerFactory logFactory)
        {
            _next = next;
            _logger = logFactory.CreateLogger("MyCustomMiddleware");
        }

        public async Task Invoke(HttpContext httpContext)
        {
            _logger.LogInformation("MyCustomMiddleware executing...");
            await _next(httpContext);
        }
    }    
}

MyCustomMiddlewareExtensions.cs:

using Microsoft.AspNetCore.Builder;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace MyCore0
{
    public static class MyCustomMiddlewareExtensions
    {
        public static IApplicationBuilder UseMyCustomMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MyCustomMiddleware>();
        }
    }
}

Startup.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace MyCore0
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            //app.UseMiddleware<MyCustomMiddleware>();//写法1.不用Extension method直接用内建的

            app.UseMyCustomMiddleware();//写法2.用Extension method

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

会看到在终端输出也有跑我们自订的内容
https://ithelp.ithome.com.tw/upload/images/20210904/20107452sBZpGiKput.png

在此我想看IP跟port资讯是舍麽
https://ithelp.ithome.com.tw/upload/images/20210904/20107452ZDZ5mHIsea.png

也可以输出~~自行客制化

在此还要特别注意
中介软件顺序
https://ithelp.ithome.com.tw/upload/images/20210904/201074527TW0tchUxU.png

写成是顺序要特别留意
比方Authentication一定要在Authorization之上。

Ref:
ASP.NET Core 中介软件
https://docs.microsoft.com/zh-tw/aspnet/core/fundamentals/middleware/?view=aspnetcore-5.0

ASP.NET Core - Middleware
https://www.tutorialsteacher.com/core/aspnet-core-middleware

ASP.NET Core 中介软件
https://docs.microsoft.com/zh-tw/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1

ASP.NET Core 基础 - Middleware
https://blog.darkthread.net/blog/aspnetcore-middleware-lab/

[Day03] ASP.NET Core 2 系列 - Middleware
https://ithelp.ithome.com.tw/articles/10192682

ASP.NET - 使用自订中介软件侦测与修正 ASP.NET Core 应用程序中的 404
https://docs.microsoft.com/zh-tw/archive/msdn-magazine/2016/june/asp-net-use-custom-middleware-to-detect-and-fix-404s-in-asp-net-core-apps

[铁人赛Day04] - 浅谈Middleware
https://ithelp.ithome.com.tw/articles/10203041

What is the difference between IApplicationBuilder.Use() and IApplicationBuilder.Run() C# Asp.net Core?
https://www.tutorialspoint.com/what-is-the-difference-between-iapplicationbuilder-use-and-iapplicationbuilder-run-chash-asp-net-core

How C# ASP.NET Core Middleware is different from HttpModule?
https://www.tutorialspoint.com/how-chash-asp-net-core-middleware-is-different-from-httpmodule

Asp.net HttpHandler vs HttpModule 详细解说
https://isdaniel.github.io/HttpHandler-HttpModule/
https://isdaniel.github.io/Ithelp-day2/

已同步发表至个人部落格
https://coolmandiary.blogspot.com/2020/11/net-coremiddleware.html


<<:  .Net Core Web Api_笔记03_HTTP资源操作模式POST

>>:  Day-4 Excel消失的0在哪里?

每天来点 Vue.js 吧 目录&总结

tags: Vuejs 本次铁人作为进入 Vue 3 的前的粗浅回顾,简单介绍了 Vue 2 官网的...

【Day 6】Replication

决定要拆章节了, 这篇只有 5.1, 5.2 5.3 放明天,因为我好累。 这章会提到 replic...

[Day 7] -『 GO语言学习笔记』- 指标函式设计 & 常数(constants)

以下笔记摘录自『 The Go Workshop 』。 采用指标的函式设计 如果变数是指标,或者传递...

Proxmox VE 网路进阶设定 (Bridge、LACP、VLAN)

在规模较大的企业网路中,为了避免单点故障会采用 LACP 的方式将多条线路聚合在一起使用,除了增加...

Day26 - 用 Ruby on Rails 写分析股票的技术指标

前言 在做选股之前,可透过技术指标来分析,像是可以用 KD、均线、量价...等 说明 这边要申明下,...