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

本文详细介绍了ASP.NET Web API中实现跨域资源共享(CORS)的机制,包括CorsPolicy、CorsPolicyProvider及CorsPolicyProviderFactory的作用与实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从编程的角度来讲,ASP.NET Web API针对CORS的实现仅仅涉及到HttpConfiguration的扩展方法EnableCorsEnableCorsAttribute特性。但是整个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的应用编程接口中,围绕着这6CORS响应报头的授权策略通过类型System.Web.Cors.CorsPolicy来表示。CorsPolicy具有如下6个属性正好与上面这6CORS响应报头一一对应。

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

#region 程序集 System.Web.Cors, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// E:\RSS_Ruichi\RSS_Project\RCSoft\RSS_SSDDS\RSS_SSDDS.API\packages\Microsoft.AspNet.Cors.5.2.7\lib\net45\System.Web.Cors.dll
#endregion

using System.Collections.Generic;

namespace System.Web.Cors
{
    public class CorsPolicy
    {
        public CorsPolicy();

        public bool AllowAnyHeader { get; set; }
        public bool AllowAnyMethod { get; set; }
        public bool AllowAnyOrigin { get; set; }
        // Access-Control-Expose-Headers
        public IList<string> ExposedHeaders { get; }
        // Access-Control-Allow-Headers
        public IList<string> Headers { get; }
        // Access-Control-Allow-Methods
        public IList<string> Methods { get; }
        // Access-Control-Allow-Origin
        public IList<string> Origins { get; }
        // Access-Control-Max-Age
        public long? PreflightMaxAge { get; set; }
        // Access-Control-Allow-Credentials
        public bool SupportsCredentials { get; set; }

        public override string ToString();
    }
}

二、CorsPolicyProvider

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

#region 程序集 System.Web.Http.Cors, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// E:\RSS_Ruichi\RSS_Project\RCSoft\RSS_SSDDS\RSS_SSDDS.API\packages\Microsoft.AspNet.WebApi.Cors.5.2.7\lib\net45\System.Web.Http.Cors.dll
#endregion

using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Cors;

namespace System.Web.Http.Cors
{
    //
    // 摘要:
    //     Provides an abstraction for getting the System.Web.Cors.CorsPolicy.
    public interface ICorsPolicyProvider
    {
        //
        // 摘要:
        //     Gets the System.Web.Cors.CorsPolicy.
        //
        // 参数:
        //   request:
        //     The request.
        //
        //   cancellationToken:
        //     The cancellation token.
        //
        // 返回结果:
        //     The System.Web.Cors.CorsPolicy.
        Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken);
    }
}

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

#region 程序集 System.Web.Http.Cors, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// E:\RSS_Ruichi\RSS_Project\RCSoft\RSS_SSDDS\RSS_SSDDS.API\packages\Microsoft.AspNet.WebApi.Cors.5.2.7\lib\net45\System.Web.Http.Cors.dll
#endregion

using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Cors;

namespace System.Web.Http.Cors
{
    //
    // 摘要:
    //     This class defines an attribute that can be applied to an action or a controller
    //     to enable CORS. By default, it allows all origins, methods and headers.
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
    public sealed class EnableCorsAttribute : Attribute, ICorsPolicyProvider
    {
        //
        // 摘要:
        //     Initializes a new instance of the System.Web.Http.Cors.EnableCorsAttribute class.
        //
        // 参数:
        //   origins:
        //     Comma-separated list of origins that are allowed to access the resource. Use
        //     "*" to allow all.
        //
        //   headers:
        //     Comma-separated list of headers that are supported by the resource. Use "*" to
        //     allow all. Use null or empty string to allow none.
        //
        //   methods:
        //     Comma-separated list of methods that are supported by the resource. Use "*" to
        //     allow all. Use null or empty string to allow none.
        public EnableCorsAttribute(string origins, string headers, string methods);
        //
        // 摘要:
        //     Initializes a new instance of the System.Web.Http.Cors.EnableCorsAttribute class.
        //
        // 参数:
        //   origins:
        //     Comma-separated list of origins that are allowed to access the resource. Use
        //     "*" to allow all.
        //
        //   headers:
        //     Comma-separated list of headers that are supported by the resource. Use "*" to
        //     allow all. Use null or empty string to allow none.
        //
        //   methods:
        //     Comma-separated list of methods that are supported by the resource. Use "*" to
        //     allow all. Use null or empty string to allow none.
        //
        //   exposedHeaders:
        //     Comma-separated list of headers that the resource might use and can be exposed.
        //     Use null or empty string to expose none.
        public EnableCorsAttribute(string origins, string headers, string methods, string exposedHeaders);

