启用 ASP.NET Web API 2 中的跨域请求Enable cross-origin requests in ASP.NET Web API 2

通过Mike Wassonby Mike Wasson

浏览器安全策略阻止 Web 页向另一个域发出 AJAX 请求。Browser security prevents a web page from making AJAX requests to another domain. 此限制称为“同源策略”,可防止恶意站点读取另一个站点中的敏感数据。This restriction is called the same-origin policy, and prevents a malicious site from reading sensitive data from another site. 但是,有时你可能想要让调用 web API 的其他站点。However, sometimes you might want to let other sites call your web API.

跨域资源共享(CORS) 是一种 W3C 标准,允许服务器放宽同源策略。Cross Origin Resource Sharing (CORS) is a W3C standard that allows a server to relax the same-origin policy. 使用 CORS,服务器可以在显式允许某些跨域请求时拒绝其他跨域请求。Using CORS, a server can explicitly allow some cross-origin requests while rejecting others. CORS 比早期技术(例如 JSONP)更安全、更灵活。CORS is safer and more flexible than earlier techniques such as JSONP. 本教程演示如何在 Web API 应用程序中启用 CORS。This tutorial shows how to enable CORS in your Web API application.

在本教程中使用的软件Software used in the tutorial

介绍Introduction

本教程演示了在 ASP.NET Web API 中的 CORS 支持。This tutorial demonstrates CORS support in ASP.NET Web API. 我们将首先创建两个 ASP.NET 项目-一个名为"web 服务",它承载 Web API 控制器,以及其他名为"WebClient",后者调用 web 服务。We'll start by creating two ASP.NET projects – one called "WebService", which hosts a Web API controller, and the other called "WebClient", which calls WebService. 由于两个应用程序承载在不同的域中,从 WebClient 对 web 服务的 AJAX 请求是跨域请求。Because the two applications are hosted at different domains, an AJAX request from WebClient to WebService is a cross-origin request.

什么是"相同的原点"?What is "same origin"?

如果它们具有相同方案、 主机和端口,两个 Url 将具有相同的原点。Two URLs have the same origin if they have identical schemes, hosts, and ports. (RFC 6454)(RFC 6454)

以下两个 Url 具有相同的原点:These two URLs have the same origin:

  • http://example.com/foo.html
  • http://example.com/bar.html

这些 Url 有两个相对上一不同的源:These URLs have different origins than the previous two:

  • http://example.net -不同的域http://example.net - Different domain
  • http://example.com:9000/foo.html -不同的端口http://example.com:9000/foo.html - Different port
  • https://example.com/foo.html -不同的方案https://example.com/foo.html - Different scheme
  • http://www.example.com/foo.html -不同的子域http://www.example.com/foo.html - Different subdomain

Note

在比较源时,Internet Explorer 不考虑端口。Internet Explorer does not consider the port when comparing origins.

创建 web 服务项目Create the WebService project

Note

本部分假定您已经知道如何创建 Web API 项目。This section assumes you already know how to create Web API projects. 如果没有,请参阅Getting Started with ASP.NET Web APIIf not, see Getting Started with ASP.NET Web API.

  1. 启动 Visual Studio 并创建一个新ASP.NET Web 应用程序 (.NET Framework) 项目。Start Visual Studio and create a new ASP.NET Web Application (.NET Framework) project.

  2. 在中新的 ASP.NET Web 应用程序对话框中,选择项目模板。In the New ASP.NET Web Application dialog box, select the Empty project template. 添加文件夹和核心引用,选择Web API复选框。Under Add folders and core references for, select the Web API checkbox.

    Visual Studio 中的新 ASP.NET 项目对话框

  3. 添加名为的 Web API 控制器TestController使用以下代码:Add a Web API controller named TestController with the following code:

    using System.Net.Http;
    using System.Web.Http;
    
    namespace WebService.Controllers
    {
        public class TestController : ApiController
        {
            public HttpResponseMessage Get()
            {
                return new HttpResponseMessage()
                {
                    Content = new StringContent("GET: Test message")
                };
            }
    
            public HttpResponseMessage Post()
            {
                return new HttpResponseMessage()
                {
                    Content = new StringContent("POST: Test message")
                };
            }
    
            public HttpResponseMessage Put()
            {
                return new HttpResponseMessage()
                {
                    Content = new StringContent("PUT: Test message")
                };
            }
        }
    }
    
  4. 可以在本地运行应用程序或部署到 Azure。You can run the application locally or deploy to Azure. (对于本教程中的屏幕截图,该应用将部署到 Azure 应用服务 Web 应用。)若要验证的 web API 是否正常工作,请导航到http://hostname/api/test/,其中主机名是在其中部署应用程序的域。(For the screenshots in this tutorial, the app deploys to Azure App Service Web Apps.) To verify that the web API is working, navigate to http://hostname/api/test/, where hostname is the domain where you deployed the application. 应看到响应文本:"获取:测试消息"。You should see the response text, "GET: Test Message".

    Web 浏览器显示测试消息

