• 为IIS Host ASP.NET Web Api添加Owin Middleware


    将OWIN App部署在IIS上                                           

    要想将Owin App部署在IIS上,只添加Package:Microsoft.OWIN.Host.SystemWeb包即可。它提供了所有Owin配置,Middleware注册等方面的Api.我们需要做的其实和SelfHost差不多。

    • 我们依然需要实现Startup类,但是不是通过WebApp来启动了。我们需要通过将Startup类打上[assembly: OwinStartup(typeof(Startup))]来定义这是OWIN的Startup类,当应用运行的时候会自动发现并调用Configuration方法
    • 在Startup类的Configuration方法中我们可以配置我们的Middleware。

    SystemWeb其实是实现了一个OwinHttpModule来根据MiddleWare的需求注册IIS的各种事件,然后执行相关的Middleware.

    先看一下IIS集成模式的HttpRequest处理的管道模型。

    我们的Middleware运行在IIS管道中哪个环节是通过调用appBuilder.UseStageMarker(PipelineStage.Authenticate);这个扩展方法来定义的。其中PipelineState有如下可选值:

    public enum PipelineStage
    {
        Authenticate = 0,
        PostAuthenticate = 1,
        Authorize = 2,
        PostAuthorize = 3,
        ResolveCache = 4,
        PostResolveCache = 5,
        MapHandler = 6,
        PostMapHandler = 7,
        AcquireState = 8,
        PostAcquireState = 9,
        PreHandlerExecute = 10,
    }

    以上枚举值对应IIS管道中的对应环节。Stage的指定有如下规则:

    1. 默认的Stage为PreHandlerExecute

    2. 每次UseStageMaker的调用指定了该次调用与前一次调用之间的注册的Middleware均在该次UseStageMaker中指定的Stage中运行

    3. Stage的指定顺序应该与IIS管道的处理顺序一致

    4. 如果Stage的指定顺序与IIS管道处理顺序不一致,在后面指定的在IIS管道中靠前的Stage会覆盖在其前面指定的在IIS管道中靠后的Stage.

    听上去有点绕口。其实理解了原因和实现就容易理解了。这种规则的原因是:IIS管道是有固定顺序的,而OWIN中Middleware的执行也是按照注册的先后顺序的,而当OWIN部署在IIS中时Middleware的执行又是依赖与IIS管道事件的,所以只有当指定的Stage顺序与IIS管道顺序一致时才不会有冲突。

    为Web Api添加Authenticate OWIN Middleware                                 

     我们首先创建一个普通的web api工程,添加如下接口,然后部署在IIS上。

    [RoutePrefix("api/persons")]
        public class PersonController : ApiController
        {
            [Route("{id}")]
            [Authorize]
            // GET api/values/5
            public string Get(int id)
            {
                return "Jensen";
            }
        }

    这时候如果去访问该接口会得到401未授权的错误。

    接下来通过添加AuthMiddleware来作为WebApi验证模块。

    首先添加AuthenticateMiddleware

    public class AuthenticateMiddleware
        {
            private Func<IDictionary<string, object>, Task> nextAppFunc;
            public AuthenticateMiddleware(Func<IDictionary<string, object>, Task> nextMiddleWareFunc)
            {
                nextAppFunc = nextMiddleWareFunc;
            }
    
            public async Task Invoke(IDictionary<string, object> parameters)
            {
                Trace.WriteLine("Auth Middleware");
                Trace.WriteLine(HttpContext.Current.CurrentNotification);
    
                var identity = new GenericIdentity("jensen");
                parameters["server.User"] = new GenericPrincipal(identity, new string[] { "admin" });
                if (nextAppFunc != null)
                {
                    await nextAppFunc.Invoke(parameters);
                }
            }
        }

    然后添加Startup类来注册AuthenticateMiddleware,并指定运行Stage为Authenticate.

    [assembly: OwinStartup(typeof(OwinIISHost.Startup))]
    namespace OwinIISHost
    {
        public class Startup
        {
            public void Configuration(IAppBuilder appBuilder)
            {
                appBuilder.Use<AuthenticateMiddleware>();
                appBuilder.UseStageMarker(PipelineStage.Authenticate);
                
            }
        }
    }

    这样当我们再次访问前面定义的api时,就能得到期望的结果了。因为在AuthenticateMiddleware中我们对所有请求都通过了验证。

    这里一开始有点疑问,WebApi中是根据HttpContext.User来获取当前请求的用户信息的。但是我们在AuthenticateMiddleware中并没有直接给HttpContext.User赋值,而是将User信息赋值到key 为server.user的OWIN环境参数中。这中间有个断档。通过查看SystemWeb Package的源码,解答了我的疑问。

    首先,我们的Middleware中接收到的OWIN环境参数类型为AspNetDictionary,可以查看其实现:

    internal AspNetDictionary(IPropertySource propertySource)//构造函数,这里propertySource由外部传入
            {
                _propertySource = propertySource;
            }
    
    
    object IDictionary<string, object>.this[string key]//索引属性,我们设置User信息给server.user key时该方法会被调用
            {
                get
                {
                    object value;
                    return PropertiesTryGetValue(key, out value) ? value : Extra[key];
                }
                set
                {
                    if (!PropertiesTrySetValue(key, value))
                    {
                        StrongExtra[key] = value;
                    }
                }
            }
    
    private bool PropertiesTrySetValue(string key, object value)
            {
                switch (key.Length)
                {
                    //....ignore some code here
                    case 11:
                        if (string.Equals(key, "server.User", StringComparison.Ordinal))
                        {
                            ServerUser = (IPrincipal)value;//赋值给ServerUser属性
                            return true;
                        }
                       break;
                    //...ignore some code here
                }
                return false;
            }
    
    internal IPrincipal ServerUser
            {
                get
                {
                    return _propertySource.GetServerUser();
                }
                set
                {
                    _propertySource.SetServerUser(value);//最后还是调了propertySource的SetServerUser方法
                }
            }

    那现在关键就看_propertySource是如何实现的了。通过查看创建AspNetDictionary的代码发现_propetySource为OwinCallContext类型的实例。看看它对SetServerUser的实现:

    void AspNetDictionary.IPropertySource.SetServerUser(IPrincipal value)
            {
                _httpContext.User = value;//真相大白,其实当我们给server.User key赋值时,value其实直接就赋给了HttpContext。
                Thread.CurrentPrincipal = value;
            }

    参考:

    AspNetKatana源码,包含SystemWebPackage

  • 相关阅读:
    NBUT 1120 Reimu's Teleport (线段树)
    NBUT 1119 Patchouli's Books (STL应用)
    NBUT 1118 Marisa's Affair (排序统计,水)
    NBUT 1117 Kotiya's Incantation(字符输入处理)
    NBUT 1115 Cirno's Trick (水)
    NBUT 1114 Alice's Puppets(排序统计,水)
    188 Best Time to Buy and Sell Stock IV 买卖股票的最佳时机 IV
    187 Repeated DNA Sequences 重复的DNA序列
    179 Largest Number 把数组排成最大的数
    174 Dungeon Game 地下城游戏
  • 原文地址:https://www.cnblogs.com/Code-life/p/7467318.html
Copyright © 2020-2023  润新知