赞
踩
源码参见Microsoft.Owin.Builder.AppBuilder
推荐三篇文章,对理解本文内容有帮助。
Delegate.CreateDelegate Method (Type,?Object,?MethodInfo)
官方文档
https://msdn.microsoft.com/en-us/library/74x8f551(v=vs.110).aspx
c#委托(delegate
)揭秘
http://www.cnblogs.com/50614090/archive/2011/11/14/2248408.html
C#中delegate
的机制原理
http://blog.csdn.net/argpunk/article/details/42121099
前文讲到的AppBuilder.Build
方法开始pipeline
的重建,其实际上是对AppBuilder.BuildInternal
的封装,传入参数为typeof(Func<IDictionary<string, object>, Task>)
,Func
的参数为IDictionary<string, object>
,返回一个Task
,这就是middleware
能串起来所要遵循的规范之一,微软工程师将其称为middleware
的签名。
先看看NotFound
的初始化
private static readonly AppFunc NotFound = new NotFound().Invoke; //将NotFound.Invoke绑定到AppBuilder.NotFound上
internal class NotFound
{
private static readonly Task Completed = CreateCompletedTask();
private static Task CreateCompletedTask()
{
var tcs = new TaskCompletionSource<object>();
tcs.SetResult(null);
return tcs.Task;
}
public Task Invoke(IDictionary<string, object> env) //这是一个满足AppBuilder中对于AppFunc定义的一个方法,之前在这里老是被绕晕了
{
env["owin.ResponseStatusCode"] = 404; //设置StatusCode
return Completed; //返回一个Task
}
}
上面的代码展示了AppBuilder.NotFound
是如何初始化为一个AppFunc
的,这是对中间件的签名,对于后面的Convert
方法来说至关重要。
private object BuildInternal(Type signature)
{
object app;
if (!_properties.TryGetValue(Constants.BuilderDefaultApp, out app)) //尝试寻找默认的最后一步处理方法,如果寻找失败则将app指向NotFound
{
app = NotFound;
}
foreach (var middleware in _middleware.Reverse()) //对List进行反向遍历,反向遍历很重要,这样上一节所说的UseStageMarker对stage.Name的处理方式才能理解
{
Type neededSignature = middleware.Item1; //解耦三元组
Delegate middlewareDelegate = middleware.Item2;
object[] middlewareArgs = middleware.Item3;
app = Convert(neededSignature, app); //尝试将app的Invoke方法创建为一个委托,委托为needSignature所表示的Type,听起来有点绕,没关系,慢慢来
//这将涉及到pipeline中AppFunc与Middleware的转换,这是OWIN的精华所在
object[] invokeParameters = new[] { app }.Concat(middlewareArgs).ToArray(); //将app作为第一个参数与args合并
app = middlewareDelegate.DynamicInvoke(invokeParameters);
app = Convert(neededSignature, app); //这一步我也没大懂,到后面懂了再说
}
return Convert(signature, app); //同理这一步我也没大懂
}
从实际例子出发容易理解上面的流程一些,上一章讲到UseCookieAuthentication
方法中先调用app.Use(typeof(CookieAuthenticationMiddleware), app, options)
,再调用app.UseStageMarker(stage)
,这实际上会调用app.Use(decoupler)
方法,而decoulper
是一个Func<AppFunc,AppFunc>
委托,所以当前进行_middleware.Reverse
遍历的时候,最先取到的就是app.Use(decoupler)
压进去的委托。
而参考上上一章对AppBuilder.Use
方法的总结,实际上会调用第一种Use
处理流程,所以上面源代码中middleware
中的三元组对应的类型如下
项 | 说明 |
---|---|
Item1 | GetParameterType(an instance of (Func<AppFunc,AppFunc> )),结果为typeof(AppFunc) = typeof(Func<Idictionary<string, object>, Task>) = a special Delegate ,是一个委托 |
Item2 | Func<AppFunc,AppFunc> 委托的一个实例,对应decoupler |
Item3 | New object[0] 为空 |
所以Convert(neededSignature, app)
可以替换成Convert(a special Delegate, an instance of Func<Idictionary<string, object>, Task>
)
来看看Convert做了什么。
private object Convert(Type signature, object app)
{
if (app == null)
{
return null;
}
object oneHop = ConvertOneHop(signature, app);
if (oneHop != null)
{
return oneHop;
}
object multiHop = ConvertMultiHop(signature, app);
if (multiHop != null)
{
return multiHop;
}
throw new ArgumentException(
string.Format(CultureInfo.CurrentCulture, Resources.Exception_NoConversionExists, app.GetType(), signature),
"signature");
}
Covert
实际上是对ConvertOneHop
和ConvertMultiHop
的封装。
先看看ConvertOneHop
方法。
private object ConvertOneHop(Type signature, object app)
{
if (signature.IsInstanceOfType(app)) //针对上面的例子,app确实是signature的一个实例,都对应Func<Idictionary<string, object>, Task>
{
return app; //所以第一次调用会直接返回
}
if (typeof(Delegate).IsAssignableFrom(signature)) //如果signature是对Delegate的继承
{
Delegate memberDelegate = ToMemberDelegate(signature, app); //尝试将app的Invoke方法创建为一个signature所表示的Type类型的委托
if (memberDelegate != null)
{
return memberDelegate;
}
}
foreach (var conversion in _conversions) //如果app的Invoke方法与signature的Invoke方法冲突,需要进行转换
//这是Middleware与AppFunc之间的重要转换,也是pipeline的重点,留到后文详述
{
Type returnType = conversion.Key.Item1;
Type parameterType = conversion.Key.Item2;
if (parameterType.IsInstanceOfType(app) &&
signature.IsAssignableFrom(returnType))
{
return conversion.Value.DynamicInvoke(app);
}
}
return null;
}
再回头看看_middleware.Rerverse
遍历的第一次中,Convert(needSignature,app)
会很快返回,值就是app
,也就是Func<Idictionary<string, object>, Task>
的一个实例,再运行app = middlewareDelegate.DynamicInvoke(invokeParameters)
的时候,因为app
已经合并进invokeParameters
中所以,等同于执行
app =>
{
if (string.Equals(name, stage.Name, StringComparison.OrdinalIgnoreCase)) //name = "Authenticate", stage.Name = "PreHandlerExecute",返回false
{
// no decoupling needed when pipeline is already split at this name
return app ;
}
if (!IntegratedPipelineContext.VerifyStageOrder(name, stage.Name)) //name = "Authenticate", stage.Name = "PreHandlerExecute",name < stage.Name,返回false,注意前面有个'!'
{
// Stage markers added out of order will be ignored.
// Out of order stages/middleware may be run earlier than expected.
// TODO: LOG
return app ;
}
stage.EntryPoint = app ; //设置PreHandlerExecute这一Stage的EntryPoint为app,此时的app就是NotFound.Invoke方法
stage = new IntegratedPipelineBlueprintStage //为Authenticate新建一个IntegratedPipelineBlueprintStage,NextStage绑定到PreHandlerExcute这一Stage上
//所以两个PipelineStage就链接起来了
{
Name = name,
NextStage = stage,
};
onStageCreated(stage); //更新firstStage,使其指向Autenticate这一Stage
return (AppFunc)IntegratedPipelineContext.ExitPointInvoked; //返回ExitPointInvoked方法
};
上面的代码演示了PreHandlerExcute
和Authenticate
两个PipelineStage
是如何串接在一起的,再来看看IntegratedPipelineContext.ExitPointInvoked
到底干了什么。
public static Task ExitPointInvoked(IDictionary<string, object> env)
{
object value;
if (env.TryGetValue(Constants.IntegratedPipelineContext, out value)) //尝试从environment中获取IntegratedPipelineContext实例,
{
var self = (IntegratedPipelineContext)value;
return self._state.ExecutingStage.ExitPointInvoked(env); //改变当前管道状态,使其可以流入下一管道
}
throw new InvalidOperationException();
}
public Task ExitPointInvoked(IDictionary<string, object> env)
{
_context.PreventNextStage = false; //改变当前管道状态
return Epilog(env); //进行最后的收尾工作
}
private Task Epilog(IDictionary<string, object> env)
{
var tcs = new TaskCompletionSource<object>();
_responseShouldEnd = false; //开启response,因为即将进行Stage的切换,与Stage刚开始执行的时候关闭response相对应
_context.PushLastObjects(env, tcs); //验证当前pipeline所在Stage中的environment为null,TaskCompletionSource<object>为null,因为即将离开Stage,而Stage是公用的
//这与IntegratedPipelineContextStage.BeginEvent中的TakeLastEnvironment,TakeLastCompletionSource相对应,都是原子操作
StageAsyncResult result = Interlocked.Exchange(ref _result, null);
if (result != null)
{
result.TryComplete();
}
return tcs.Task;
}
扯了好远,在没有进行调试的情况下推断这些运行流程还真是很累的一件事儿。这对于前面没有搞懂的地方有很大帮助,看代码。
app = middlewareDelegate.DynamicInvoke(invokeParameters)
执行之后,app = (AppFunc)IntegratedPipelineContext.ExitPointInvoked
了,这就是PreHandlerExecute
的收尾工作。
之后再次执行了app = Convert(neededSignature, app)
,此时的参数app
仍然是一个AppFunc
,所以还是会很快返回,进入下一循环。
这次_middleware.Rerverse
遍历获取到的应该是app.Use(typeof(CookieAuthenticationMiddleware), app, options)
压进去的CookieAuthenticationMiddleware
。
参考AppBuilder(一)
那一节所分析的结果,因为传入的参数是一个Type
,args
长度为2
,所以会采用第四种方法来处理,如下
private static Tuple<Type, Delegate, object[]> ToConstructorMiddlewareFactory(object middlewareObject, object[] args, ref Delegate middlewareDelegate)
这个方法尝试寻找middlewareObject
类中的参数个数为args
长度+1,即是3个的构造函数。以下是对应的构造函数
public CookieAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, CookieAuthenticationOptions options) : base(next, options)
所以可以推断出此时取到的middleware
三元组为
项 | 说明 |
---|---|
Item1 | OwinMiddleware的Type |
Item2 | CookieAuthenticationMiddleware(OwinMiddleware next, IAppBuilder app, CookieAuthenticationOptions options) 构造函数 |
Item3 | [IAppBuilder app, CookieAuthenticationOptions options] 长度为2的object[] |
再次执行
app = Convert(needSignature, app)…>object oneHop = ConvertOneHop(signature, app)
此时的参数app
是一个AppFunc
,而signature
是一个OwinMiddleware
,会用到_conversions
,这将是OwinMiddleware
与AppFunc
之间互相转换的实现,需要用到AppBuilder
时候对_conversions
初始化的知识,留待下一章再说。
总结AppBuilder.BuildeInternal
对middleware
的List
遍历是反向的,虽然现在还不明白为什么如此设计,而且如何在一个PipelineStage
中执行多个middleware
也还不明朗,曾经以为是使用类似Invoke += Middleware.Invoke
实现的,但既然是反向的,这不就顺序反了吗?
目前能确定下来的时候每个PipelineStage
的EntryPoint
已经显式指定了,刚刚大概又想了一下,为了保证PipelineStage
的规范性,那么每个PipelineStage
应该都是一个Func<AppFunc, AppFunc>
形式的才对,而Middleware
应该是被封装在这两个AppFunc
之间的,这么说,应该是_conversions
来完成了同一个PipelineStage
中的Middleware
的串联工作了,理应如此。下一节再验证这个问题。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。