如何一秒钟从头构建一个 ASP.NET Core 中间件

前言

其实地上本没有路,走的人多了,也便成了路。 -- 鲁迅

就像上面鲁迅说的那样,其实在我们开发中间件的过程中,微软并没有制定一些策略或者文档来约束你如何编写一个中间件程序, 但是其中却存在者一些最佳实践的方法,大多数人来使用这种方法来使应用程序变得更加容易理解并且易于维护,这就叫“路”,在2017年,这叫套路。

在掌握了这些套路之后,能够帮助你迅速的搭建一个中间件的基本框架,并且易于扩展和维护,下面我们就来看看怎么样从头开始开发一个中间件吧。

如果你对 ASP.NET Core HTTP 管道还不太清楚的话,下面这篇文章将有助于你对其进行一个系统的了解:
http://www.cnblogs.com/savorboard/p/aspnetcore-http-pipeline.html

Getting Started

说明: 这只是通常情况下,具体的情况还请使用具体的套路。

Setup 1 创建扩展类

如果你的中间件需要一个向 ASP.NET Core 的 DI 容器中添加默认的一些服务的话,那么你就编写一个需要扩展类,用来在 Startup.cs 中的 ConfigureServices 中注册服务。

举例,Microsoft.AspNetCore.ResponseCompression 这是一个用来压缩请求内容的一个中间件,那么它就需要一个服务用来处理压缩相关的东西,所以它扩展了 IServiceCollection 并且添加了自己的 Services。

整个中间件的核心代码并非在这里,这里只是一个开始,那么有同学可能会问了,什么情况下我们需要提前向一个DI里面注入我们中间件需要的服务呢? 答案是,如果你不知道或者不确定你需要什么样的服务的时候,跳过此步骤,进入下一步,再等你需要的时候再回头来补上就是。

那么,我们先看一下编写一个扩展Service的静态类应该怎么做?

首先,新建一个以 xxxServicesExtensions 文件名结尾的静态类,用来编写注入DI的扩展方法。

类建立完成之后,需要向里面添加内容了。通常情况下,中间件中 Service 的扩展方法都是以 Addxxx(this IServiceCollection services) 开头来命名。在这里有一个需要注意的地方就是它的命名空间,通常情况下我们使用 using Microsoft.AspNetCore.Builder 这个命名空间。

然后,方法里面就是需要注册的服务了。假设我们需要向里面注册一个 IResponseCompressionProvider 和 它的实现类 ResponseCompressionProvider,那么最终的扩展方法可能看起来是这样的。

using System;
using Microsoft.AspNetCore.ResponseCompression;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Microsoft.AspNetCore.Builder{  

 public static IServiceCollection AddResponseCompression(this IServiceCollection services)    {    
    if (services == null){          
     throw new ArgumentNullException(nameof(services));}services.TryAddSingleton<IResponseCompressionProvider, ResponseCompressionProvider>();    
        return services;} }

Setup 2 创建配置类

有的时候,用户在使用我们编写的中间件的时候,我们需要向提供者提供一些配置项,这些配置项在中间件执行之前用来传递一些必要参数信息或者是一些设置信息。举个我们熟悉例子,我们在使用 MVC 中间件的时候,可能会看到以下写法:

// Startup.cs
public void ConfigureServices(IServiceCollection services) {  var userDefinedFilter = new xxxFilter();services.AddMvc(x => x.Filters.Add(userDefinedFilter));}

可以看到,用户可将一些自定义的 Filter 传入到中间件的,然后中间件在运行的时候,我们传入的 Filter 就生效了。

注意,中间件使用的配置项有两种添加方法,一种是添加到 AddMiddleware(Action<xxxOptions> option) 另外一种是 UseMiddleware<>(Action<xxxOptions> option),那么这两种有什么区别呢?
那么,前者Add中的配置项一般情况下是中间执行之前就需要的一些信息,也就是说中间件的启动就依赖于这些配置项,他放置于容器配置(Add DI Service)的时候添加进去更加方便或者合适的时候使用它,另外一种(后者)是容器已经构建完毕,不需要依赖于容器提供的配置项可以使用此种方式。

