从编程的角度来讲,ASP.NET Web API针对CORS的实现仅仅涉及到HttpConfiguration的扩展方法EnableCors和EnableCorsAttribute特性。但是整个CORS体系不限于此,在它们背后隐藏着一系列的类型,我们将会利用本章余下的内容对此作全面讲述,今天我们就来讨论一下用于定义CORS授权策略的EnableCorsAttribute特性背后的故事。

目录
一、CorsPolicy
二、CorsPolicyProvider
三、CorsPolicyProviderFactory
四、CorsPolicyProviderFactory的注册
五、总结

一、CorsPolicy

通过将EnableCorsAttribute特性应用到HttpController类型或者定义其中的某个Action方法上,我们可以为提供的资源定义相应的授权策略。ASP.NET Web API最终会利用这些策略对请求(包括预检请求)进行解析并生成相应的CORS响应报头。在ASP.NET Web API的应用编程接口中,CORS授权策略通过CorsPolicy类型表示。

通过《W3C的CORS规范》的介绍,我们知道针对跨域资源的授权策略不仅仅要求请求的源站点值得信任,还涉及到对请求采用的HTTP方法、携带的自定义报头和用户凭证的要求,以及针对自定义响应报头的授权等。除此之外,为了避免频繁浏览器频繁地发送预检请求,它可以将响应的结果进行缓存,而这又涉及到对缓存过期时间的控制。总得来说,这些授权策略体现在如下6个CORS响应报头上。

  • Access-Control-Allow-Origin
  • Access-Control-Expose-Headers
  • Access-Control-Allow-Methods
  • Access-Control-Allow-Headers
  • Access-Control-Max-Age
  • Access-Control-Allow-Credentials

在ASP.NET Web API的应用编程接口中,围绕着这6个CORS响应报头的授权策略通过类型System.Web.Cors.CorsPolicy来表示。CorsPolicy具有如下6个属性正好与上面这6个CORS响应报头一一对应。

   1: public class CorsPolicy

   2: {

   3:     //其他成员

   4:     public IList<string>     Origins { get; }

   5:     public IList<string>     ExposedHeaders { get; }

   6:     public IList<string>     Headers { get; }

   7:     public IList<string>     Methods { get;; }

   8:     public long?             PreflightMaxAge { get; set; }

   9:     public bool              SupportsCredentials { get; set; }

  10: }

除了上述这6个属性之外,CorsPolicy还具有如下3个布尔类型的属性(AllowAnyOrigin、AllowAnyHeader和AllowAnyMethod),它们分别表示是否支持所有的源站点、自定义请求报头和HTTP方法。

   1: public class CorsPolicy

   2: {

   3:     //其他成员

   4:     public bool AllowAnyOrigin { get; set; }

   5:     public bool AllowAnyHeader { get; set; }

   6:     public bool AllowAnyMethod { get; set; }

   7: }

二、CorsPolicyProvider

作为跨域资源请求进行授权检查的依据,同时用于生成相应的CORS报头的CorsPolicy对象通过另一个名为CorsPolicyProvider的对象来提供,所有的CorsPolicyProvider类型均实现了的接口System.Web.Http.Cors.ICorsPolicyProvider。如下面的代码片断所示,该接口具有的唯一方法GetCorsPolicyAsync会根据代表但前请求的HttpRequestMessage对象得到表示CORS授权策略的CorsPolicy对象。

   1: public interface ICorsPolicyProvider

   2: {

   3:     Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken);

   4: }

实际上我们通过应用在目标HttpController类型或者定义其中的Action方法上用于定义CORS授权策略的System.Web.Http.Cors.EnableCorsAttribute就是ICorsPolicyProvider接口的实现者之一。如下面的代码片断所示,EnableCorsAttribute同样具有6个针对CORS响应报头的属性。在实现的GetCorsPolicyAsync方法中,它就是通过这6个属性对返回的CorsPolicy对象进行初始化。

   1: [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple=false)]

   2: public sealed class EnableCorsAttribute : Attribute, ICorsPolicyProvider

   3: {    

   4:     public EnableCorsAttribute(string origins, string headers, string methods);

   5:     public EnableCorsAttribute(string origins, string headers, string methods,string exposedHeaders);

   6:  

   7:     public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken);

   8:     

   9:     public IList<string> Origins { get; }

  10:     public IList<string> ExposedHeaders { get; }

  11:     public IList<string> Headers { get; }

  12:     public IList<string> Methods { get; }

  13:     public long          PreflightMaxAge { get; set; }

  14:     public bool          SupportsCredentials { get; set; }

  15: }