        //
        // 摘要:
        //     Gets the headers that the resource might use and can be exposed.
        public IList<string> ExposedHeaders { get; }
        //
        // 摘要:
        //     Gets the headers that are supported by the resource.
        public IList<string> Headers { get; }
        //
        // 摘要:
        //     Gets the methods that are supported by the resource.
        public IList<string> Methods { get; }
        //
        // 摘要:
        //     Gets the origins that are allowed to access the resource.
        public IList<string> Origins { get; }
        //
        // 摘要:
        //     Gets or sets the number of seconds the results of a preflight request can be
        //     cached.
        public long PreflightMaxAge { get; set; }
        //
        // 摘要:
        //     Gets or sets a value indicating whether the resource supports user credentials
        //     in the request.
        public bool SupportsCredentials { get; set; }

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

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

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

#region 程序集 System.Web.Http.Cors, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// E:\RSS_Ruichi\RSS_Project\RCSoft\RSS_SSDDS\RSS_SSDDS.API\packages\Microsoft.AspNet.WebApi.Cors.5.2.7\lib\net45\System.Web.Http.Cors.dll
#endregion

using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Cors;

namespace System.Web.Http.Cors
{
    //
    // 摘要:
    //     This class defines an attribute that can be applied to an action or a controller
    //     to disable CORS.
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
    public sealed class DisableCorsAttribute : Attribute, ICorsPolicyProvider
    {
        //
        // 摘要:
        //     Initializes a new instance of the System.Web.Http.Cors.DisableCorsAttribute class.
        public DisableCorsAttribute();

        //
        // 摘要:
        //     Gets the CORS policy asynchronously.
        //
        // 参数:
        //   request:
        //     The request.
        //
        //   cancellationToken:
        //     The cancellation token assigned for this task.
        //
        // 返回结果:
        //     The newly created task for this operation.
        public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, CancellationToken cancellationToken);
    }
}

由于应用在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对象。

#region 程序集 System.Web.Http.Cors, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// E:\RSS_Ruichi\RSS_Project\RCSoft\RSS_SSDDS\RSS_SSDDS.API\packages\Microsoft.AspNet.WebApi.Cors.5.2.7\lib\net45\System.Web.Http.Cors.dll
#endregion

using System.Net.Http;

namespace System.Web.Http.Cors
{
    //
    // 摘要:
    //     Provides an abstraction for getting the System.Web.Http.Cors.ICorsPolicyProvider.
    public interface ICorsPolicyProviderFactory
    {
        //
        // 摘要:
        //     Gets the System.Web.Http.Cors.ICorsPolicyProvider for the request.
        //
        // 参数:
        //   request:
        //     The request.
        //
        // 返回结果:
        //     The System.Web.Http.Cors.ICorsPolicyProvider.
        ICorsPolicyProvider GetCorsPolicyProvider(HttpRequestMessage request);
    }
}

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

#region 程序集 System.Web.Http.Cors, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// E:\RSS_Ruichi\RSS_Project\RCSoft\RSS_SSDDS\RSS_SSDDS.API\packages\Microsoft.AspNet.WebApi.Cors.5.2.7\lib\net45\System.Web.Http.Cors.dll
#endregion

using System.Net.Http;

namespace System.Web.Http.Cors
{
    //
    // 摘要:
    //     An implementation of System.Web.Http.Cors.ICorsPolicyProviderFactory that returns
    //     the System.Web.Http.Cors.ICorsPolicyProvider from the controller or action attribute.
    public class AttributeBasedPolicyProviderFactory : ICorsPolicyProviderFactory
    {
        //
        // 摘要:
        //     Initializes a new instance of the System.Web.Http.Cors.AttributeBasedPolicyProviderFactory
        //     class.
        public AttributeBasedPolicyProviderFactory();

        //
        // 摘要:
        //     Gets or sets the default System.Web.Http.Cors.ICorsPolicyProvider.
        //
        // 返回结果:
        //     The default System.Web.Http.Cors.ICorsPolicyProvider.
        public ICorsPolicyProvider DefaultPolicyProvider { get; set; }

