详解Asp.net 5中的ApplicationBuilder
applicationbuilder(iapplicationbuilder接口),是owin的基础,而且里面都是代理、代理的代理,各种lambda表达式,估计要看这部分代码,很多人得头昏脑涨。今天就对个类以及几个扩展方法进行讲解。
按惯例先贴代码(这是我修改后的,将接口继承去掉了、httpcontext类修改成自己的myhttpcontext类)
public class applicationbuilder { private readonly ilist<func<requestdelegate, requestdelegate>> _components = new list<func<requestdelegate, requestdelegate>>(); public applicationbuilder() { } private applicationbuilder(applicationbuilder builder) { } public applicationbuilder use(func<requestdelegate, requestdelegate> middleware) { _components.add(middleware); return this; } public applicationbuilder new() { return new applicationbuilder(this); } public requestdelegate build() { requestdelegate app = context => { context.statuscode = "404"; system.console.writeline("404"); return task.fromresult(0); }; foreach (var component in _components.reverse()) { app = component(app); } return app; } }
requestdelegate的定义如下:
public delegate task requestdelegate(myhttpcontext context);
从applicationbuilder的源代码中我们可以关注3个点:_components、use方法、build方法。
- _components是也一个列表(ilist)对象,不过里面类型有点特殊——是以代理requestdelegate为参数、代理requestdelegate为返回值的一个代理。这里用代理说有点别嘴,可以把代理叫做函数,就是里面的类型是一个函数,这个函数的参数也是函数,返回值也是函数。
- use方法,就是在上面的列表对象后面添加一条新记录。
- build方法就是将_components数组按照反向顺序,制作成一个链式结构(有点类似链表的感觉)。下面用俩幅图说明下:
build之前
build之后
我们还可以从代码中看到item1的参数给的是“404”,而返回结果是requestdelegate类型。也就是说这个返回类似于voidrequestdelegate(myhttpcontext context)。如果系统给我们一个context变量,那么这个管道就可以从头到尾的跑下去了。而事实上在asp.net5中,这个管道就是用于替代传统的ihttpmodule的(可能不准确),那现在问题就来了,item1的参数是这个管道的第一环还是最后一环呢?从图形来看应该是第一环,但是事实上这是一个误解。因为箭头两面一个是参数,一个是执行体(参数是一个方法,会在执行体内调用执行)。在执行体内,可能在开始就执行参数的内容,之后执行具体的内容;也可以是先执行具体内容,之后执行参数,最后在执行一部分具体内容;还可以先执行具体内容,之后参数;还可能无视参数,直接直接自己的内容,那么之前的参数就会被忽略。也就是说无所谓顺序,404可能是管道的第一环,也可能是最后一环,也可能是中间环节,还可能压根就不执行。这个和item1、item2等内容具体的写法有关系。(虽然也是链式结构是不是和链表感觉不一样)
是不是感觉太零活了,源码还对applicationbuilder做了俩个扩展方法,代码整理如下:
public static class runextensions { public static applicationbuilder use(this applicationbuilder app, func<myhttpcontext, func<task>, task> middleware) { return app.use(next => { return context => { func<task> simplenext = () => next(context); return middleware(context, simplenext); }; }); } public static void run(this applicationbuilder app, requestdelegate handler) { if (app == null) { throw new argumentnullexception("why?"); } if (handler == null) { throw new argumentnullexception("how?"); } app.use(_ => handler); } }
首先说use方法,改方法是对之前use方法的一个更改。将传入的参数更改为func<myhttpcontext, func<task>, task>。这样做有什么好处?之前的func<requestdelegate, requestdelegate>对象并不能给人清楚的明了的感觉,而func<myhttpcontext, func<task>, task>就非常明确了。传入的参数:myhttpcontext就是context对象,func<task>就是next的执行体。返回值是一个task(类似于void)。一目了然。
再说run方法,显而易见,run方法只执行自己的内容,并没有执行参数体。所以链式结构的在其前的都会被舍弃,不会被执行。
最后把自己的测试例子贴出来,供大家参考
示例1:
static void main(string[] args) { myhttpcontext context = new myhttpcontext() { statuscode = "a" }; func<myhttpcontext, func<task>, task> middleware = (x, y) => { context.statuscode += "c"; system.console.writeline(context.statuscode); return y(); }; func<myhttpcontext, func<task>, task> middleware2 = (x, y) => { context.statuscode += "end1"; system.console.writeline(context.statuscode); return task.fromresult(0); }; applicationbuilder builder = new applicationbuilder(); builder.use( next => { return (myhttpcontext o) => { o.statuscode += "b"; system.console.writeline(context.statuscode); next(o); return task.fromresult(0); }; } ); builder.use(middleware); //builder.use(middleware2); //builder.use(middleware); //builder.run(o => { o.statuscode += "end2"; return task.fromresult(0); }); builder.build().invoke(context); system.console.readline(); }
执行结果:
示例2:
static void main(string[] args) { myhttpcontext context = new myhttpcontext() { statuscode = "a" }; func<myhttpcontext, func<task>, task> middleware = (x, y) => { context.statuscode += "c"; system.console.writeline(context.statuscode); return y(); }; func<myhttpcontext, func<task>, task> middleware2 = (x, y) => { context.statuscode += "end1"; system.console.writeline(context.statuscode); return task.fromresult(0); }; applicationbuilder builder = new applicationbuilder(); builder.use( next => { return (myhttpcontext o) => { o.statuscode += "b"; system.console.writeline(context.statuscode); next(o); return task.fromresult(0); }; } ); builder.use(middleware); builder.use(middleware2); //builder.use(middleware); //builder.run(o => { o.statuscode += "end2"; system.console.writeline(context.statuscode); return task.fromresult(0); }); builder.build().invoke(context); system.console.readline(); }
执行结果:
示例3:
static void main(string[] args) { myhttpcontext context = new myhttpcontext() { statuscode = "a" }; func<myhttpcontext, func<task>, task> middleware = (x, y) => { context.statuscode += "c"; system.console.writeline(context.statuscode); return y(); }; func<myhttpcontext, func<task>, task> middleware2 = (x, y) => { context.statuscode += "end1"; system.console.writeline(context.statuscode); return task.fromresult(0); }; applicationbuilder builder = new applicationbuilder(); builder.use( next => { return (myhttpcontext o) => { o.statuscode += "b"; system.console.writeline(context.statuscode); next(o); return task.fromresult(0); }; } ); builder.use(middleware); //builder.use(middleware2); //builder.use(middleware); builder.run(o => { o.statuscode += "end2"; system.console.writeline(context.statuscode); return task.fromresult(0); }); builder.build().invoke(context); system.console.readline(); }
执行结果:
到此这篇关于详解asp.net 5中的applicationbuilder的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持。