授权的源站点和允许的自定义请求报头和HTTP方法,以及暴露给客户端JavaScript程序的自定义响应报头均可以直接通过构造函数参数来指定。对于这4个参数,我们可以指定某个单一的值(比如origin="http://www.artech.com"),也可以指定一个通过逗号分割的列表(比如origin="http://www.artech.com, http://www.jinnan.me")。除了exposedHeaders之外,我们还可以指定“*”作为其参数值,意味着不对此作任何限制,它们会控制生成CorsPolicy对象的3个对应布尔类型属性值(AllowAnyOrigin、AllowAnyHeader和AllowAnyMethod)。

除了EnableCorsAttribute特性之外,在“System.Web.Http.Cors”命名空间下还定义着另一个与之相对的特性DisableCorsAttribute。顾名思义,如果DisableCorsAttribute特性被应用到某个HttpController类型或者定义其中的某个Action方法上,意味着目标HttpController或者Action不支持跨域资源共享。如下面的代码片断所示,在实现的GetCorsPolicyAsync方法中,并没有一个具体的CorsPolicy返回。

   1: [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple=false)]

   2: public sealed class DisableCorsAttribute : Attribute, ICorsPolicyProvider

   3: {

   4:     public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken)

   5:     {

   6:         return Task.FromResult<CorsPolicy>(null);

   7:     }

   8: }

由于应用在Action方法上的CorsPolicyProvider特性比应用在HttpController类型上的特性具有更好的选择优先级,所以对于一个定义了众多Action方法的HttpController类型来说,如果绝大部分Action方法均需要提供跨域资源共享的支持并具有相同的资源授权策略,可以直接在HttpController类型上应用EnableCorsAttribute特性并作相应的设置。对于不需要支持跨域资源共享的Action来说,直接在对应的方法上应用DisableCorsAttribute特性即可。如果某个Action具有特殊的授权需求,可以通过应用的EnableCorsAttribute特性作针对性设置。反之亦然。

三、CorsPolicyProviderFactory

CorsPolicyProvider用于提供用于描述CORS授权策略的CorsPolicy对象,其自身又通过对应的CorsPolicyProviderFactory来创建,所有的CorsPolicyProviderFactory类型均实现了接口System.Web.Http.Cors.ICorsPolicyProviderFactory。如下面的代码片断所示,该接口具有的唯一方法GetCorsPolicyProvider会根据代表当前请求的HttpRequestMessage对象来提供对应的CorsPolicyProvider对象。

   1: public interface ICorsPolicyProviderFactory

   2: {

   3:     ICorsPolicyProvider GetCorsPolicyProvider(HttpRequestMessage request);

   4: }

由于提供的两个具体CorsPolicyProvider类型(EnableCorsAttribute和DisableCorsAttribute)都是特性,所以ASP.NET Web API定义了如下一个AttributeBasedPolicyProviderFactory类型的CorsPolicyProviderFactory以解析特性的方式提供对应的CorsPolicyProvider。

   1: public class AttributeBasedPolicyProviderFactory : ICorsPolicyProviderFactory

   2: {    

   3:     public virtual ICorsPolicyProvider GetCorsPolicyProvider(HttpRequestMessage request);

   4:     public ICorsPolicyProvider DefaultPolicyProvider { get; set; }

   5: }

实现在GetCorsPolicyProvider方法中的CorsPolicyProvider提供机制很简单:它直接利用注册到当前ServicesContainer上的HttpActionSelector根据当前请求获取用于描述目标Action的HttpActionDescriptor对象,然后调用其GetCustomAttributes<T>方法得到应用到对应Action方法上的第一个实现了ICorsPolicyProvider接口的特性。如果这样的特性不存在,则获取描述所在HttpController类型的HttpControllerDescritor对象,采用同样的方式得到应用在目标HttpController类型上的第一个实现了ICorsPolicyProvider接口的特性。

