开发者社区> 杰克.陈> 正文

ASP.NET Web API 2 对 CORS 的支持

简介: 原文 ASP.NET Web API 2 对 CORS 的支持 ASP.NET Web API 2 对 CORS 的支持 Brock Allen 跨域资源共享 (CORS) 是一种万维网联合会 (W3C) 规范(通常被认为是 HTML5 的一部分),它可让 JavaScript 克服由浏览器施加的同域策略安全限制。
+关注继续查看

原文 ASP.NET Web API 2 对 CORS 的支持

ASP.NET Web API 2 对 CORS 的支持

Brock Allen

跨域资源共享 (CORS) 是一种万维网联合会 (W3C) 规范(通常被认为是 HTML5 的一部分),它可让 JavaScript 克服由浏览器施加的同域策略安全限制。 所谓同域策略,就是 JavaScript 只能对包含网页的同一个域进行 AJAX 回调(其中,“域”就是主机名、协议和端口号的组合)。 例如,http://foo.com 中某个网页上的 JavaScript 无法对 http://bar.com(或 http://www.foo.com、https://foo.com 或 http://foo.com:999 等)进行 AJAX 调用。

CORS 可让服务器指明允许哪些域对它们进行调用,从而放宽这种限制。 CORS 是由浏览器强制执行的,并且必须在服务器上实现,而最新版本的 ASP.NET Web API 2 全面支持 CORS。 通过 Web API 2,您可以对策略进行配置以允许不同域的 JavaScript 客户端访问您的 API。

CORS 基本信息

由于 Web API 完全按照该规范来实现,因此,为了使用 Web API 中的新 CORS 功能,详细了解 CORS 本身将大有帮助。 这些详细内容现在看起来可能都是理论之谈,但对于以后了解 Web API 中的可用设置来说将十分有用:在您调试 CORS 时,这些内容有助于您更快速地解决问题。

CORS 的一般机制是,当 JavaScript 尝试进行跨域 AJAX 调用时,浏览器会通过在 HTTP 请求中发送标头(如“Origin”)来“询问”服务器是否允许进行这样的调用。 服务器通过在响应中返回 HTTP 标头(如“Access-Control-Allow-Origin”)指明允许的操作。 这种权限检查将针对客户端调用的每个不同 URL 进行,这就意味着不同的 URL 可以具有不同权限。

除域之外,CORS 还可以让服务器指明允许使用的 HTTP 方法、客户端可以发送的 HTTP 请求标头、客户端可以读取的 HTTP 响应标头以及是否允许浏览器自动发送或接收凭据(Cookie 或授权标头)。 其他请求和响应标头指明允许使用其中的哪些功能。 图 1 总结了这些标头(请注意,一些功能没有在响应中发送的标头,仅有响应)。

图 1 CORS HTTP 标头

权限/功能 请求标头 响应标头
Access-Control-Allow-Origin
HTTP 方法 Access-Control-Request-Method Access-Control-Allow-Method
请求标头 Access-Control-Request-Headers Access-Control-Allow-Headers
响应标头   Access-Control-Expose-Headers
凭据   Access-Control-Allow-Credentials
缓存预检响应   Access-Control-Max-Age

浏览器可通过两种不同的方式向服务器请求这些权限:简单 CORS 请求和预检 CORS 请求。

