源码参见Microsoft.Owin.Host.SystemWeb.OwinBuilder
Microsoft.Owin.Builder.AppBuilder
前文讲到
internal static OwinAppContext Build()
{
Action startup = GetAppStartup();
return Build(startup);
}
GetAppStartup()
已经寻找到Startup
类,并封装了其中的Configuration
方法,接下来就会调用Build(startup)
方法。
internal static OwinAppContext Build(Action startup)
{
if (startup == null)
{
throw new ArgumentNullException("startup");
}
var appContext = new OwinAppContext(); //实例化OwinAppContext,主要获得网站名称和程序名称
appContext.Initialize(startup); //进一步初始化OwinAppContext
return appContext;
}
来看看OwinAppContext.Initialize(IAppBuilder)
做了什么
internal void Initialize(Action startup)
{
Capabilities = new ConcurrentDictionary(); //初始化为多线程安全的Dictionary,说明为多线程共享资源
var builder = new AppBuilder(); //初始化AppBuilder,向Properties中填入一些基本信息,这构成了Server环境的快照
builder.Properties[Constants.OwinVersionKey] = Constants.OwinVersion;
builder.Properties[Constants.HostTraceOutputKey] = TraceTextWriter.Instance;
builder.Properties[Constants.HostAppNameKey] = AppName;
builder.Properties[Constants.HostOnAppDisposingKey] = OwinApplication.ShutdownToken;
builder.Properties[Constants.HostReferencedAssemblies] = new ReferencedAssembliesWrapper();
builder.Properties[Constants.ServerCapabilitiesKey] = Capabilities;
builder.Properties[Constants.SecurityDataProtectionProvider] = new MachineKeyDataProtectionProvider().ToOwinFunction();
builder.SetLoggerFactory(new DiagnosticsLoggerFactory());
Capabilities[Constants.SendFileVersionKey] = Constants.SendFileVersion;
CompilationSection compilationSection = (CompilationSection)System.Configuration.ConfigurationManager.GetSection(@"system.web/compilation");
bool isDebugEnabled = compilationSection.Debug;
if (isDebugEnabled)
{
builder.Properties[Constants.HostAppModeKey] = Constants.AppModeDevelopment;
}
DetectWebSocketSupportStageOne();
try
{
startup(builder); //真正的由用户定义的middleware注入开始了,这将完成pipeline的构造
}
catch (Exception ex)
{
_trace.WriteError(Resources.Trace_EntryPointException, ex);
throw;
}
AppFunc = (AppFunc)builder.Build(typeof(AppFunc));//记录pipeline的第一个middleware,这将是整个pipeline的入口
}
先从AppBuilder
的实例化开始
public AppBuilder()
{
_properties = new Dictionary<string, object>(); //初始化environment资源
_conversions = new Dictionary, Delegate>(); //初始化转换器,这将不是本文重点,后期再涉及
_middleware = new Listobject[]>>(); //初始化middleware列表
_properties[Constants.BuilderAddConversion] = new Action(AddSignatureConversion); //记录Conversion
_properties[Constants.BuilderDefaultApp] = NotFound;
SignatureConversions.AddConversions(this);
}
上面的源码展示了AppBuilder
初始化pipeline
流动所需的envrionment
和middleware
,这也是OWIN
最重要的两个数据结构。
builder
完成实例化之后会尝试startup(builder)
这即是调用用户定义的Configuration(IAppBuilder)
方法。
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(BlogContext.Create); //将数据库上下文注入envrionment中
app.CreatePerOwinContext(AppUserManager.Create); //将AppUserManager注入environment中
app.CreatePerOwinContext(AppSigninManager.Create); //将AppSigninManager注入environment中
app.UseCookieAuthentication(new CookieAuthenticationOptions //标记CookeAuthentication这个middleware在pipeline中的stage,并配置参数
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
// Enables the application to remember the second login verification factor such as phone or email.
// Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
// This is similar to the RememberMe option when you log in.
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie); //标记stage
}
}
以新建MVC
工程默认生成的Starup
类为例。目前我们可以大致猜测一下这份配置方法中只进行了两个stage
的注册,而pipeline
有11个stage
之多,具体如下
private static readonly IList StageNames = new[]
{
Constants.StageAuthenticate,
Constants.StagePostAuthenticate,
Constants.StageAuthorize,
Constants.StagePostAuthorize,
Constants.StageResolveCache,
Constants.StagePostResolveCache,
Constants.StageMapHandler,
Constants.StagePostMapHandler,
Constants.StageAcquireState,
Constants.StagePostAcquireState,
Constants.StagePreHandlerExecute,
};
接下来我们通过对app.UseCookieAuthentication(new CookieAuthenticationOptions)
的追踪来了解如何进行middleware
的注册。
public static class CookieAuthenticationExtensions
{
public static IAppBuilder UseCookieAuthentication(this IAppBuilder app, CookieAuthenticationOptions options)
{
return app.UseCookieAuthentication(options, PipelineStage.Authenticate); //在PipelineStage.Authenticate阶段执行
}
public static IAppBuilder UseCookieAuthentication(this IAppBuilder app, CookieAuthenticationOptions options, PipelineStage stage)
{
if (app == null)
{
throw new ArgumentNullException("app");
}
app.Use(typeof(CookieAuthenticationMiddleware), app, options); //app.Use实际上就是将middleware压入middleware的List中
app.UseStageMarker(stage); //标记middleware所在stage
return app;
}
}
这里涉及到AppBuilder
的两个核心操作app.Use
和app.UseStageMarker
。
先来看看app.Use
,在AppBuilder
中定义了Use(object middleware, params object[] args)
,而在AppBuilderUseExtensions
还有两种定义,我们逐个来解释。
public IAppBuilder Use(object middleware, params object[] args)
{
_middleware.Add(ToMiddlewareFactory(middleware, args));
return this;
}
可见这个方法主要做的操作是将middleware
进行转换并压入middleware
的List中,转换过程实际上对middleware
进行参数检查和签名,这将是一个非常复杂的过程,一步一步地来。
先看看ToMiddlewareFactory
方法。
private static Tupleobject[]> ToMiddlewareFactory(object middlewareObject, object[] args) //接受两个参数,返回一个三元组
{
if (middlewareObject == null)
{
throw new ArgumentNullException("middlewareObject");
}
var middlewareDelegate = middlewareObject as Delegate; //尝试将middlewareObject类型转换为Delegate,而我们上面传入的是一个Type,将会转换失败
if (middlewareDelegate != null) //如果转换成功
{
return Tuple.Create(GetParameterType(middlewareDelegate), middlewareDelegate, args); //直接返回三元组,这似乎是最理想的情况
}
Tupleobject[]> factory = ToInstanceMiddlewareFactory(middlewareObject, args); //尝试在middlewareObject类中寻找Initialize方法,并检查args参
//数是否符合Initialize方法的参数
if (factory != null)
{
return factory;
}
factory = ToGeneratorMiddlewareFactory(middlewareObject, args); //尝试在middlewareObject类中寻找Invoke方法,并作参数检查
if (factory != null)
{
return factory;
}
if (middlewareObject is Type) //这是本文的app.Use方法传入的参数类型,所以会进入这一流程中
{
return ToConstructorMiddlewareFactory(middlewareObject, args, ref middlewareDelegate);
}
throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture,
Resources.Exception_MiddlewareNotSupported, middlewareObject.GetType().FullName));
}
接下来将分别对ToInstanceMiddlewareFactory
,ToGeneratorMiddlewareFactory
,ToConstructorMiddlewareFactory
进行介绍。
private static Tuple<Type, Delegate, object[]> ToInstanceMiddlewareFactory(object middlewareObject, object[] args)
{
MethodInfo[] methods = middlewareObject.GetType().GetMethods(); //将middleware当作一个实例处理,获取其Type,再获得类中所有方法
foreach (var method in methods) //遍历方法
{
if (method.Name != Constants.Initialize) //寻找Initialize方法
{
continue;
}
ParameterInfo[] parameters = method.GetParameters(); //获取Initialize的参数表
Type[] parameterTypes = parameters.Select(p => p.ParameterType).ToArray(); //获取参数表的Type类型
if (parameterTypes.Length != args.Length + 1) //Initialize的参数应该比args的参数多一个,因为第一个参数应该是下一个middleware
{
continue;
}
if (!parameterTypes
.Skip(1)
.Zip(args, TestArgForParameter)
.All(x => x))
{ //对Initialize第一个以后的参数与args进行一对一验证,args应该为对应项的实例
continue;
}
// DynamicInvoke can't handle a middleware with multiple args, just push the args in via closure.
//开发者在此处注释动态Invoke无法处理middleware有多个参数的情况,所以进行了一次封装,简化参数表
Func
ToInstanceMiddlewareFactory
返回的是一个具体的实例,似乎不满足Func
类型,这也是目前没弄明白的地方
private static Tuple<Type, Delegate, object[]> ToGeneratorMiddlewareFactory(object middlewareObject, object[] args)
{
MethodInfo[] methods = middlewareObject.GetType().GetMethods(); //将middlewareObject当作一个实例,获取Type并获取所有方法
foreach (var method in methods)
{
if (method.Name != Constants.Invoke) //寻找Invoke方法
{
continue;
}
ParameterInfo[] parameters = method.GetParameters(); //获取Invoke方法的参数表
Type[] parameterTypes = parameters.Select(p => p.ParameterType).ToArray(); //获取参数的Type表
if (parameterTypes.Length != args.Length + 1)
{
continue;
}
if (!parameterTypes
.Skip(1)
.Zip(args, TestArgForParameter)
.All(x => x))
{ //将Type表与args进行一对一验证
continue;
}
IEnumerable<Type> genericFuncTypes = parameterTypes.Concat(new[] { method.ReturnType }); //将参数Type表与返回Type合并
Type funcType = Expression.GetFuncType(genericFuncTypes.ToArray()); //获取方法签名Type
Delegate middlewareDelegate = Delegate.CreateDelegate(funcType, middlewareObject, method); //对实例的Invoke方法进行delegate封装
return Tuple.Create(parameters[0].ParameterType, middlewareDelegate, args);
}
return null;
}
ToGeneratorMiddlewareFactory
返回类型由Invoke
方法决定,通常会是一个Task
。
private static Tupleobject[]> ToConstructorMiddlewareFactory(object middlewareObject, object[] args, ref Delegate middlewareDelegate)
{
var middlewareType = middlewareObject as Type; //尝试将middlwareObject转换为Type
ConstructorInfo[] constructors = middlewareType.GetConstructors(); //获取类的构造方法
foreach (var constructor in constructors) //遍历构造方法
{
ParameterInfo[] parameters = constructor.GetParameters(); //获取构造函数的参数表
Type[] parameterTypes = parameters.Select(p => p.ParameterType).ToArray(); //获取参数的Type表
if (parameterTypes.Length != args.Length + 1) //参数表应该比args多一项,第一项应该为下一个middleware
{
continue;
}
if (!parameterTypes
.Skip(1)
.Zip(args, TestArgForParameter)
.All(x => x))
{ //对参数表的第一项之后的与args一对一校验
continue;
}
ParameterExpression[] parameterExpressions = parameters.Select(p => Expression.Parameter(p.ParameterType, p.Name)).ToArray(); //提取参数表的Type和Name
NewExpression callConstructor = Expression.New(constructor, parameterExpressions); //创建构造函数
middlewareDelegate = Expression.Lambda(callConstructor, parameterExpressions).Compile(); //对构造函数进行Lambda封装
return Tuple.Create(parameters[0].ParameterType, middlewareDelegate, args);
}
throw new MissingMethodException(string.Format(CultureInfo.CurrentCulture,
Resources.Exception_NoConstructorFound, middlewareType.FullName, args.Length + 1));
}
上面三种方法让我们看见了微软工程师的花样使用委托、Lambda
、Func
,如果不是资深C#程序员,谁知道这语言还有这些特性,但能感觉到主要是想将原来类中的初始化方法进行归一化,封装在一个匿名delegate
中,并合成一个三元组,后面采用统一的方法进行调用,这个三元组的Item1
就是共用的下一个middleware
也是当前middleware
所在pipelineStage
的签名。
再来看看AppBuilderUseExtension
扩展的两个Use
方法和一个Run
方法。
public static IAppBuilder Use(this IAppBuilder app, params object[] args)
{
if (app == null)
{
throw new ArgumentNullException("app");
}
return app.Use(typeof(T), args);
}
这个Use
方法将middleware
的Type
放在Use
中,做为一个模板方法,实际上是对Use(typeof(T), args)
的一个封装。
public static IAppBuilder Use(this IAppBuilder app, Func /*next*/, Task> handler)
{
if (app == null)
{
throw new ArgumentNullException("app");
}
if (handler == null)
{
throw new ArgumentNullException("handler");
}
return app.Use(handler);
}
这个Use
方法接受一个Func
的委托,并对Use
进行封装,为了后文引述方便我们将其重命名为UseExtensionOne
。
public static void Run(this IAppBuilder app, Func handler)
{
if (app == null)
{
throw new ArgumentNullException("app");
}
if (handler == null)
{
throw new ArgumentNullException("handler");
}
app.Use(handler);
}
这个Run
方法接受一个 Func
的委托,并对Use
进行封装,将其重命名为RunExtensionTwo
方法。
现在查看微软对于Middleware
的使用举例页面 http://www.asp.net/aspnet/overview/owin-and-katana/owin-middleware-in-the-iis-integrated-pipeline
网页中黄色部分对middleware
的注入有具体举例,如下
//这是对UseExtensionOne的实际应用
app.Use((context, next) =>
{
PrintCurrentIntegratedPipelineStage(context, "Middleware 1");
return next.Invoke();
});
//这是对UseExtensionOne的实际应用
app.Use((context, next) =>
{
PrintCurrentIntegratedPipelineStage(context, "2nd MW");
return next.Invoke();
});
//这是对RunExtentionTwo的实际应用
app.Run(context =>
{
PrintCurrentIntegratedPipelineStage(context, "3rd MW");
return context.Response.WriteAsync("Hello world");
});
三个很简单的middleware
被注入到pipeline
中,但因为实际上是对Use
的封装,也就是对Use(typeof(T), args)
的封装,所以实际上与 app.Use(typeof(CookieAuthenticationMiddleware), app, options)
差不多,最终都会存成一样的三元组压进middleware
的List
中,所以他们都会调用UseHandlerMiddleware
的构造函数,而查看UseHandlerMiddleware
发现其构造函数的第一项就是一个AppFunc
,这与middlewareStage
的切换和pipeline
的流动息息相关,后面将详细介绍其原理。
总结,本文主要讲了AppBuilder
的Use
方法的具体流程与扩展,三种扩展方法实际上都是对基础Use
的封装,而基础Use
方法总的来说可以接受四种middlewareObject
序号 | 说明 |
---|---|
1 | Delegate是最简单的,直接可以封装成三元组压入List |
2 | 有Initialize方法的类的实例,参数表第一项为一个AppFunc或者OwinMiddleware,只要其Invoke之后能返回一个Task就行,为了避免DynamicInvoke的弊端进行了一次封装, |
3 | 有Invoke方法的类的实例,参数表也需要汇聚到一个object[]中,这两种设计应该是有不同需求背景的,目前不知道究竟有什么不同 |
4 | Type,这需要对这个类的构造方法进行封装,参考UseHandlerMiddleware的构造函数,第一个参数应该是一个AppFunc |
需要理解这四种Use
方法的不同,还需要了解pipeline
重构是如何做的,这就是下一节AppBuilder.Build
中的具体内容了,里面有很多细节的东西,这将直接构建整个pipeline
,也是整个OWIN
最核心的地方。