关于针对目标Action的选择问题,有一个核心的核心的细节值得关注:如果当前请求并非真正的跨域资源请求,而仅仅是一个采用“OPTIONS”作为HTTP方法的预检请求(Preflight Request),利用注册的HttpActionSelector根据当前请求是无法将目标Action选择出来的,所以需要将请求的HTTP方法替换成真正跨域资源请求采用的HTTP方法。通过上面针对W3C的CORS规范的介绍我们知道,此HTTP方法可以通过预检请求的“Access-Control-Request-Method”报头获得。实际上在上一个“通过自定义HttpMessageHandler实现CORS”的实例中,我们已经对此作个过演示了。

从上面给出的针对AttributeBasedPolicyProviderFactory的定义可以看出,除了实现的方法GetCorsPolicyProvider方法之外,它还具有一个DefaultPolicyProvider属性。该属性表示默认采用的CorsPolicyProvider,如果没有任何实现ICorsPolicyProvider接口的特性被应用到目标Action方法和它所在的HttpController类型上,该属性将会作为GetCorsPolicyProvider方法的返回值。

四、CorsPolicyProviderFactory的注册

ASP.NET Web API默认使用的CorsPolicyProviderFactory需要注册到当前的HttpConfiguration上。具体来说,所谓注册CorsPolicyProviderFactory实际上就是将它保存到当前HttpConfiguration的Properties属性表示的字典中。CorsPolicyProviderFactory的注册可以通过HttpConfiguration如下所示的扩展方法SetCorsPolicyProviderFactory来完成。

另一扩展方法GetCorsPolicyProviderFactory 则用于获取成功注册的CorsPolicyProviderFactory。如果调用该方法CorsPolicyProviderFactory尚未被注册,一个AttributeBasedPolicyProviderFactory对象会被创建出来并注册到HttpConfiguration上。

   1: public static class CorsHttpConfigurationExtensions

   2: {

   3:     //其他成员

   4:     public static ICorsPolicyProviderFactory GetCorsPolicyProviderFactory(this HttpConfiguration httpConfiguration);

   5:     public static void SetCorsPolicyProviderFactory(this HttpConfiguration httpConfiguration, ICorsPolicyProviderFactory corsPolicyProviderFactory);

   6: }

五、总结

综上所述,CorsPolicy用于描述具体的CORS资源授权策略,它由CorsPolicyProvider来提供,而后者又通过CorsPolicyProviderFactory来创建。如右图所示的UML揭示了CorsPolicy、CorsPolicyProvider和CorsPolicyProviderFactory相关接口和类之间的关系。对于这些类型来说,除了CorsPolicy定义在程序集System.Web.Cors.dll,其余的类型均定义在程序集System.Web.Http.Cors.dll中。

CORS系列文章
[1] 同源策略与JSONP

[2] 利用扩展让ASP.NET Web API支持JSONP

[3] W3C的CORS规范

[4] 利用扩展让ASP.NET Web API支持CORS

[5] ASP.NET Web API自身对CORS的支持: 从实例开始

[6] ASP.NET Web API自身对CORS的支持: CORS授权策略的定义和提供

[7] ASP.NET Web API自身对CORS的支持: CORS授权检验的实施

[8] ASP.NET Web API自身对CORS的支持: CorsMessageHandler