创建 WebClient 项目Create the WebClient project

  1. 创建另一个ASP.NET Web 应用程序 (.NET Framework) 项目,然后选择MVC项目模板。Create another ASP.NET Web Application (.NET Framework) project and select the MVC project template. (可选) 选择更改身份验证 > 无身份验证Optionally, select Change Authentication > No Authentication. 对于本教程中,不需要身份验证。You don't need authentication for this tutorial.

    在 Visual Studio 中新建 ASP.NET 项目对话框中的 MVC 模板

  2. 在中解决方案资源管理器,打开文件Views/Home/Index.cshtmlIn Solution Explorer, open the file Views/Home/Index.cshtml. 此文件中的代码替换为以下:Replace the code in this file with the following:

    <div>
        <select id="method">
            <option value="get">GET</option>
            <option value="post">POST</option>
            <option value="put">PUT</option>
        </select>
        <input type="button" value="Try it" onclick="sendRequest()" />
        <span id='value1'>(Result)</span>
    </div>
    
    @section scripts {
    <script>
        // TODO: Replace with the URL of your WebService app
        var serviceUrl = 'http://mywebservice/api/test'; 
    
        function sendRequest() {
            var method = $('#method').val();
    
            $.ajax({
                type: method,
                url: serviceUrl
            }).done(function (data) {
                $('#value1').text(data);
            }).fail(function (jqXHR, textStatus, errorThrown) {
                $('#value1').text(jqXHR.responseText || textStatus);
            });
        }
    </script>
    }
    

    有关serviceUrl变量,请使用 web 服务应用程序的 URI。For the serviceUrl variable, use the URI of the WebService app.

  3. 在本地运行 WebClient 应用或将其发布到另一个网站。Run the WebClient app locally or publish it to another website.

当您单击"试用"按钮时,AJAX 请求提交到 web 服务应用程序使用中列出的 HTTP 方法 (GET、 POST 或 PUT) 的下拉列表框。When you click the "Try It" button, an AJAX request is submitted to the WebService app using the HTTP method listed in the dropdown box (GET, POST, or PUT). 这样可以检查不同跨域请求。This lets you examine different cross-origin requests. 目前,web 服务应用程序不支持 CORS,因此如果单击该按钮将收到一个错误。Currently, the WebService app does not support CORS, so if you click the button you'll get an error.

在浏览器中的 try 错误

Note

如果监视工具中的 HTTP 流量喜欢Fiddler,可以看到,在浏览器 does 发送 GET 请求,并请求成功,但 AJAX 调用将返回错误。If you watch the HTTP traffic in a tool like Fiddler, you'll see that the browser does send the GET request, and the request succeeds, but the AJAX call returns an error. 务必了解同域策略不会阻止浏览器中的从发送请求。It's important to understand that same-origin policy does not prevent the browser from sending the request. 相反,它会阻止应用程序不能看到响应Instead, it prevents the application from seeing the response.

显示 web 请求的 fiddler web 调试器

启用 CORSEnable CORS

现在让我们在 web 服务应用程序中启用 CORS。Now let's enable CORS in the WebService app. 首先,添加 CORS NuGet 包。First, add the CORS NuGet package. 在 Visual Studio 中,从工具菜单中,选择NuGet 包管理器,然后选择程序包管理器控制台In Visual Studio, from the Tools menu, select NuGet Package Manager, then select Package Manager Console. 在包管理器控制台窗口中,键入以下命令:In the Package Manager Console window, type the following command:

Install-Package Microsoft.AspNet.WebApi.Cors

此命令安装最新的包和更新所有依赖项,其中包括核心 Web API 库。This command installs the latest package and updates all dependencies, including the core Web API libraries. 使用-Version标志以面向特定版本。Use the -Version flag to target a specific version. CORS 包,需要 Web API 2.0 或更高版本。The CORS package requires Web API 2.0 or later.

打开文件应用程序_Start/WebApiConfig.csOpen the file App_Start/WebApiConfig.cs. 将以下代码添加到WebApiConfig.Register方法:Add the following code to the WebApiConfig.Register method:

using System.Web.Http;
namespace WebService
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // New code
            config.EnableCors();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

接下来,添加 [EnableCors] 属性为TestController类:Next, add the [EnableCors] attribute to the TestController class:

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

namespace WebService.Controllers
{
    [EnableCors(origins: "http://mywebclient.azurewebsites.net", headers: "*", methods: "*")]
    public class TestController : ApiController
    {
        // Controller methods not shown...
    }
}

有关来源参数,请使用 WebClient 应用程序的部署位置的 URI。For the origins parameter, use the URI where you deployed the WebClient application. 这允许跨域请求从 WebClient,同时仍不允许其他所有跨域请求。This allows cross-origin requests from WebClient, while still disallowing all other cross-domain requests. 更高版本,我将介绍的参数 [EnableCors] 中更多详细信息。Later, I'll describe the parameters for [EnableCors] in more detail.

不包括末尾的正斜杠来源URL。Do not include a forward slash at the end of the origins URL.

重新部署更新的 web 服务应用程序。Redeploy the updated WebService application. 不需要更新 WebClient。You don't need to update WebClient. 现在应该成功从 WebClient AJAX 请求。Now the AJAX request from WebClient should succeed. 所有允许使用 GET、 PUT 和 POST 方法。The GET, PUT, and POST methods are all allowed.

Web 浏览器显示成功的测试消息

CORS 的工作原理How CORS Works

本部分介绍在 CORS 请求中,在 HTTP 消息的级别会发生什么情况。This section describes what happens in a CORS request, at the level of the HTTP messages. 务必要了解 CORS 的工作原理,以便你可以配置 [EnableCors] 正确属性,正如您期望的功能不正常工作时进行故障排除。It's important to understand how CORS works, so that you can configure the [EnableCors] attribute correctly and troubleshoot if things don't work as you expect.

CORS 规范引入了几个新的 HTTP 标头启用跨域请求。The CORS specification introduces several new HTTP headers that enable cross-origin requests. 如果浏览器支持 CORS,则将设置自动跨域请求; 这些标头您不必执行任何特殊的 JavaScript 代码中。If a browser supports CORS, it sets these headers automatically for cross-origin requests; you don't need to do anything special in your JavaScript code.

下面是跨域请求的示例。Here is an example of a cross-origin request. "源"标头提供发出请求的站点的域。The "Origin" header gives the domain of the site that is making the request.