简单 CORS 请求。下面是简单 CORS 请求的示例:

 

 
POST http://localhost/WebApiCorsServer/Resources/ HTTP/1.1
Host: localhost
Accept: */*
Origin: http://localhost:55912
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
value1=foo&value2=5

响应如下:

 

 
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: http://localhost:55912
Content-Length: 27
{"Value1":"foo","Value2":5}

该请求是从 http://localhost:55912 到 http://localhost 的跨域请求,而浏览器会在该请求中添加一个“Origin”HTTP 标头以指示服务器的调用域。 服务器用“Access-­Control-Allow-Origin”响应标头进行响应,指明允许使用此域。 浏览器强制执行服务器的策略,而 JavaScript 将接收其正常成功回调。

服务器或者可以使用来自该请求的确切域值做出响应,或者可以使用指明允许使用任何域的“*”值做出响应。 如果服务器还未允许该调用域,则“Access-Control-Allow-Origin”标头会缺失,并会引起该调用 JavaScript 的错误回调。

请注意,进行简单 CORS 请求时,仍会对服务器进行调用。 如果您对 CORS 了解不深,可能会觉得奇怪,但这种行为无异于浏览器已构造 <form> 元素并进行正常 POST 请求的情况。 CORS 不会阻止对服务器的调用;但它会阻止调用 JavaScript 接收结果。 如果您要阻止调用方调用服务器,则需要在服务器代码中实现某种授权(可能要使用 [Authorize] 授权筛选器属性)。

前面的示例称为简单 CORS 请求,因为来自客户端的 AJAX 调用的类型或者是 GET,或者是 POST;Content-Type 是 application/x-www-form-­urlencoded、multipart/form-data 或 text/plain 中的一个;未发送任何其他请求标头。 如果 AJAX 调用是另一个 HTTP 方法,Content-Type 是某个其他值,或者客户端想要发送其他请求标头,则会将该请求视为预检请求。 预检请求的机制略有不同。

预检 CORS 请求。如果 AJAX 调用不是简单请求,则它需要一个预检 CORS 请求,此请求只不过是一个发送到服务器以获取权限的附加 HTTP 请求。 此预检请求由浏览器自动发出,并使用 OPTIONS HTTP 方法。 如果服务器成功响应该预检请求并授予权限,则浏览器将执行 JavaScript 正在尝试执行的实际 AJAX 调用。

如果关心的是性能问题(以及何时出现性能问题),则浏览器可通过在预检响应中包含 Access-Control-Max-Age 标头来缓存此预检请求的结果。 该值包含可对权限进行缓存的秒数。

下面是预检 CORS 请求的示例:

 

 
OPTIONS http://localhost/WebApiCorsServer/Resources/1 HTTP/1.1
Host: localhost
Access-Control-Request-Method: PUT
Origin: http://localhost:55912
Access-Control-Request-Headers: content-type
Accept: */*

预检响应如下:

 

 
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://localhost:55912
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Headers: content-typeAccess-Control-Max-Age: 600

下面是实际 AJAX 请求:

 

 
PUT http://localhost/WebApiCorsServer/Resources/1 HTTP/1.1
Host: localhost
Content-Length: 27
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://localhost:55912
Content-Type: application/json
{"value1":"foo","value2":5}

AJAX 响应如下:

 

 
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: http://localhost:55912
Content-Length: 27
{"Value1":"foo","Value2":5}

请注意,此示例中触发了一个预检 CORS 请求,因为 HTTP 方法为 PUT,且客户端需要发送 Content-Type 标头以指示该请求包含 application/json。 在预检请求中(也包括 Origin),Access-Control-Request-Method 和 Access-Control-Request-Headers 请求标头用于请求 HTTP 方法的类型以及客户端希望发送的其他标头的权限。

服务器授予权限(并设置一段预检缓存持续时间),浏览器随后允许实际 AJAX 调用。 如果服务器没有向任何请求的功能授予权限,则相应的响应标头将不存在,并且不会进行 AJAX 调用,也不会引起 JavaScript 错误回调。

之前的 HTTP 请求和响应是使用 Firefox 完成的。 如果使用 Internet Explorer,则您会注意到请求了一个附加 Accept 标头。 如果使用 Chrome,则会看到另外请求了 Accept 和 Origin。 有趣的是,您不会在 Access-Control-­Allow-Headers 中看到 Accept 或 Origin,因为已指定它们是隐含的,可以省略(Web API 会将其省略)。 是否需要实际请求 Origin 和 Accept 是一个争论的焦点,但鉴于目前这些浏览器的使用方式,您的 Web API CORS 策略极有可能需要将它们包括进来。 遗憾的是,浏览器供应商对该规范的解读似乎并不一致。

响应标头。为客户端提供使用 Access-Control-Expose-Headers 响应标头来访问响应标头权限的操作十分方便。 下面是允许调用 JavaScript 访问自定义响应标头“bar”的 HTTP 响应示例:

 

 
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: http://localhost:55912Access-Control-Expose-Headers: bar
bar: a bar value
Content-Length: 27
{"Value1":"foo","Value2":5}