同样的道理,当你自己为你的用户编写一个中间件的时候,当你也需要用户可以自定义一些配置或者需要传入一些参数的时候,你也可以这么做。那到底怎么样做呢? 我们一起来看看。

首先,我们需要一个 xxxOptions 结尾的配置类,用来配置我们中间件需要的一些配置项。我们还是以上面的压缩中间件举例。

public class GzipCompressionProviderOptions : IOptions<GzipCompressionProviderOptions>{ 
 public CompressionLevel Level { get; set; } = CompressionLevel.Fastest;  GzipCompressionProviderOptions IOptions<GzipCompressionProviderOptions>.Value => this; }

它其中配置了一个压缩的等级 CompressionLevel ,这是一个枚举字段。 然后我们可以看到,这个类它继承了 IOptions<out T> 接口,这是一个知识点,什么意思呢? IOptions<out TOptions> 是 ASP.NET Core 新的一个配置体系里面的一个接口,当你实现这个接口之后,ASP.NET Core DI 容器提供了了一个 services.Configure<xxxOptions> 这样的方法来让你把配置项注入到容器中,当然你也可以将配置项和 appsetting.json 中的配置关联起来,以便于配置一些在运行期可能需要变动信息。更多关于 IOptions<T> 的信息可以看 这里的翻译。

这个 xxxOptions 类通常情况下会提供一些默认值,也就是说当用户不提供这些参数的时候,你需要有一个合理的机制或者默认值来正常运行你的中间件。

假如你的配置项有很多,也就是说还有进一步比较细化的配置,那么你可以做一个封装,就像MVC的Options类一样,这样能够给你的中间件提供更加合理的维护和扩展。

Setup 3 核心中间件

接下来,就是我们的核心代码类了,通常情况下会有一个 xxxMiddleware 结尾的类用来处理 HTTP 管道请求中的一些业务,这个类的构造函数中已经可以使用在Setup1或者Setup2中向DI容器中注册的服务了。

按照约定,Middleware 类中需要有一个 Invoke 方法,用来处理中间件的核心业务,它的签名如下:

public Task Invoke(HttpContext httpContext);

注意,这是一个约定方法,并没有接口来约束它。在 Invoke 方法中,是中间件实现的核心代码。 示例如下:

