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

作者: Mike WassonBy Mike Wasson

此内容适用于早期版本的 .NET。This content is for a previous version of .NET. 新开发应使用 ASP.NET CoreNew development should use ASP.NET Core. 有关在 ASP.NET Core 中 (CORS) 使用 Web API 和跨源请求的详细信息,请参阅:For more information on using Web API and Cross-Origin Requests (CORS) in ASP.NET Core, see:

浏览器安全性将阻止网页向另一个域发出 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 项目-一个名为 "WebService",它托管一个 Web API 控制器,另一个称为 "WebClient",后者调用 WebService。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 到 WebService 的 AJAX 请求是一个跨源请求。Because the two applications are hosted at different domains, an AJAX request from WebClient to WebService is a cross-origin request.

显示 web 服务和 web 客户端

什么是 "相同来源"?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 的来源不同于前两个 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.

创建 WebService 项目Create the WebService project

Note

本部分假设你已了解如何创建 Web API 项目。This section assumes you already know how to create Web API projects. 否则,请参阅 ASP.NET Web API 入门If 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 控制器 TestControllerAdd 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 App Service Web 应用。 ) 若要验证 web API 是否正常工作,请导航到 http://hostname/api/test/ ,其中 hostname 是部署应用程序的域。(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. 解决方案资源管理器 中,打开文件 视图/Home/索引。In 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.

单击 "试用" 按钮时,将使用下拉框中列出的 HTTP 方法将 AJAX 请求提交到 WebService 应用, (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. 目前,WebService 应用不支持 CORS,因此,如果单击该按钮,则会出现错误。Currently, the WebService app does not support CORS, so if you click the button you'll get an error.

浏览器中的 "尝试" 错误

Note

如果在类似于 Fiddler的工具中观看 HTTP 流量,你会发现浏览器确实发送了 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

现在,让我们在 WebService 应用中启用 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.

打开文件 应用 _ 启动/webapiconfig.csOpen the file App_Start/WebApiConfig.cs. 将以下代码添加到 webapiconfig.cs 方法: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.

重新部署更新的 WebService 应用程序。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. 此标头的值可以与源标头匹配,也可以是通配符值 " * ",这意味着允许任何源。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

  • 如果 set) 为以下类型之一,则 Content-type 标头 (:The Content-Type header (if set) is one of the following:

    • application/x-www-form-urlencodedapplication/x-www-form-urlencoded
    • 多部分/窗体数据multipart/form-data
    • text/plaintext/plain

有关请求标头的规则适用于应用程序通过对 XMLHttpRequest 对象调用 setRequestHeader 而设置的标头。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.

通常用于测试具有预检选项 (的终结点的工具例如, 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 和 DEBUG 请求与无扩展名 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 GetItemThe 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. 全球Global

设置允许的来源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 allow 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 不包含标准标头(如 "Accept"),即使应用程序在脚本中设置这些标头也是如此。FireFox does not include standard headers such as "Accept", even when the application sets them in script.

如果将 标头 设置为 "" 之外 * 的任何内容,则应该至少包含 "accept"、"content-type" 和 "源",以及要支持的任何自定义标头。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-LanguageContent-Language
  • Content-TypeContent-Type
  • ExpiresExpires
  • Last-ModifiedLast-Modified
  • Pragma

CORS 规范调用这些 简单的响应标头The CORS spec calls these simple response headers. 若要使其他标头可用于应用程序,请设置 [EnableCors]exposedHeaders 参数。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. 若要使标头可用,请在 exposedHeaders 中包含 "X-自定义标头"。To 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 设置为 true。To send credentials with a cross-origin request, the client must set XMLHttpRequest.withCredentials to true.

直接使用 XMLHttpRequestUsing 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 中允许跨域凭据,请在 [EnableCors] 特性上将 SupportsCredentials 属性设置为 true: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. " * " 如果 SupportsCredentials 为 true,则 CORS 规范还指出设置的来源无效。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. 您可以通过创建从 Attribute 派生的类并实现 ICorsPolicyProvider 来提供自己的实现。You 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.

作为使用特性的替代方法,可以注册创建 ICorsPolicyProvider 对象的 ICorsPolicyProviderFactory 对象。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. 幸运的是,所有主流浏览器的当前版本都 支持 CORSFortunately, the current versions of all major browsers include support for CORS.