JavaScript 客户端只需使用 XMLHttpRequest getResponseHeader 函数读取该值。 下面是使用 jQuery 的示例:

 

 
$.ajax({
  url: "http://localhost/WebApiCorsServer/Resources/1",
  // other settings omitted
}).done(function (data, status, xhr) {
  var bar = xhr.getResponseHeader("bar");
  alert(bar);
});

凭据和身份验证。CORS 最容易引起混淆的地方与凭据和身份验证有关。 通常,使用 Web API 进行的身份验证可通过 Cookie 或 Authorization 标头来进行(还有其他方法,但这两种方法最常用)。 在正常浏览器活动中,如果此前已采用其中一种方法,则浏览器会在后续请求中将这些值隐式传递到服务器。 不过,对于跨域 AJAX,必须在 JavaScript 中显式请求这种隐式值传递(通过 XMLHttpRequest 上的 withCredentials 标记),且必须在服务器的 CORS 策略中显式允许这种传递(通过 Access-Control-Allow-Credentials 响应标头)。

下面是使用 jQuery 设置 withCredentials 标记进行 JavaScript 客户端设置的示例:

 

 
$.ajax({
  url: "http://localhost/WebApiCorsServer/Resources/1",
  xhrFields: {
    withCredentials: true
  }
  // Other settings omitted
});

withCredentials 标记有两个用途:如果服务器发出 Cookie,则浏览器可接受该 Cookie;如果浏览器具有 Cookie,则它可将该 Cookie 发送到服务器。

下面是允许凭据的 HTTP 响应的示例:

 

 
HTTP/1.1 200 OK
Set-Cookie: foo=1379020091825
Access-Control-Allow-Origin: http://localhost:55912
Access-Control-Allow-Credentials: true

Access-Control-Allow-Credentials 响应标头有两个用途:如果响应具有 Cookie,则浏览器可接受该 Cookie;如果浏览器在请求时发送了 Cookie,则 JavaScript 客户端可接收该调用的结果。 换句话说,如果客户端设置 withCredentials,则该客户端仅会在服务器(在响应中)允许凭据的情况下在 JavaScript 中看到成功回调。 如果设置了 withCredentials 但服务器不允许凭据,则客户端不能访问结果,并且会引起客户端错误回调。

如果使用 Authorization 标头而不使用 Cookie(例如,在使用基本或集成 Windows 身份验证时),则适用同样一组规则和行为。 有关使用凭据和 Authorization 标头的注意事项:服务器不必在 Access-Control-Allow-Headers CORS 响应标头中显式授予 Authorization 标头。

请注意,在使用 Access-Control-Allow-Credentials CORS 响应标头时,如果服务器发出此标头,则“*”通配符值不能用于 Access-Control-Allow-Origin。 相反,CORS 规范要求使用显式域。 Web API 框架会为您处理这种情况,我之所以在此提及,是因为您可能会在调试期间注意到这种行为。

对凭据和身份验证的这种讨论有一个有趣的问题。 到目前为止,描述一直是针对浏览器隐式发送凭据的情况。 JavaScript 客户端可以显式发送凭据(同样,通常是通过 Authorization 标头)。 如果是这种情况,则上述与凭据有关的规则或行为都不适用。

在这种情况下,客户端会对该请求显式设置 Authorization 标头,无需对 XMLHttpRequest 设置 withCredentials。 此标头会触发一个预检请求,并且服务器需要通过 Access-Control-Allow-Headers CORS 响应标头来允许 Authorization 标头。 同样,服务器无需发出 Access-Control-­Allow-Credentials CORS 响应标头。

下面是用于显式设置 Authorization 标头的客户端代码的示例:

 

 
$.ajax({
  url: "http://localhost/WebApiCorsServer/Resources/1",
  headers: {
    "Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3Mi..."
  }
  // Other settings omitted
});