public class xxxMiddleware{   
 private readonly RequestDelegate _next;    public xxxMiddleware(RequestDelegate next)    {    
   if (next == null){            throw new ArgumentNullException(nameof(next));} _next = next;}    public async Task Invoke(HttpContext context)    {       // ......await _next(context);       return;} }

在 xxxMiddleware 这个里面有一个构造函数参数 RequestDelegate,它是一个委托,代表的需要执行的下一个中间件,通常情况下我们会把它放到我们业务代码的末尾。

Setup 4 中间件扩展注册

中间件有三种注册方法(Run,Map,Use),我们暂不考虑Run和Map,因为他们只适用于很小和少的一些情况

完成了以上工作后,接下来,我们需要把中间件注册到我们的 ASP.NET Core 的执行,这个时候我们需要一个 xxxBuilderExtensions 类,它也是一个静态类,注意它的命名空间通常为
Microsoft.AspNetCore.Builder,因为这个用户在使用我们的中间件的时候就不必再添加额外的命令空间,依靠 Visual Studio 的智能提示就可以很快速的搜索到。我们来看一下示例:

using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Microsoft.AspNetCore.Builder{  
 public static class xxxBuilderExtensions{    
     public static IApplicationBuilder UseResponseCompression(this IApplicationBuilder builder)        {
                if (builder == null){              
                  throw new ArgumentNullException(nameof(builder));}          
                 return builder.UseMiddleware<xxxMiddleware>();}} }

Yeoman 一秒钟

有同学可能会说了,这些套路既然是这样的,那么有没有什么代码生成工具来帮我做这些事情呢?答案是肯定的。

博主已经帮你们把工具做好了,它使用的是当今最流行的脚手架工具 npm 中的 Yeoman 。使用它可以帮助你迅速的搭建一个中间件解决方案代码模板,让你专注于业务开发。

我已经把这个模板上传于 Yeoman 的仓库中,你只需要按照如下命令就可以帮你自动生成一套 ASP.NET Core 中间件解决方案代码模板了,当然单元测试也包含其中。

npm 工具的安装相信你自己可以的。下面是安装 Yeoman 工具和博主的模板工具。

// 安装 Yeoman 脚手架工具  -g 命令为全局安装npm install -g yo// 安装博主的 Yeoman(ASP.NET Core Middleware)模板npm install -g generator-aspnetcore-middleware

然后选择你需要生成解决方案的文件夹,使用如下命令生成。

yo aspnetcore-middleware

注意:生成的过程中需要输入你中间件的名称。按要求输入即可。

总结

本篇文章主要讲述了从头创建一个 ASP.NET Core 的流程,这适用于大多数场合,但是并不代表所有的场合,在实际开发的过程中还需要具体的考虑一下。接着博主提供了一个yo自动化脚手架模板用来快速创建一个中间件解决方案。

原文地址:http://www.cnblogs.com/savorboard/p/generator-aspnetcore-middleware.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/326704.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ssm(Spring+Spring mvc+mybatis)

在不久的之前我写了一篇ssh(SpringSpring mvchibernate)的文章&#xff0c;感兴趣的可以去这里看看~ 今天我来写一下ssm的增删改欧查案例&#xff1a; 数据库&#xff1a;mysql 浏览器&#xff1a;火狐浏览器 jdk&#xff1a;1.7 开发编辑器&#xff1a;myeclipse10.5 首…

C# winform程序免安装.net framework在XP/win7/win10环境运行

前文&#xff1a; 首先感谢群里的大神宇内流云 提供的anyexec for windows版本。 经过本人搭建虚拟机在xp环境 使用anyexec运行winfrom程序后&#xff0c;测试通过,如下是用的xp运行winfrom程序的部分截图 下面是n年前入坑C#写的winform。。。玩毒奶粉用的&#xff0c;勿喷。 .…

AVL树双旋转+图解

图解 代码实现 package com.atguigu.avl; /*** 创建人 wdl* 创建时间 2021/3/30* 描述*/ public class AVLTreeDemo {public static void main(String[] args) { // int[] arr{4,3,6,5,7,8};//创建一个AVLTree对象 // int arr[]{10,12,8,9,7,6};int[] arr{10,11…

[SSCore] 开源dotnet core 版本 SuperSocket

前言碎语 最近一直在做旧版本dotnet 程序迁移至dotnet core的工作, 非常欣慰dotnet社区的蓬勃发展, 目前大部分的第三方类库或开源代码都有了dotnet core版本 或者可以方便的找到替代方案. 这其中我唯一觉得遗憾的是dotnet 社区大名鼎鼎的socket 通讯框架SuperSocket SuperSock…

asp.net core mvc剖析:KestrelServer

KestrelServer是基于Libuv开发的高性能web服务器&#xff0c;那我们现在就来看一下它是如何工作的。在上一篇文章中提到了Program的Main方法&#xff0c;在这个方法里Build了一个WebHost&#xff0c;我们再来看一下代码&#xff1a; public static void Main( string [] args) …

win 7 mysql 1067_win7系统登陆MySQL服务出现1067错误的解决方法

很多小伙伴都遇到过win7系统登陆MySQL服务出现1067错误的困惑吧&#xff0c;一些朋友看过网上零散的win7系统登陆MySQL服务出现1067错误的处理方法&#xff0c;并没有完完全全明白win7系统登陆MySQL服务出现1067错误是如何解决的&#xff0c;今天小编准备了简单的解决办法&…

图的快速入门

快速入门案例 代码实现 package com.atguigu.graph;import java.util.ArrayList; import java.util.Arrays;/*** 创建人 wdl* 创建时间 2021/4/2* 描述*/ public class Graph {private ArrayList<String> vertexList;//存储顶点集合private int[][] edges;//存储图对应的…

.net core依赖注入的封装

现在流行的系统一般都采用依赖注入的实现方式&#xff0c;利用DI容器来直接获取所用到的类/接口的实例。.net core也一样采用DI的方式&#xff0c;提供了DI容器的接口IServiceCollection&#xff0c;并提供了基于该接口的缺省实现ServiceCollection。 这样我们就可以不再像以前…

图的深度优先遍历+图解

图解 代码实现 package com.atguigu.graph;import java.util.ArrayList; import java.util.Arrays;/*** 创建人 wdl* 创建时间 2021/4/2* 描述*/ public class Graph {private ArrayList<String> vertexList;//存储顶点集合private int[][] edges;//存储图对应的邻接矩阵…

Azure SQL的DTU和eDTU到底是个什么鬼

Azure SQL 使用了数据库事务单位 (DTU) 和弹性数据库事务单位 (eDTU)来作为一个计量单位。 但是DTU和eDTU是什么鬼啊? 官方文档这样解释 DTU 是一个资源度量单位&#xff0c;表示保证可用于单一数据库服务层内特定性能级别的单个 Azure SQL 数据库的资源。 DTU是一定比例的 C…

2015蓝桥杯省赛---java---B---1(三角形面积)

题目 三角形面积 解法 数学方法&#xff0c;直接求三角形的面积 88 - (82)/2 - (46)/2 - (84)/2 64 - (81216) 64 - 36 28 答案 28

深入理解Async/Await

C# 5 Async/Await 语法特性&#xff0c;极大地简化了异步编程&#xff0c;但我们知道&#xff0c;异步编程的基本原理并没有发生根本改变。也就是说&#xff0c;当一些复杂的东西看起来很简单时&#xff0c;它通常意味着有一些有趣的事情在背后发生。在计算机程序设计语言领域&…

2015蓝桥杯省赛---java---B---2(立方变自身)

题目 立方变自身 分析 简单枚举 i^3 99之后&#xff0c;数字越大&#xff0c;数字之和越不可能等于其自身。 代码 package com.atguigu.TEST;public class Demo01 {private static int ans;public static void main(String[] args) { // 6for (int i 1; i < 99; i) {…

【南京】.Net 开源基础服务线下技术交流会

南京地区的.net开发人员对基础服务这块感兴趣的&#xff0c;欢迎大家参加及会后继续交流&#xff0c;踊跃参与&#xff01;若对基础服务相关有深度技术交流的&#xff0c;后续交换联系方式&#xff0c;可一起深度合作。 .NET技术行业落地分享交流会 邀请南京地区.NET技术专家和…

mysql 语句块语法_MySQL ------ MySQL常用语句的语法 (三十四)

MySQL常用的语句语法注意&#xff1a;1、 | 符号用来指出几个选中中的一个&#xff0c;因此NULL | NOT NULL 表示给出null 或 not null2、包含在方括号中的关键字或子句是可选的(如 [like this])3、既没有列出所有的MySQL语句&#xff0c;也没有列出每一条子句和选项4、大写的表…

图的广度优先算法+遍历

图解 代码实现 package com.atguigu.graph;import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList;/*** 创建人 wdl* 创建时间 2021/4/2* 描述*/ public class Graph {private ArrayList<String> vertexList;//存储顶点集合private int[][…

.Net异步编程知多少

1. 引言 最近在学习Abp框架&#xff0c;发现Abp框架的很多Api都提供了同步异步两种写法。异步编程说起来&#xff0c;大家可能都会说异步编程性能好。但好在哪里&#xff0c;引入了什么问题&#xff0c;以及如何使用&#xff0c;想必也未必能答的上来。 自己对异步编程也不是很…

指纹识别开发1.0

在不久之前&#xff0c;用java和C#分别开发了个人脸识别&#xff0c;感觉挺不错的&#xff0c;于是脑袋一发热&#xff0c;想了想能不能搞个指纹识别&#xff0c;答案当然是能&#xff0c;那么问题来了&#xff0c;在人脸识别的时候可以借助自带摄像头提取你的face&#xff0c;…

DFS VS BFS

实际案例 代码实现 package com.atguigu.graph;import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList;/*** 创建人 wdl* 创建时间 2021/4/2* 描述*/ public class Graph {private ArrayList<String> vertexList;//存储顶点集合private in…

CLR运行时细节 - Method Descriptor

方法描述符:MethodDesc 运行时用来描述类型的托管方法,它保存在方法描述桶(MethodDescChunk)内;方法描述符保存了方法在运行时的一些重要信息:是否JIT编译;是否有方法表槽(决定了方法入口是跟在方法描述符(MethodDesc)后还是在方法表(MethodTable)后面);距离MethodDescChunk的索…