0x00 为什么要引入扩展方法
有的中间件功能比较简单,有的则比较复杂,并且依赖其它组件。除了直接用ApplicationBuilder的Use()方法注册中间件外,还可以使用ApplicationBuilder的扩展方法UseMiddleware()注册中间件。这种情况下可以注册类型,这个方法会通过反射解析这个类型,并把它包装成Func<ReuqestDelegate,RequestDelegate>然后调用Use()方法注册。
遇到这种情况一般直觉上是通过继承一个抽象类并实现其中的方法在写一个中间件。不过.NET Core不是这么做的。中间件类使用约定而不是继承来进行约束。这里说的约定就是约定原本的意思,例如约定好了中间件类中必须包含一个叫Invoke的方法,叫别的就不行,有重载也不行。因为中间件类没有任何继承上的约束,在注册过程中就是通过反射去寻找名字为Invoke的方法,然后把它包装成RequestDelegate的。这篇文章就是要说一下写一个中间件类都有哪些约定以及中间件类的注册。
0x01 一个最简单的例子
先看一个中间件类的最简单的例子:
上一篇文章中说过了,中间件本质就是一个方法,这个方法接收一个HttpContext参数,返回Task。在上面这个中间件类中Invoke就是这个方法。为了能够调用下一个中间件,当前中间件还需要保存下一个中间件的引用。这个引用是通过构造函数传进来的,如果当前中间件不需要调用后面中间件的话,这个引用完全可以不保存。如果要注册这个中间件,我们可以这样做:
但如果我们这个中间件比较复杂,依赖很多其他模块,那么我们在注册的时候需要构造依赖模块的实例,并在中间件类的构造函数中把这些依赖传进去。这加强了中间件和依赖模块之间的耦合度。为了能减少这种耦合,同时享受到依赖注入带来的便利,提供了UseMiddleware<T>扩展方法来注册中间件类T。
UseMiddleware扩展方法会找到上面中间件类中的Invoke方法,创建上面类的实例,在创建实例时遇到需要注入的类型会尝试注入,然后把Invoke方法包装为ReuqestDelegate,进而包装为Func<RequestDelegate,RequestDelegate>,然后通过ApplicatonBuilder的Use方法(上篇文章讲过了)注册到IList<Func<RequestDelegaet,RequestDelegate>中。
从上面的SimpleMiddleware我们可以看到这个类没有任何显示的继承关系,那么我们在写一个中间件类时需要注意哪些约束呢?我们只要看一下UseMiddleware注册中间件的过程就明白了。下面是对UseMiddleware()方法的分析,对代码分析不感兴趣的可以跳过直接看后面的结论和测试。