GET http://myservice.azurewebsites.net/api/test HTTP/1.1
Referer: http://myclient.azurewebsites.net/
Accept: */*
Accept-Language: en-US
Origin: http://myclient.azurewebsites.net
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: myservice.azurewebsites.net

如果服务器允许该请求,则将设置访问控制的允许的域标头。If the server allows the request, it sets the Access-Control-Allow-Origin header. 此标头的值匹配 Origin 标头,或为通配符值"*",允许任何源的含义。The value of this header either matches the Origin header, or is the wildcard value "*", meaning that any origin is allowed.

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/plain; charset=utf-8
Access-Control-Allow-Origin: http://myclient.azurewebsites.net
Date: Wed, 05 Jun 2013 06:27:30 GMT
Content-Length: 17

GET: Test message

如果响应不包括访问控制的允许的域标头,AJAX 请求失败。If the response does not include the Access-Control-Allow-Origin header, the AJAX request fails. 具体而言,在浏览器不允许该请求。Specifically, the browser disallows the request. 即使服务器返回成功的响应,请在浏览器不会将响应提供给客户端应用程序。Even if the server returns a successful response, the browser does not make the response available to the client application.

预检请求Preflight Requests

对于某些 CORS 请求,浏览器发送额外的请求,名为"预检请求,",再发送实际请求的资源。For some CORS requests, the browser sends an additional request, called a "preflight request", before it sends the actual request for the resource.

在浏览器可以跳过预检请求,如果满足以下条件:The browser can skip the preflight request if the following conditions are true:

  • 请求方法是 GET、 HEAD 或 POST,The request method is GET, HEAD, or POST, and

  • 应用程序不会设置之外接受,接受语言的内容语言,任何请求标头内容类型或最后一个事件 ID、The application does not set any request headers other than Accept, Accept-Language, Content-Language, Content-Type, or Last-Event-ID, and

  • 内容类型标头 (如果设置) 是以下之一:The Content-Type header (if set) is one of the following:

    • application/x-www-form-urlencodedapplication/x-www-form-urlencoded
    • multipart/form-datamultipart/form-data
    • 文本/无格式text/plain

有关请求标头规则适用于通过调用来设置应用程序的标头setRequestHeaderXMLHttpRequest对象。The rule about request headers applies to headers that the application sets by calling setRequestHeader on the XMLHttpRequest object. (CORS 规范调用这些"作者请求标头"。)此规则不适用于标头浏览器可以设置,例如用户代理、 主机或内容长度。(The CORS specification calls these "author request headers".) The rule does not apply to headers the browser can set, such as User-Agent, Host, or Content-Length.

下面是预检请求的示例:Here is an example of a preflight request:

OPTIONS http://myservice.azurewebsites.net/api/test HTTP/1.1
Accept: */*
Origin: http://myclient.azurewebsites.net
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: accept, x-my-custom-header
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: myservice.azurewebsites.net
Content-Length: 0

预检请求使用 HTTP OPTIONS 方法。The pre-flight request uses the HTTP OPTIONS method. 它包括两个特殊的标头:It includes two special headers:

  • 访问控制的请求的方法:将用于实际请求的 HTTP 方法。Access-Control-Request-Method: The HTTP method that will be used for the actual request.
  • 访问的控件的请求的标头:请求标头的列表,应用程序实际请求上设置。Access-Control-Request-Headers: A list of request headers that the application set on the actual request. (同样,这不包括在浏览器设置的标头。)(Again, this does not include headers that the browser sets.)

下面是示例响应中,假定服务器允许请求:Here is an example response, assuming that the server allows the request:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 0
Access-Control-Allow-Origin: http://myclient.azurewebsites.net
Access-Control-Allow-Headers: x-my-custom-header
Access-Control-Allow-Methods: PUT
Date: Wed, 05 Jun 2013 06:33:22 GMT

响应包括访问的控制的允许的方法标头,其中列出了允许的方法,和 (可选) 访问的控制的允许的标头标头,其中列出了允许的标头。The response includes an Access-Control-Allow-Methods header that lists the allowed methods, and optionally an Access-Control-Allow-Headers header, which lists the allowed headers. 如果预检请求成功,则浏览器发送实际请求,如前面所述。If the preflight request succeeds, the browser sends the actual request, as described earlier.

常用于测试具有预检 OPTIONS 请求的终结点的工具 (例如, FiddlerPostman) 默认情况下不发送所需的选项标头。Tools commonly used to test endpoints with preflight OPTIONS requests (for example, Fiddler and Postman) don't send the required OPTIONS headers by default. 确认Access-Control-Request-MethodAccess-Control-Request-Headers与请求一起发送标头和选项标头打开通过 IIS 应用程序。Confirm that the Access-Control-Request-Method and Access-Control-Request-Headers headers are sent with the request and that OPTIONS headers reach the app through IIS.

若要配置 IIS 以允许接收和处理选项请求的 ASP.NET 应用程序,请将以下配置添加到应用程序的web.config文件中<system.webServer><handlers>部分:To configure IIS to allow an ASP.NET app to receive and handle OPTION requests, add the following configuration to the app's web.config file in the <system.webServer><handlers> section:

<system.webServer>
  <handlers>
    <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
    <remove name="OPTIONSVerbHandler" />
    <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
  </handlers>
