• Host ASP.NET WebApi in Owin


    什么是OWIN                                          

      Owin其实是微软为了解耦.Net Web app对IIS的依赖而制定的一套规范,规范定义了Web Server与Web App之间的接口,这样Web App就可以Host在所有兼容OWIN规范的Web Server了(包含控制台应用和Windows服务...)。具体来说,Owin将Web app和Web Server整体划分为以下几个模块。

    Host 

    根据官方解释,Host是指server和application所依托执行的进程。主要负责应用的启动与配置。

    Server

    Server负责直接与客户端进行Http通信,然后将请求转换为Owin的语义,然后用Owin的流程进行处理的Http Server.

    Middleware

    Middleware是开发人员自行注册到Owin处理请求管道中的模块(类似IIS中的Module),可以直接参与请求的处理。所以我认为Web Api框架其实也属于Middleware.(不过官方将Web Api框架归属于Web Framework。它会将Owin的语义转换为Web Framework内部的语义,然后按照内部的处理流程处理请求)

    Application

    Application就是基于所有上面所有Middleware(准确说应该是Web Framework,看来官方定义一个Web Framework模块是有意义的)所构建的应用层。

    这些定义只是一些描述,那具体实施是怎么实施呢。微软自己有一个开源项目叫Katana,它是对Owin规范的官方实现(其实主要就是实现上述的Host/Server部分,因为Middleware,Application部分都是需要我们自己开发的)。下面通过使用Katana,我们将web api部署在一个控制台进程中,来看看具体怎么去使用它以及OWIN接口到底是什么。

    Host WebApi in Console App                                  

    1.首先我们创建一个控制台应用

    2.然后我们通过Nuget引入Package Microsoft.AspNet.WebApi.OwinSelfHost,安装过程中所有依赖的Owin和Web Api的package都会一并安装。

    3.我们可以添加一个ApiController:PersonController,并添加一个接口方法:

     1 [RoutePrefix("api/persons")]
     2     public class PersonController : ApiController
     3     {
     4         [Route("{id}/name")] 6         public string getName(string id)
     7         {
     8             return id + "@boss";
     9         }
    10     }

    4. 接下来我们需要将web api进行配置和部署。该Katana登场了。

    首先按照Owin约定我们得添加一个用于Startup的类,这个类中需要有一个签名为Configuration(IAppBuilder app)的方法:

    public class Startup
    {
            public void Configuration(IAppBuilder appBuilder)
            {var configuration = new HttpConfiguration();
                configuration.MapHttpAttributeRoutes();//配置web api的router
                appBuilder.UseWebApi(configuration);//这个扩展是由package Microsoft.AspNet.WebApi.Owin提供,它负责注册web api到owin的处理管道中,
                                //并在处理请求时将Owin语义与web api中的语义进行转换 } }

    我们在Main函数中添加如下代码:

    WebApp.Start<Startup>("http://localhost:8088/");//WebApp利用katana实现的OwinHttpListener来在指定url上监听http请求。
    
    Console.WriteLine("Started!");
    Console.ReadKey();

    这样我们就基于katana实现了在console app中运行web api了。

    F5运行以下,看看效果。

    然后可以通过浏览器访问http://localhost:8088/api/persons/123/name,应该能看到如下画面

    Add Authenticate Middleware                           

    接下来我们通过添加owin middleware的方式来为web api添加保护机制(Authentication)。在这之前我先解释下关于Middleware的基础知识。

    Middleware是一组Owin server在处理http请求的时候会轮流调用到的模块,他们通过调用IAppBuilder的Use扩展方法来注册。运行时的Middleware的调用顺序与注册顺序一致。

    并且对管道中下一Middleware的调用是由当前执行的Middleware来执行。具体到接口来说是这样:

    1. OWIN定义了一个Middleware的执行接口Func<IDictionary<string,object>,Task>,然后要求每个Middleware的定义需满足如下条件:

    •     提供接受一个类型为执行接口类型(Func<IDictionary<string,object>,Task>)的构造函数
    •  提供一个满足如下签名的方法Task Invoke(IDictionary<string,object> parameters)

    也就是说,owin将每个middleware最后都抽象成了一个函数,这个函数接受IDictionary作为参数,返回一个执行具体处理的Task.

    2. OWIN在创建每个Middleware的实例时,会根据注册顺序传入当前Middleware在执行管道中的下一个Middleware的执行接口。Middleware需要存储起来后续调用。

    3. 当处理请求时,Server会调用管道中第一个Middleware的Invoke方法,然后由该Middleware决定处理完请求后是否调用下一个Middleware. 在调用Invoke时,Owin server

    会将当前请求的所有上下文属性传入该Dictionary对象中。详细的上下文属性列表见官方文档

    我们在当前项目中新建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)
            {
                Console.WriteLine("Authenticating");
                string queryString = parameters["owin.RequestQueryString"] as string;//获取http请求的query string
                var respStream = parameters["owin.ResponseBody"] as Stream;//获取http请求的response stream
                var streamWriter = new StreamWriter(respStream);
                var queryDic = ParseQueryString(queryString);
    
                const string tokenKey = "token";
                const string predefineToken = "88888888";
                if (!queryDic.ContainsKey(tokenKey)||queryDic[tokenKey]!=predefineToken)//检查请求中所带token是否合法,此处仅为测试需要,直接硬编码。
                {
             //如果token非法,则直接写入Access Denied到response中。停止继续执行管道中其他middleware. streamWriter.WriteLine(
    "Access Denied!"); streamWriter.Flush(); return; }
          
    var identity = new GenericIdentity("boss zhang"); parameters["server.User"] = new GenericPrincipal(identity, new string[] { "admin" });//token合法,生成principal对象到parameters中,key "server.User"
                                                            //用于存储当前请求的user信息,相当于HttpContext.User
    if (nextAppFunc != null) { await nextAppFunc.Invoke(parameters);//继续执行管道中下一个middleware } } private Dictionary<string, string> ParseQueryString(string originalString) { string[] queryStringItems = originalString.Split(new string[] { "&" }, StringSplitOptions.RemoveEmptyEntries); var queryStringDic = new Dictionary<string, string>(); foreach (var item in queryStringItems) { string[] queryStringKvp = item.Split(new string[] { "=" }, StringSplitOptions.None); if (queryStringKvp.Length == 2) { queryStringDic[queryStringKvp[0]] = queryStringKvp[1]; } } return queryStringDic; }
    }

    然后再给之前定义的PersonController加上Authorize Attribute加以保护。

    F5运行,然后在浏览器中访问以下url: http://localhost:8088/api/persons/123/name?token=88888888.依然能得到之前正确的返回。

    如果去掉token=88888888,则得到如下结果。

    这说明我们的AuthenticateMiddle发挥作用了。

     完整代码见https://github.com/lbwxly/OwinSample.git

    参考文档:

    http://www.dotnetcurry.com/signalr/915/owin-katana-signalr-web-server

    https://ovaismehboob.com/2014/12/01/understanding-owin-by-developing-a-custom-owin-middleware-component/

  • 相关阅读:
    在Java中如何优雅地判空
    软件可以流氓到什么程度?从卸载步骤就可以看出来!
    面试中常问的List去重问题,你都答对了吗?
    为什么程序员都不喜欢使用switch而使用if来做条件跳转
    那些年,我们一起卸载过的软件…
    趣图:当我捕获Bug的时候
    9个成功的微服务设计的基础知识
    5.1 包装类
    4.9 初始化块
    4.8 继承与组合
  • 原文地址:https://www.cnblogs.com/Code-life/p/7440872.html
Copyright © 2020-2023  润新知