在 ASP.NET Web API 2 中啟用跨原始來源要求Enable cross-origin requests in ASP.NET Web API 2

Mike Wassonby Mike Wasson

瀏覽器安全性可防止網頁對另一個網域提出 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.

什麼是「相同的來源」?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.

建立 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. 使用下列程式碼,新增名為 TestController 的 Web API 控制器: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 App Service Web Apps)。若要確認 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".

    顯示測試訊息的網頁瀏覽器

建立 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變數,請使用 WebService 應用程式的 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 方法(GET、POST 或 PUT)將 AJAX 要求提交至 WebService 應用程式。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.

開啟檔案應用程式_Start/WebApiConfig. csOpen the file App_Start/WebApiConfig.cs. 將下列程式碼新增至WebApiConfig方法: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.

顯示成功測試訊息的網頁瀏覽器

CORS 的運作方式How CORS Works

本節說明在 HTTP 訊息層級的 CORS 要求中會發生什麼事。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

  • 應用程式不會設定 [接受]、[接受語言]、[內容語言]、[內容類型] 或 [上一個事件識別碼] 以外的任何要求標頭,以及The application does not set any request headers other than Accept, Accept-Language, Content-Language, Content-Type, or Last-Event-ID, and

  • 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物件上的setRequestHeaderThe 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

回應會包含一個存取控制-Allow-方法標頭,其中會列出允許的方法,並可選擇性地列出允許的標頭的「存取控制-允許-標頭」標頭。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 應用程式接收和處理選項要求,請將下列設定新增至應用程式在 <system.webServer><handlers> 區段的 web.config 檔案中: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. 下列範例只會針對 GetItem 方法啟用 CORS。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. 下列範例會針對每個方法(PutItem除外)啟用 CORS。The 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 標頭(所謂的「作者要求標頭」)。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-LanguageContent-Language
  • Content-TypeContent-Type
  • 到期 (Expires)Expires
  • 上次修改時間Last-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-Custom-Header ' 的自訂標頭。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-Custom-header '。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 withCredentials設定為 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. 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. 您可以藉由建立衍生自屬性的類別並執行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. 幸好,所有主要瀏覽器的目前版本都包含對 CORS 的支援Fortunately, the current versions of all major browsers include support for CORS.