前言
ASP.NET Core 中 HTTP 管道使用中间件组合处理的方式,
换句人话来说,
对于写代码的人而言, 一切皆中间件.
业务逻辑 / 数据访问 / 等等一切都需要以中间件的方式来呈现.
那么我们必须学会如何实现自定义中间件 这里划重点, 必考
这里我们介绍下中间件的几种实现方式...
匿名函数
通常新建一个空的 ASP.NET Core web Application https://my.oschina.net/taadis/blog/2396182 , 项目名字无所谓啦
在启动类里可以看到这么一句:
- // Startup.cs
- // ...
- App.Run(async (context) =>
- {
- await context.Response.WriteAsync("Hello World!");
- });
- // ...
这就是一个匿名函数实现的中间件, 虽然内容比较少.
可以看到通过匿名函数实现的中间件是内嵌在启动类文件中的, 因此通常也叫做内联中间件
接下来, 我们通过匿名函数来实现内联中间件, 以便加深理解.
新建一个空的 ASP.NET Core Web Application https://my.oschina.net/taadis/blog/2396182
然后修改启动类代码如下:
- // Startup.cs
- using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.AspNetCore.Http;
- using Microsoft.Extensions.DependencyInjection;
- using System;
- namespace WebApplication1
- {
- public class Startup
- {
- public void ConfigureServices(IServiceCollection services)
- {
- }
- public void Configure(IApplicationBuilder App, IHostingEnvironment env)
- {
- if (env.IsDevelopment())
- {
- App.UseDeveloperExceptionPage();
- }
- // 使用匿名函数实现一个内联中间件
- App.Use(async (context, next) =>
- {
- throw new NotImplementedException("一个使用匿名函数, 但未实现具体内容的内联中间件");
- });
- App.Run(async (context) =>
- {
- await context.Response.WriteAsync("Hello World!");
- });
- }
- }
- }
这里我们在 App.Run 之前使用 App.Use 添加一个匿名函数实现的内联中间件, 按照中间件的注册顺序, 当发起请求时, 会抛出一个异常 NotImplementedException("一个使用匿名函数, 但未实现具体内容的内联中间件")
我们 F5 启动下, 看看页面
嗯, 符合预期.
我们再来调整下启动类, 代码如下:
- using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.AspNetCore.Http;
- using Microsoft.Extensions.DependencyInjection;
- namespace WebApplication1
- {
- public class Startup
- {
- public void ConfigureServices(IServiceCollection services)
- {
- }
- public void Configure(IApplicationBuilder App, IHostingEnvironment env)
- {
- if (env.IsDevelopment())
- {
- App.UseDeveloperExceptionPage();
- }
- // 使用匿名函数实现一个内联中间件
- App.Use(async (context, next) =>
- {
- // 这里不对 request 做任何处理, 直接调用下一个中间件
- await next.Invoke();
- });
- App.Run(async (context) =>
- {
- await context.Response.WriteAsync("Hello World!");
- });
- }
- }
- }
这里我们在 App.Run 之前使用 App.Use 添加一个匿名函数实现的内联中间件, 该中间件没有对 request 做任何处理, 只是一个空的空间件, 按照中间件的注册顺序, 当发起请求时, 页面应该显示 Hello World!.
我们 F5 启动, 看看效果
嗯, 符合预期.
个人觉得: 匿名函数不是很直观, 但是用内联的方式可以快速开始一些开发, 不用新建一个中间件类, 不用专门想个不一样的名字, 小场景下是非常方便实用的
实现接口
通过实现接口 IMiddleware 编写自定义中间件, 这是一种强类型的方式, 我们需要必须强制按照接口的定义来实现.
IMiddleware
接口 IMiddleware 定义如下:
- using System.Threading.Tasks;
- namespace Microsoft.AspNetCore.Http
- {
- public interface IMiddleware
- {
- Task InvokeAsync(HttpContext context, RequestDelegate next);
- }
- }
可以看到接口 IMiddleware 的命名空间是 Microsoft.AspNetCore.Http, 需要实现的方法是 InvokeAsync(), 看起来不算太复杂, 嗯, 看起来不算太复杂
嗯, 重新开始, 我们新建一个空的 ASP.NET Core Web Application https://my.oschina.net/taadis/blog/2396182
然后我们通过实现接口的方式来自定义一个中间件, 代码如下:
- // 新建类 MyMiddleware.cs
- using Microsoft.AspNetCore.Http;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- namespace WebApplication1
- {
- public class MyMiddleware : IMiddleware
- {
- public Task InvokeAsync(HttpContext context, RequestDelegate next)
- {
- throw new NotImplementedException();
- }
- }
- }
按照上面实现的中间件 MyMiddleware, 在执行时应该会抛出 NotImplementedException.
使用接口实现的中间件需要在先在服务容器中注册
- // Startup.cs
- using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.AspNetCore.Http;
- using Microsoft.Extensions.DependencyInjection;
- namespace WebApplication1
- {
- public class Startup
- {
- public void ConfigureServices(IServiceCollection services)
- {
- // 在服务容器中注册自定义中间件
- services.AddSingleton<MyMiddleware>();
- }
- public void Configure(IApplicationBuilder App, IHostingEnvironment env)
- {
- if (env.IsDevelopment())
- {
- App.UseDeveloperExceptionPage();
- }
- // 使用 UseMiddleware() 把自定义中间件添加到管道中
- App.UseMiddleware<MyMiddleware>();
- App.Run(async (context) =>
- {
- await context.Response.WriteAsync("Hello World!");
- });
- }
- }
- }
然后 F5 启动, 页面上可以看到如下结果:
符合我们上面的预期, 抛出了一个 NotImplementedException.
然后我们改造下 MyMiddleware 中间件
- // MyMiddleware.cs
- using Microsoft.AspNetCore.Http;
- using System.Threading.Tasks;
- namespace WebApplication1
- {
- public class MyMiddleware : IMiddleware
- {
- public async Task InvokeAsync(HttpContext context, RequestDelegate next)
- {
- // 这里不对 request 做任何处理, 直接调用下一个中间件
- await next(context);
- }
- }
- }
这里相当于我们实现了一个叫做 MyMiddleware 的中间件, 但是并没有对请求进行任何处理, 页面上应该正常显示 Hello World! 字符串.
然后我们 F5 启动看看
嗯... 符合预期.
个人觉得: 这种方式最符合面向对象的特性, 也符合面向接口的原则, 少一些难以理解的魔法, 反而有助于理解.
约定方式
编程世界有这么一句话, 叫 "约定大于配置".
那么编写中间件的约定是什么呢?
重新开始, 新建一个空的 ASP.NET Core Web Application https://my.oschina.net/taadis/blog/2396182
然后新建一个类, 类名叫做 MyMiddleware 好了, 代码如下:
- // MyMiddleware.cs
- using Microsoft.AspNetCore.Http;
- using System;
- using System.Threading.Tasks;
- namespace WebApplication1
- {
- public class MyMiddleware
- {
- // 1. 需要实现一个构造函数, 参数为 RequestDelegate
- public MyMiddleware(RequestDelegate next)
- {
- }
- // 2. 需要实现一个叫做 InvokeAsync 方法
- public async Task InvokeAsync(HttpContext context)
- {
- throw new NotImplementedException("这是一个按照约定方式编写的中间件, 但未实现具体内容");
- }
- }
- }
约定的内容, 就是满足 2 个需要... 不满足需要则异常.
然后我们把这个中间件, 注册到管道中, 以便使用
- // Startup.cs
- using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.AspNetCore.Http;
- using Microsoft.Extensions.DependencyInjection;
- namespace WebApplication1
- {
- public class Startup
- {
- public void ConfigureServices(IServiceCollection services)
- {
- }
- public void Configure(IApplicationBuilder App, IHostingEnvironment env)
- {
- if (env.IsDevelopment())
- {
- App.UseDeveloperExceptionPage();
- }
- // 注册自定义中间件
- // 注册顺序 = 1
- App.UseMiddleware<MyMiddleware>();
- App.Run(async (context) =>
- {
- await context.Response.WriteAsync("Hello World!");
- });
- }
- }
- }
然后 F5 启动, 来看看效果
嗯, 符合预期.
然后我们来调整下中间件, 让请求能正常响应输出 Hello World!
- using Microsoft.AspNetCore.Http;
- using System;
- using System.Threading.Tasks;
- namespace WebApplication1
- {
- public class MyMiddleware
- {
- private readonly RequestDelegate _next;
- // 需要实现一个构造函数, 参数为 RequestDelegate
- public MyMiddleware(RequestDelegate next)
- {
- _next = next;
- }
- // 需要实现一个叫做 InvokeAsync 方法
- public async Task InvokeAsync(HttpContext context)
- {
- // 不处理任何 request, 直接调用下一个中间件
- await _next.Invoke(context);
- }
- }
- }
然后 F5 启动, 看看效果
嗯, 符合预期.
个人觉得: 只能说一句, 约定方式是目前用的最多的方式...
End
写在最后
Tips: 有些内容可能看起来还是不太容易理解, 至少当下你是很难理解的, 但是套路就在哪里, 好比 1+1=2, 你知道 1+1 为什么 = 2 么? 但你一定会算会用 1+1=2...
来源: https://www.cnblogs.com/taadis/p/12148859.html