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,并添加一个接口方法:
[RoutePrefix("api/persons")]
public class PersonController : ApiController
{
[Route("{id}/name")] public string getName(string id)
{
return id + "@boss";
}
}
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 = "";
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 == )
{
queryStringDic[queryStringKvp[]] = queryStringKvp[];
}
} 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/
Host ASP.NET WebApi in Owin的更多相关文章
- Asp.Net WebApi 使用OWIN架构后,出现 “没有 OWIN 身份验证管理器与此请求相关联(No OWIN authentication manager is associated with the request)” 异常的解决办法
在Asp.Net WebApi 项目中使用OWIN模块之后,如果没有在OWIN的Startup类中配置认证方式,调用WebApi的相关Controller和Action就会出现如下异常: 出现错误. ...
- 基于ASP.NET WebAPI OWIN实现Self-Host项目实战
引用 寄宿ASP.NET Web API 不一定需要IIS 的支持,我们可以采用Self Host 的方式使用任意类型的应用程序(控制台.Windows Forms 应用.WPF 应用甚至是Windo ...
- ASP.NET WebApi OWIN 实现 OAuth 2.0
OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...
- [转]ASP.NET WebApi OWIN 实现 OAuth 2.0
OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...
- 基于OWIN ASP.NET WebAPI 使用OAUTH2授权服务的几点优化
前面在ASP.NET WEBAPI中集成了Client Credentials Grant与Resource Owner Password Credentials Grant两种OAUTH2模式,今天 ...
- ASP.NET WebApi OWIN 实现 OAuth 2.0(自定义获取 Token)
相关文章:ASP.NET WebApi OWIN 实现 OAuth 2.0 之前的项目实现,Token 放在请求头的 Headers 里面,类似于这样: Accept: application/jso ...
- Asp.net WebAPI 单元测试
现在Asp.net webapi 运用的越来越多,其单元而是也越来越重要.一般软件开发都是多层结构,上层调用下层的接口,而各层的实现人员不同,一般大家都只写自己对应单元测试.对下层的依赖我们通过IOC ...
- asp.net webapi 自托管插件式服务(转)
asp.net webapi 自托管插件式服务 webapi问世已久,稀里糊涂的人哪它都当mvc来使,毕竟已mvc使用级别的经验就可以应对webapi. webapi和mvc在asp.net5时代 ...
- asp.net webapi 自托管插件式服务
webapi问世已久,稀里糊涂的人哪它都当mvc来使,毕竟已mvc使用级别的经验就可以应对webapi. webapi和mvc在asp.net5时代合体了,这告诉我们,其实 它俩还是有区别的,要不现在 ...
随机推荐
- C++中虚函数和纯虚函数的区别与总结
首先:强调一个概念 定义一个函数为虚函数,不代表函数为不被实现的函数. 定义他为虚函数是为了允许用基类的指针来调用子类的这个函数. 定义一个函数为纯虚函数,才代表函数没有被实现. 定义纯虚函数是为了实 ...
- CocoaPods学习系列4——进阶用法
这篇文章,记录一下CocoaPods的进阶用法. 进阶用法主要体现在.podspec文件和Podfile的配置上. .podspec文件的进阶配置 以官方的一个.podspec文件示例细说: Pod: ...
- 求中位数,O(n)的java实现【利用快速排序折半查找中位数】
查找无序数组的中位数,要想时间复杂度为O(n)其实用计数排序就能很方便地实现,在此讨论使用快速排序进行定位的方法. 1.中位数定义 2.算法思想 3.Java代码实现 4.时间复杂度分析 5.附录 中 ...
- docker安装脚本
此docker安装脚本为官方提供的,可以从网上下载,此处直接把脚本内容贴上. #!/bin/sh set -e # This script is meant for quick & easy ...
- iOS-沙盒路径
iphone沙箱模型的有四个文件夹,分别是什么,永久数据存储一般放在什么位置,得到模拟器的路径的简单方式是什么.documents,tmp,app,Library.(NSHomeDirectory() ...
- CSS如何设置字体的类型、大小、颜色
设计网页时,一般设置body的字体,让其他标签继承body的字体,这样设置特别方便,但是标题标签h1到h6和表单标签(input类型)是没有继承body的字体属性的,它们的字体需要单独设置. < ...
- Ubuntu的root用户问题
在Ubuntu中系统是默认禁止root用户登入操作,要使用超级用户可以加sudo 例: sudo chown book:book /work -R 或者切换到root su root passwor ...
- 八行代码解决八皇后问题(c++)
说的有点夸装,实际上并不只是巴航代码,加上前面的变量声明之类的一共有40多行的样子吧,好像是在知乎上看到的,现在有时间再把它写下来: 其中用到了一些c++11特性,例如lambda 以及给予范围的 f ...
- webservice SOAP WSDL UDDI简介
WebServices简介 先给出一个概念 SOA ,即Service Oriented Architecture ,中文一般理解为面向服务的架构, 既然说是一种架构的话,所以一般认为 SOA 是包含 ...
- MSSQL数据库分区表
http://blog.csdn.net/lgb934/article/details/8662956 http://database.9sssd.com/mssql/art/951