下面是预检请求:

 

 
OPTIONS http://localhost/WebApiCorsServer/Resources/1 HTTP/1.1
Host: localhost
Access-Control-Request-Method: GET
Origin: http://localhost:55912
Access-Control-Request-Headers: authorization
Accept: */*

下面是预检响应:

 

 
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: authorization

在 Authorization 标头中显式设置令牌值是一种较为安全的身份验证方法,因为您可以避免遭受“跨网站请求伪造”(CSRF) 攻击。 您可在 Visual Studio 2013 新增的单页应用程序 (SPA) 模板中看到这种方法。

至此,您已经了解了 HTTP 级别上的 CORS 基础知识,接下来,我将向您说明如何使用新的 CORS 框架从 Web API 发出这些标头。

Web API 2 对 CORS 的支持

Web API 中对 CORS 的支持是一个完整框架,允许应用程序定义 CORS 请求的权限。 该框架围绕一个策略方案展开,该策略方案可让您指定针对进入应用程序的任何给定请求而允许的 CORS 功能。

首先,为了获取该 CORS 框架,您必须从 Web API 应用程序引用 CORS 库(默认情况下,Visual Studio 2013 中的任何 Web API 模板都不引用这些库)。 该 Web API CORS 框架通过 NuGet 作为 Microsoft.AspNet.WebApi.Cors 程序包提供。 如果您不使用 NuGet,则该框架也可作为 Visual Studio 2013 的一部分提供,并且您需要引用两个程序集:System.Web.Http.Cors.dll 和 System.Web.Cors.dll(在我的计算机上,这两个程序集位于 C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Stack 5\Packages)。

接下来,为了表达该策略,Web API 提供了一个名为 EnableCorsAttribute 的自定义属性类。 此类包含允许的域、HTTP 方法、请求标头、响应标头以及是否允许使用凭据等方面的属性(它们对前面所述的 CORS 规范的所有详细信息进行建模)。

最后,为了让 Web API CORS 框架处理 CORS 请求并发出适当的 CORS 响应标头,该类必须检查进入应用程序的每个请求。 Web API 通过消息处理程序提供用于这种拦截操作的扩展点。 Web API CORS 框架会相应地实现一个名为 CorsMessageHandler 的消息处理程序。 对于 CORS 请求,该处理程序会查询在所调用方法的属性中表达的策略,并发出适当的 CORS 响应标头。

EnableCorsAttribute。EnableCorsAttribute 类就是应用程序表达其 CORS 策略的方式。 EnableCorsAttribute 类有一个可接受三个或四个参数的重载构造函数。 这些参数(依次)为:

  1. 允许域列表
  2. 允许请求标头列表
  3. 允许 HTTP 方法列表
  4. 允许响应标头列表(可选)

还有一个允许使用凭据的属性 (Supports­Credentials) 以及另一个用于指定预检缓存持续时间值的属性 (PreflightMaxAge)。

图 2 显示了将 EnableCors 属性应用于控制器上各种方法的示例。 用于各种 CORS 策略设置的值应该与前面的示例中说明的 CORS 请求和响应匹配。

图 2 将 EnableCors 属性应用于操作方法

 

 
public class ResourcesController : ApiController
{
  [EnableCors("http://localhost:55912", // Origin
              null,                     // Request headers
              "GET",                    // HTTP methods
              "bar",                    // Response headers
              SupportsCredentials=true  // Allow credentials
  )]
  public HttpResponseMessage Get(int id)
  {
    var resp = Request.CreateResponse(HttpStatusCode.NoContent);
    resp.Headers.Add("bar", "a bar value");
    return resp;
  }
  [EnableCors("http://localhost:55912",       // Origin
              "Accept, Origin, Content-Type", // Request headers
              "PUT",                          // HTTP methods
              PreflightMaxAge=600             // Preflight cache duration
  )]
  public HttpResponseMessage Put(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
  [EnableCors("http://localhost:55912",       // Origin
              "Accept, Origin, Content-Type", // Request headers
              "POST",                         // HTTP methods
              PreflightMaxAge=600             // Preflight cache duration
  )]
  public HttpResponseMessage Post(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
}

请注意,每个构造函数参数都是一个字符串。 通过指定逗号分隔列表,可以表示多个值(图 2 中的允许请求标头就指定了这种列表)。 如果您想允许所有域、请求标头或 HTTP 方法,则可以使用“*”作为值(对于响应标头仍须显式指定)。

除在方法级别应用 EnableCors 属性外,还可以在类级别应用该属性,或将其全局应用于应用程序。 应用该属性的级别会在 Web API 代码中为该级别及下面级别的所有请求配置 CORS。 例如,如果在方法级别应用该策略,则该策略仅应用于该操作的请求,而如果在类级别应用该策略,则该策略将应用于对该控制器的所有请求。 最后,如果全局应用该策略,则该策略将应用于所有请求。

下面是在类级别应用该属性的另一个示例。 此示例中使用的设置十分宽松,因为它将通配符用于允许的域、请求标头和 HTTP 方法:

 

 
[EnableCors("*", "*", "*")]
public class ResourcesController : ApiController
{
  public HttpResponseMessage Put(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
  public HttpResponseMessage Post(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
}

如果在多个位置存在策略,则会使用“最接近的”属性,并忽略其他属性(优先顺序是方法、类、全局)。 如果您已在较高级别上应用策略,但随后想在较低级别上排除某一请求,则可以使用名为 DisableCorsAttribute 的另一个属性类。 此属性实质上是一个没有允许权限的策略。

如果在您不想允许 CORS 的控制器上有其他方法,则有两个选择。 首先,您可在 HTTP 方法列表中显式指定,如图 3 所示。 或者,您可以保留通配符,但使用 DisableCors 属性来排除 Delete 方法,如图 4 所示。

图 3 针对 HTTP 方法使用显式值

 

 
[EnableCors("*", "*", "PUT, POST")]
public class ResourcesController : ApiController
{
  public HttpResponseMessage Put(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
  public HttpResponseMessage Post(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
  // CORS not allowed because DELETE is not in the method list above
  public HttpResponseMessage Delete(int id)
  {
    return Request.CreateResponse(HttpStatusCode.NoContent);
  }
}

图 4 使用 DisableCors 属性

 

 
[EnableCors("*", "*", "*")]
public class ResourcesController : ApiController
{
  public HttpResponseMessage Put(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
  public HttpResponseMessage Post(Resource data)
  {
    return Request.CreateResponse(HttpStatusCode.OK, data);
  }
  // CORS not allowed because of the [DisableCors] attribute
  [DisableCors]
  public HttpResponseMessage Delete(int id)
  {
    return Request.CreateResponse(HttpStatusCode.NoContent);
  }
}

CorsMessageHandler。必须为 CORS 框架启用 CorsMessageHandler,才能执行其为评估 CORS 策略并发出 CORS 响应标头而拦截请求的工作。 消息处理程序的启用通常是在应用程序的 Web API 配置类中通过调用 EnableCors 扩展方法进行的:

 

 
public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    // Other configuration omitted
    config.EnableCors();
  }
}

如果您希望提供全局 CORS 策略,则可以将 EnableCorsAttribute 类的实例作为参数传递到 EnableCors 方法。 例如,下面的代码会在应用程序中全局配置一个宽松 CORS 策略:

 

 
public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    // Other configuration omitted
    config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
  }
}

与任何消息处理程序一样,CorsMessageHandler 可按路由交替注册,而不是进行全局注册。

因此,这是 ASP.NET Web API 2 中的基本“开箱即用”CORS 框架。 此框架的一个好处是,它可以针对更加动态的情况进行扩展,接下来我会对此进行介绍。

自定义策略

从前面的示例可明显看到,域列表(如果未使用通配符)是一个编译到 Web API 代码中的静态列表。 虽然这样做在开发过程中或在特定情况下可能行得通,但如果需要动态确定域列表或其他权限(比如,从数据库获取),则静态列表就不够用了。

幸运的是,Web API 中的 CORS 框架是可扩展的,可以十分方便地支持动态域列表。 实际上,该框架十分灵活,有两种常用的对策略生成进行自定义的方法。

自定义 CORS 策略属性。启用动态 CORS 策略的一种方法是开发一个可从某个数据源生成策略的自定义属性类。 可以使用此自定义属性类,而不使用由 Web API 提供的 EnableCorsAttribute 类。 这种方法十分简便,并且能够根据需要对特定类和方法(而不对其它类和方法)应用属性,从而保持控制的精细度。

为了实现这种方法,您只需生成一个与现有 EnableCorsAttribute 类相类似的自定义属性。 重点是 ICorsPolicyProvider 接口,此接口负责为任何给定请求创建 CorsPolicy 实例。 图 5 包含一个示例。

图 5 自定义 CORS 策略属性

 

 
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
                AllowMultiple = false)]
public class EnableCorsForPaidCustomersAttribute :
  Attribute, ICorsPolicyProvider
{
  public async Task<CorsPolicy> GetCorsPolicyAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
  {
    var corsRequestContext = request.GetCorsRequestContext();
    var originRequested = corsRequestContext.Origin;
    if (await IsOriginFromAPaidCustomer(originRequested))
    {
      // Grant CORS request
      var policy = new CorsPolicy
      {
        AllowAnyHeader = true,
        AllowAnyMethod = true,
      };
      policy.Origins.Add(originRequested);
      return policy;
    }
    else
    {
      // Reject CORS request
      return null;
    }
  }
  private async Task<bool> IsOriginFromAPaidCustomer(
    string originRequested)
  {
    // Do database look up here to determine if origin should be allowed
    return true;
  }
}

CorsPolicy 类具有用于表示要授予的 CORS 权限的全部属性。 此处使用的值只作为示例,但假定这些值可从数据库(或从任何其他源)查询来动态填充。

自定义策略提供程序工厂。生成动态 CORS 策略的第二种常用方法是创建自定义策略提供程序工厂。 此提供程序工厂就是包含用于当前请求的策略提供程序的一个 CORS 框架。 Web API 的默认实现方式使用自定义属性来发现该策略提供程序(如前所见,该属性类本身就是策略提供程序)。 这是另外一段可插入的 CORS 框架,并且,如果您想要针对策略(而不是自定义属性)使用一种方法,则可实现自己的策略提供程序工厂。

前述基于属性的方法提供了从某个请求到某个策略的隐式关联。 自定义策略提供程序工厂方法不同于属性方法,因为它需要该实现提供将传入请求与策略进行匹配的逻辑。 这是一种更为粗略的方法,因为它实质上是一种用于获取 CORS 策略的集中式方法。

图 6 显示了自定义策略提供程序工厂的可能示例。 此示例的重点是实现 ICorsPolicyProviderFactory 接口及其 GetCorsPolicyProvider 方法。

图 6 自定义策略提供程序工厂

 

 
public class DynamicPolicyProviderFactory : ICorsPolicyProviderFactory
{
  public ICorsPolicyProvider GetCorsPolicyProvider(
    HttpRequestMessage request)
  {
    var route = request.GetRouteData();
    var controller = (string)route.Values["controller"];
    var corsRequestContext = request.GetCorsRequestContext();
    var originRequested = corsRequestContext.Origin;
    var policy = GetPolicyForControllerAndOrigin(
      controller, originRequested);
    return new CustomPolicyProvider(policy);
  }
  private CorsPolicy GetPolicyForControllerAndOrigin(
   string controller, string originRequested)
  {
    // Do database lookup to determine if the controller is allowed for
    // the origin and create CorsPolicy if it is (otherwise return null)
    var policy = new CorsPolicy();
    policy.Origins.Add(originRequested);
    policy.Methods.Add("GET");
    return policy;
  }
}
public class CustomPolicyProvider : ICorsPolicyProvider
{
  CorsPolicy policy;
  public CustomPolicyProvider(CorsPolicy policy)
  {
    this.policy = policy;
  }
  public Task<CorsPolicy> GetCorsPolicyAsync(
    HttpRequestMessage request, CancellationToken cancellationToken)
  {
    return Task.FromResult(this.policy);
  }
}

此方法的主要不同之处在于,完全由该实现从传入请求确定策略。 图 6 中,控制器和域可用于查询数据库获取策略值。 此外,此方法虽然最为灵活,但它可能需要更多的工作才能从请求确定策略。

若要使用该自定义策略提供程序工厂,必须通过 Web API 配置中的 SetCorsPolicyProviderFactory 扩展方法将其注册到 Web API:

 

 
public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    // Other configuration omitted
    config.EnableCors();
    config.SetCorsPolicyProviderFactory(
      new DynamicPolicyProviderFactory());
  }
}

 

持续的社区贡献

ASP.NET Web API 是一个开源框架,同时是更大的开源框架集(统称为 ASP.NET Web 堆栈)的一部分,该堆栈还包括 MVC、网页等。

这些框架用于生成 ASP.NET 平台,并由 Microsoft 的 ASP.NET 团队进行管理。 作为开源平台的管理者,ASP.NET 团队欢迎社区贡献,而 Web API 中的跨域资源共享 (CORS) 实现就是一项贡献内容。

CORS 最初由 Brock Allen 开发,是 thinktecture IdentityModel 安全库 (thinktecture.github.io) 的一部分。

调试 CORS

如果您的跨域 AJAX 调用工作不正常,可以综合考虑几种方法来调试 CORS。

客户端。一种很简单的调试方法,就是使用自选 HTTP 调试器(如 Fiddler)并检查所有 HTTP 请求。 借助前面搜集的有关 CORS 规范详细内容的知识,您通常可以通过检查 CORS HTTP 标头(或者是否缺少标头)找出没有为特定 AJAX 请求授予权限的原因。

另一种方法是使用浏览器的 F12 开发者工具。 现代浏览器中的控制台窗口会在 AJAX 调用因 CORS 而失败时提供有用的错误消息。

服务器端。CORS 框架本身会使用 Web API 的跟踪功能来提供详细的跟踪消息。 只要向 Web API 注册了 ITraceWriter,CORS 框架就会发出消息,其中包含有关所选择的策略提供程序、使用的策略以及发送的 CORS HTTP 标头的信息。 有关 Web API 跟踪的详细信息,请参阅 MSDN 上的 Web API 文档。

需求极高的功能

一段时间以来,CORS 一直是一个需求极高的功能,并最终内置到 Web API 中。 本文重点介绍了 CORS 自身的详细内容,这种知识在实现和调试 CORS 过程中十分重要。 掌握了这种知识,您就能够方便地利用 Web API 中的 CORS 支持,在应用程序中实现跨域调用。

 

Brock Allen 是一名顾问,专门从事 Microsoft .NET Framework、Web 开发和基于 Web 的安全性工作。他还是培训公司 DevelopMentor 的一名讲师、thinktecture GmbH & Co. KG 的助理顾问、thinktecture 开源项目的撰稿人以及 ASP.NET 平台的撰稿人。您可在他的网站 brockallen.com 与他取得联系,或向他发送电子邮件,邮件地址为 brockallen@gmail.com

衷心感谢以下技术专家对本文的审阅:Yao Huan Lin (Microsoft) 
Yao Huang Lin (yaohuang@microsoft.com) 是 Microsoft 的 ASP.NET Web API 团队中的软件开发者。 他参与了 .NET Framework 的许多组件的工作,包括 ASP.NET、Windows Communication Foundation (WCF) 和 Windows Workflow Foundation (WF)。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
27732 0
API网关支持专享实例
信息摘要: API网关支持专享实例自助购买(北京、青岛、杭州、上海、深圳、新加坡、法兰克福)适用客户: 开发者,企业,对API网关服务品质有更高要求的客户。版本/规格功能: 专享实例是API网关新推出的提供更高RPS与SLA的规格,用户可以通过支付规格配置费用自助购买更高的RPS,获取独享的资源,包含公网入口IP地址、仅允许自己VPC访问的的内网IP地址、公网出口带宽、独立隔离的服务器集群等,提供更高等级的SLA保障。
704 0
ASP.NET Core 配置 EF SQLite 支持 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 配置 EF SQLite 支持 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 配置 EF SQLite 支持 上一章节我有提到 macOS 版的 Visual Studio Community 没有携带 Lo...
1350 0
Api网关对调试Api的支持
当Consumer在编写代码去调用Api之前,一定要知道Api的入参和返回结果的定义是什么样的,了解的方式基本也就是参考Provider所提供的Api文档。 但是文档上的内容都是静态的,只是通过参数定义说明和返回示例的
2588 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
19981 0
+关注
杰克.陈
一个安静的程序猿~
10425
文章
2
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载