ASP.NET Web API自身对CORS的支持: EnableCorsAttribute特性背后的故事的更多相关文章

  1. ASP.NET Web API自身对CORS的支持:从实例开始

    在<通过扩展让ASP.NET Web API支持W3C的CORS规范>中我们通过自定义的HttpMessageHandler为ASP.NET Web API赋予了跨域资源共享的能力,具体来 ...

  2. ASP.NET Web API自身对CORS的支持: CORS授权检验的实施

    通过<EnableCorsAttribute特性背后的故事>我们知道:由CorsPolicyProvider提供的CorsPolicy表示目标Action采用的资源授权策略,ASP.NET ...

  3. ASP.NET Web API 2 对 CORS 的支持

    CORS概念 跨域资源共享 (CORS) 是一种万维网联合会 (W3C) 规范(通常被认为是 HTML5 的一部分),它可让 JavaScript 克服由浏览器施加的同域策略安全限制. 所谓同域策略, ...

  4. Web API 2 对 CORS 的支持

    Web API 2 对 CORS 的支持 CORS概念 跨域资源共享 (CORS) 是一种万维网联合会 (W3C) 规范(通常被认为是 HTML5 的一部分),它可让 JavaScript 克服由浏览 ...

  5. 在ASP.NET Web API中实现CORS(跨域资源共享)

    默认情况下,是不允许网页从不同的域访问服务器资源的,访问遵循"同源"策略的原则. 会遇到如下的报错: XMLHttpRequest cannot load http://local ...

  6. ASP.NET Web API实践系列04,通过Route等特性设置路由

    ASP.NET Web API路由,简单来说,就是把客户端请求映射到对应的Action上的过程.在"ASP.NET Web API实践系列03,路由模版, 路由惯例, 路由设置"一 ...

  7. 通过扩展让ASP.NET Web API支持W3C的CORS规范

    让ASP.NET Web API支持JSONP和W3C的CORS规范是解决"跨域资源共享"的两种途径,在<通过扩展让ASP.NET Web API支持JSONP>中我们 ...

  8. 跨域资源共享(CORS)在ASP.NET Web API中是如何实现的?

    在<通过扩展让ASP.NET Web API支持W3C的CORS规范>中,我们通过自定义的HttpMessageHandler自行为ASP.NET Web API实现了针对CORS的支持, ...

  9. JavaScript跨域调用、JSONP、CORS与ASP.NET Web API[共8篇]

    [第1篇] 同源策略与JSONP 浏览器是访问Internet的工具,也是客户端应用的宿主,它为客户端应用提供一个寄宿和运行的环境.而这里所说的应用,基本是指在浏览器中执行的客户端JavaScript ...

随机推荐

  1. Zookeeper操作

    Zookeeper操作 注意搭建: 1.集群规模不小于3个节点 2.服务器之间系统时间要保持一致 1.搭建步骤: 1.解压安装包 2.设置zookeeper环境变量 3.修改配置文件————zoo.c ...

  2. redmine整合GIT版本库

    redmine整合GIT版本库   服务器的环境: Ubuntu 11.10 64位 Redmine 1.4.5.stable.10943 git version 1.7.5.4 + gitolite ...

  3. LINUX 文件权限详解

    ls -l // 查看文件的权限 等价于 ll 文件的权限信息查看 -rw-rw-r-- 1 ceshi ceshi 891 Aug 8 17:28 server drwxrwxr-x 10 cesh ...

  4. HDU5800 To My Girlfriend(DP)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5800 Description Dear Guo I never forget the mom ...

  5. 【原】iOS学习之三种拨打电话方式的比较

    拨打电话小编从网上找到三种,在这里做一些总结和比较 1.基本使用 NSString *str = [[NSMutableString alloc] initWithFormat:@"tel: ...

  6. 【模拟】POJ 3087

    直达–>POJ 3087 Shuffle'm Up 题意:一开始没怎么看明白,注意现是从S2里拿牌放在最底下,再放S1,这样交叉放(我一开始以为是S1和S2随意哪个先放,分别模拟取最小),然后在 ...

  7. Flask下如何处理Requests 上传中文文件名的问题

    一.问题的由来     最近有个项目,叫做文档服务资源中心,类似于七牛,为各个业务系统提供统一的文件资源服务,包括文件的存储.操作管理.下载.预览等.在做文件存储的时候,遇到了这个当指定上传的文件名为 ...

  8. HDU 2087  KMP模板题

    1.HDU 2087 2.题意:一个主串,一个子串,求子串在主串里出现了几次. 3.总结:看了题解,还是不太懂.. //#include<iostream>#include<cmat ...

  9. jQuery 获取屏幕高度、宽度

    做手机Web开发做浏览器兼容用到了,所以在网上找了些汇总下. alert($(window).height()); //浏览器当前窗口可视区域高度alert($(document).height()) ...

  10. [LintCode] Find Peak Element 求数组的峰值

    There is an integer array which has the following features: The numbers in adjacent positions are di ...