</system.webServer>

删除OPTIONSVerbHandler可以防止 IIS 处理选项的请求。The removal of OPTIONSVerbHandler prevents IIS from handling OPTIONS requests. 替换为ExtensionlessUrlHandler-Integrated-4.0允许选项的请求来访问应用程序,因为默认模块注册仅允许 GET、 HEAD、 POST 和调试无扩展名 Url 请求。The replacement of ExtensionlessUrlHandler-Integrated-4.0 allows OPTIONS requests to reach the app because the default module registration only allows GET, HEAD, POST, and DEBUG requests with extensionless URLs.

[EnableCors] 的范围规则Scope Rules for [EnableCors]

可以在应用程序中每个操作,每个控制器,或全局范围内的所有 Web API 控制器启用 CORS。You can enable CORS per action, per controller, or globally for all Web API controllers in your application.

每个操作Per Action

若要为单个操作中启用 CORS,请设置 [EnableCors] 操作方法上的属性。To enable CORS for a single action, set the [EnableCors] attribute on the action method. 下面的示例启用 CORS 的GetItem仅限方法。The following example enables CORS for the GetItem method only.

public class ItemsController : ApiController
{
    public HttpResponseMessage GetAll() { ... }

    [EnableCors(origins: "http://www.example.com", headers: "*", methods: "*")]
    public HttpResponseMessage GetItem(int id) { ... }

    public HttpResponseMessage Post() { ... }
    public HttpResponseMessage PutItem(int id) { ... }
}

每个控制器Per Controller

如果您设置 [EnableCors] 控制器类,它适用于在控制器上的所有操作。If you set [EnableCors] on the controller class, it applies to all the actions on the controller. 若要操作禁用 CORS,请添加 [DisableCors] 属性为该操作。To disable CORS for an action, add the [DisableCors] attribute to the action. 下面的示例为每个方法,但启用 CORS PutItemThe following example enables CORS for every method except PutItem.

[EnableCors(origins: "http://www.example.com", headers: "*", methods: "*")]
public class ItemsController : ApiController
{
    public HttpResponseMessage GetAll() { ... }
    public HttpResponseMessage GetItem(int id) { ... }
    public HttpResponseMessage Post() { ... }

    [DisableCors]
    public HttpResponseMessage PutItem(int id) { ... }
}

全局Globally

若要为应用程序中的所有 Web API 控制器中启用 CORS,请将传递EnableCorsAttribute实例向EnableCors方法:To enable CORS for all Web API controllers in your application, pass an EnableCorsAttribute instance to the EnableCors method:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var cors = new EnableCorsAttribute("www.example.com", "*", "*");
        config.EnableCors(cors);
        // ...
    }
}

如果在多个作用域设置属性,优先顺序是:If you set the attribute at more than one scope, the order of precedence is:

  1. 操作Action
  2. 控制器Controller
  3. GlobalGlobal

设置允许的来源Set the allowed origins

来源的参数 [EnableCors] 属性指定允许哪些域访问资源。The origins parameter of the [EnableCors] attribute specifies which origins are allowed to access the resource. 值为逗号分隔的允许的来源列表。The value is a comma-separated list of the allowed origins.

[EnableCors(origins: "http://www.contoso.com,http://www.example.com", 
    headers: "*", methods: "*")]

此外可以使用通配符值"*"若要允许来自任何来源的请求。You can also use the wildcard value "*" to allow requests from any origins.

允许任何来源的请求之前,请仔细考虑。Consider carefully before allowing requests from any origin. 这意味着几乎任何网站可以向你的 web API 的 AJAX 调用。It means that literally any website can make AJAX calls to your web API.

// Allow CORS for all origins. (Caution!)
[EnableCors(origins: "*", headers: "*", methods: "*")]

设置允许的 HTTP 方法Set the allowed HTTP methods

方法的参数 [EnableCors] 属性指定的 HTTP 方法允许访问资源。The methods parameter of the [EnableCors] attribute specifies which HTTP methods are allowed to access the resource. 若要允许所有方法,使用通配符值"*"。To allow all methods, use the wildcard value "*". 以下示例允许只有 GET 和 POST 的请求。The following example allows only GET and POST requests.