        //
        // 摘要:
        //     Gets the System.Web.Http.Cors.ICorsPolicyProvider for the request.
        //
        // 参数:
        //   request:
        //     The request.
        //
        // 返回结果:
        //     The System.Web.Http.Cors.ICorsPolicyProvider.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     request
        public virtual ICorsPolicyProvider GetCorsPolicyProvider(HttpRequestMessage request);
    }
}

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

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

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

四、CorsPolicyProviderFactory的注册

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

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

#region 程序集 System.Web.Http.Cors, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// E:\RSS_Ruichi\RSS_Project\RCSoft\RSS_SSDDS\RSS_SSDDS.API\packages\Microsoft.AspNet.WebApi.Cors.5.2.7\lib\net45\System.Web.Http.Cors.dll
#endregion

using System.ComponentModel;
using System.Web.Cors;
using System.Web.Http.Cors;

namespace System.Web.Http
{
    //
    // 摘要:
    //     CORS-related extension methods for System.Web.Http.HttpConfiguration.
    [EditorBrowsable(EditorBrowsableState.Never)]
    public static class CorsHttpConfigurationExtensions
    {
        //
        // 摘要:
        //     Enables the support for CORS.
        //
        // 参数:
        //   httpConfiguration:
        //     The System.Web.Http.HttpConfiguration.
        public static void EnableCors(this HttpConfiguration httpConfiguration);
        public static void EnableCors(this HttpConfiguration httpConfiguration, bool rethrowExceptions);
        //
        // 摘要:
        //     Enables the support for CORS.
        //
        // 参数:
        //   httpConfiguration:
        //     The System.Web.Http.HttpConfiguration.
        //
        //   defaultPolicyProvider:
        //     The default System.Web.Http.Cors.ICorsPolicyProvider.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     httpConfiguration
        public static void EnableCors(this HttpConfiguration httpConfiguration, ICorsPolicyProvider defaultPolicyProvider);
        public static void EnableCors(this HttpConfiguration httpConfiguration, ICorsPolicyProvider defaultPolicyProvider, bool rethrowExceptions);
        //
        // 摘要:
        //     Gets the System.Web.Cors.ICorsEngine from the System.Web.Http.HttpConfiguration.
        //
        // 参数:
        //   httpConfiguration:
        //     The System.Web.Http.HttpConfiguration.
        //
        // 返回结果:
        //     The System.Web.Cors.ICorsEngine.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     httpConfiguration
        public static ICorsEngine GetCorsEngine(this HttpConfiguration httpConfiguration);
        //
        // 摘要:
        //     Gets the System.Web.Http.Cors.ICorsPolicyProviderFactory from the System.Web.Http.HttpConfiguration.
        //
        // 参数:
        //   httpConfiguration:
        //     The System.Web.Http.HttpConfiguration.
        //
        // 返回结果:
        //     The System.Web.Http.Cors.ICorsPolicyProviderFactory.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     httpConfiguration
        public static ICorsPolicyProviderFactory GetCorsPolicyProviderFactory(this HttpConfiguration httpConfiguration);
        //
        // 摘要:
        //     Sets the System.Web.Cors.ICorsEngine on the System.Web.Http.HttpConfiguration.
        //
        // 参数:
        //   httpConfiguration:
        //     The System.Web.Http.HttpConfiguration.
        //
        //   corsEngine:
        //     The System.Web.Cors.ICorsEngine.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     httpConfiguration or corsEngine
        public static void SetCorsEngine(this HttpConfiguration httpConfiguration, ICorsEngine corsEngine);
        //
        // 摘要:
        //     Sets the System.Web.Http.Cors.ICorsPolicyProviderFactory on the System.Web.Http.HttpConfiguration.
        //
        // 参数:
        //   httpConfiguration:
        //     The System.Web.Http.HttpConfiguration.
        //
        //   corsPolicyProviderFactory:
        //     The System.Web.Http.Cors.ICorsPolicyProviderFactory.
        //
        // 异常:
        //   T:System.ArgumentNullException:
        //     httpConfiguration or corsPolicyProviderFactory
        public static void SetCorsPolicyProviderFactory(this HttpConfiguration httpConfiguration, ICorsPolicyProviderFactory corsPolicyProviderFactory);
    }
}

 

五、总结

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值