如何一秒钟从头构建一个 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,一经查实,立即删除!

相关文章

Spring Bean 定义继承

转载自 Spring Bean 定义继承 Bean 定义继承 bean 定义可以包含很多的配置信息&#xff0c;包括构造函数的参数&#xff0c;属性值&#xff0c;容器的具体信息例如初始化方法&#xff0c;静态工厂方法名&#xff0c;等等。 子 bean 的定义继承父定义的配置数据。子定义可以…

ssm(Spring+Spring mvc+mybatis)

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

grub shell 错误_使用grub-install修复Grub时出错

你的位置:问答吧-> Debian-> 问题详情使用grub-install修复Grub时出错WinXPDebian双系统&#xff0c;由于误操作导致开机后无法进入系统&#xff0c;错误信息为&#xff1a;GRUB Loading stage1.5.GRUB loading,please wait...error 22按照网上的说法&#xff0c;使用启动…

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

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

ssm(Spring+Spring mvc+mybatis)实体类——Dept

package org.entity;/**.* * * 项目名称&#xff1a;test_ssm_16qn3 * 类名称&#xff1a;Dept * 类描述&#xff1a; 部门表实体类 * 创建人&#xff1a;Mu Xiongxiong * 创建时间&#xff1a;2017-12-26 下午8:30:55 * 修改人&#xff1a;Mu Xiongxiong …

Spring 基于构造函数的依赖注入

转载自 Spring 基于构造函数的依赖注入 Spring 基于构造函数的依赖注入 当容器调用带有一组参数的类构造函数时&#xff0c;基于构造函数的 DI 就完成了&#xff0c;其中每个参数代表一个对其他类的依赖。 示例&#xff1a; 下面的例子显示了一个类 TextEditor&#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…

ssm(Spring+Spring mvc+mybatis)Dao接口——IDeptDao

package org.dao;import java.util.List;import org.entity.Dept;/*** * * 项目名称&#xff1a;test_ssm_16qn3 * 类名称&#xff1a;IDeptDao * 类描述&#xff1a; 部门表的接口 * 创建人&#xff1a;Mu Xiongxiong * 创建时间&#xff1a;2017-12-26 下午8:…

Spring 基于设值函数的依赖注入

Spring 基于设值函数的依赖注入 当容器调用一个无参的构造函数或一个无参的静态 factory 方法来初始化你的 bean 后&#xff0c;通过容器在你的 bean 上调用设值函数&#xff0c;基于设值函数的 DI 就完成了。 示例&#xff1a; 下述例子显示了一个类 TextEditor&#xff0c…

[SSCore] 开源dotnet core 版本 SuperSocket

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

字母图形

资源限制 时间限制&#xff1a;1.0s 内存限制&#xff1a;256.0MB 问题描述 利用字母可以组成一些美丽的图形&#xff0c;下面给出了一个例子&#xff1a; ABCDEFG BABCDEF CBABCDE DCBABCD EDCBABC 这是一个5行7列的图形&#xff0c;请找出这个图形的规律&#xff0c;并…

Spring 注入内部 Beans

转载自 Spring 注入内部 Beans 注入内部 Beans 正如你所知道的 Java 内部类是在其他类的范围内被定义的&#xff0c;同理&#xff0c;inner beans 是在其他 bean 的范围内定义的 bean。因此在 或 元素内 元素被称为内部bean&#xff0c;如下所示。 <?xml version"…

asp.net core mvc剖析:KestrelServer

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

ssm(Spring+Spring mvc+mybatis)Dao层实现类——DeptDaoImpl

/** * Title: DeptDaoImpl.java * Package org.dao.impl * Description: TODO该方法的主要作用&#xff1a; * author A18ccms A18ccms_gmail_com * date 2017-12-26 下午9:02:32 * version V1.0 */ package org.dao.impl;import java.util.List;import org.dao.IDeptDao…

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;//存储图对应的…

Spring 注入集合

转载自 Spring 注入集合 注入集合 你已经看到了如何使用 value 属性来配置基本数据类型和在你的 bean 配置文件中使用<property>标签的 ref 属性来配置对象引用。这两种情况下处理奇异值传递给一个 bean。 现在如果你想传递多个值&#xff0c;如 Java Collection 类…

.net core依赖注入的封装

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

ssm(Spring+Spring mvc+mybatis)Dao层配置sql的文件——DeptDaoMapper.xml

<?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!-- namespace&#xff1a;接口的全路径名 --> <map…

图的深度优先遍历+图解

图解 代码实现 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;//存储图对应的邻接矩阵…