[EnableCors(origins: "http://www.example.com", headers: "*", methods: "get,post")]
public class TestController : ApiController
{
    public HttpResponseMessage Get() { ... }
    public HttpResponseMessage Post() { ... }
    public HttpResponseMessage Put() { ... }    
}

设置允许的请求标头Set the allowed request headers

本文介绍了如何前面预检请求可能包括一个访问控制的请求标头标头,列出应用程序设置的 HTTP 标头 (所谓"author 请求标头")。This article described earlier how a preflight request might include an Access-Control-Request-Headers header, listing the HTTP headers set by the application (the so-called "author request headers"). 标头的参数 [EnableCors] 属性指定允许哪些作者请求标头。The headers parameter of the [EnableCors] attribute specifies which author request headers are allowed. 若要允许任何标头,设置标头到"*"。To allow any headers, set headers to "*". 到允许列表特定的标头,设置标头以逗号分隔列表的允许的标头:To whitelist specific headers, set headers to a comma-separated list of the allowed headers:

[EnableCors(origins: "http://example.com", 
    headers: "accept,content-type,origin,x-my-header", methods: "*")]

但是,浏览器不是它们如何设置访问控制的请求标头中完全一致的。However, browsers are not entirely consistent in how they set Access-Control-Request-Headers. 例如,Chrome 当前包括"源"。For example, Chrome currently includes "origin". FireFox 不包括标准标头,如"接受",即使应用程序将它们设置在脚本中。FireFox does not include standard headers such as "Accept", even when the application sets them in script.

如果您设置标头以外的其他任何内容"*",您至少应包含"接受","内容类型",,和"源",以及你想要支持任何自定义标头。If you set headers to anything other than "*", you should include at least "accept", "content-type", and "origin", plus any custom headers that you want to support.

设置允许的响应标头Set the allowed response headers

默认情况下,在浏览器不公开所有向应用程序的响应标头。By default, the browser does not expose all of the response headers to the application. 默认情况下可用的响应标头是:The response headers that are available by default are:

  • Cache-ControlCache-Control
  • 内容语言Content-Language
  • Content-TypeContent-Type
  • 过期Expires
  • Last-ModifiedLast-Modified
  • 杂注Pragma

CORS 规范调用这些简单响应标头The CORS spec calls these simple response headers. 若要使其他标头可用于应用程序,设置exposedHeaders的参数 [EnableCors]To make other headers available to the application, set the exposedHeaders parameter of [EnableCors].

在以下示例中,控制器的Get方法设置名为 X-自定义的标头的自定义标头。In the following example, the controller's Get method sets a custom header named ‘X-Custom-Header'. 默认情况下,在浏览器将公开此标头中的跨域请求。By default, the browser will not expose this header in a cross-origin request. 若要使标头,包括 X-自定义的标头中exposedHeadersTo make the header available, include ‘X-Custom-Header' in exposedHeaders.

[EnableCors(origins: "*", headers: "*", methods: "*", exposedHeaders: "X-Custom-Header")]
public class TestController : ApiController
{
    public HttpResponseMessage Get()
    {
        var resp = new HttpResponseMessage()
        {
            Content = new StringContent("GET: Test message")
        };
        resp.Headers.Add("X-Custom-Header", "hello");
        return resp;
    }
}

在跨域请求中传递凭据Pass credentials in cross-origin requests

凭据要求特殊处理 CORS 请求中。Credentials require special handling in a CORS request. 默认情况下,在浏览器不发送任何凭据与跨域请求。By default, the browser does not send any credentials with a cross-origin request. 凭据包含 cookie,以及 HTTP 身份验证方案。Credentials include cookies as well as HTTP authentication schemes. 若要将发送具有跨源请求的凭据,客户端必须设置XMLHttpRequest.withCredentials为 true。To send credentials with a cross-origin request, the client must set XMLHttpRequest.withCredentials to true.

使用XMLHttpRequest直接:Using XMLHttpRequest directly:

var xhr = new XMLHttpRequest();
xhr.open('get', 'http://www.example.com/api/test');
xhr.withCredentials = true;

在 jQuery 中:In jQuery:

$.ajax({
    type: 'get',
    url: 'http://www.example.com/api/test',
    xhrFields: {
        withCredentials: true
    }

此外,服务器必须允许凭据。In addition, the server must allow the credentials. 若要允许 Web API 中的跨域凭据,请设置SupportsCredentials属性设置为 true [EnableCors] 属性:To allow cross-origin credentials in Web API, set the SupportsCredentials property to true on the [EnableCors] attribute:

[EnableCors(origins: "http://myclient.azurewebsites.net", headers: "*", 
    methods: "*", SupportsCredentials = true)]

如果此属性为 true,HTTP 响应将包括一个访问控制的允许的凭据标头。If this property is true, the HTTP response will include an Access-Control-Allow-Credentials header. 此标头告知浏览器的服务器允许跨域请求凭据。This header tells the browser that the server allows credentials for a cross-origin request.

如果浏览器发送的凭据,但响应不包含有效的访问控制的允许的凭据标头,在浏览器并不会对该应用程序的响应和 AJAX 请求失败。If the browser sends credentials, but the response does not include a valid Access-Control-Allow-Credentials header, the browser will not expose the response to the application, and the AJAX request fails.

请谨慎设置SupportsCredentials为 true,因为这意味着在另一个域的网站可以不在用户不知情的情况下对用户的名义,Web API 发送登录的用户的凭据。Be careful about setting SupportsCredentials to true, because it means a website at another domain can send a logged-in user's credentials to your Web API on the user's behalf, without the user being aware. CORS 规范还会说明该设置来源到" * "无效如果SupportsCredentials为 true。The CORS spec also states that setting origins to "*" is invalid if SupportsCredentials is true.

自定义 CORS 策略提供程序Custom CORS policy providers

[EnableCors] 特性实现ICorsPolicyProvider接口。The [EnableCors] attribute implements the ICorsPolicyProvider interface. 您可以通过创建派生的类提供您自己的实现特性并实现ICorsPolicyProviderYou can provide your own implementation by creating a class that derives from Attribute and implements ICorsPolicyProvider.

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
public class MyCorsPolicyAttribute : Attribute, ICorsPolicyProvider 
{
    private CorsPolicy _policy;

    public MyCorsPolicyAttribute()
    {
        // Create a CORS policy.
        _policy = new CorsPolicy
        {
            AllowAnyMethod = true,
            AllowAnyHeader = true
        };

        // Add allowed origins.
        _policy.Origins.Add("http://myclient.azurewebsites.net");
        _policy.Origins.Add("http://www.contoso.com");
    }

    public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request)
    {
        return Task.FromResult(_policy);
    }
}

现在可以应用任何位置,可使该属性 [EnableCors]Now you can apply the attribute any place that you would put [EnableCors].

[MyCorsPolicy]
public class TestController : ApiController
{
    .. //

例如,自定义 CORS 策略提供程序无法从配置文件读取设置。For example, a custom CORS policy provider could read the settings from a configuration file.

作为使用属性的替代方法,可以注册ICorsPolicyProviderFactory创建的对象ICorsPolicyProvider对象。As an alternative to using attributes, you can register an ICorsPolicyProviderFactory object that creates ICorsPolicyProvider objects.

public class CorsPolicyFactory : ICorsPolicyProviderFactory
{
    ICorsPolicyProvider _provider = new MyCorsPolicyProvider();

    public ICorsPolicyProvider GetCorsPolicyProvider(HttpRequestMessage request)
    {
        return _provider;
    }
}

若要设置ICorsPolicyProviderFactory,调用SetCorsPolicyProviderFactory扩展方法在启动时,按如下所示:To set the ICorsPolicyProviderFactory, call the SetCorsPolicyProviderFactory extension method at startup, as follows:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.SetCorsPolicyProviderFactory(new CorsPolicyFactory());
        config.EnableCors();

        // ...
    }
}

浏览器支持Browser support

Web API CORS 包是一种服务器端技术。The Web API CORS package is a server-side technology. 用户的浏览器还需要支持 CORS。The user's browser also needs to support CORS. 幸运的是,所有主要浏览器的当前版本包括CORS 的支持Fortunately, the current versions of all major browsers include support for CORS.