ASP.NET Web API 2 でのクロスオリジン要求を有効にする

Mike Wasson

このコンテンツは、以前のバージョンの .NET 用です。 新しい開発では、 ASP.NET Coreを使用する必要があります。 ASP.NET Core で Web API とクロスオリジン要求 (CORS) を使用する方法の詳細については、以下を参照してください。

ブラウザーのセキュリティ機能により、Web ページでは AJAX 要求を別のドメインに送信することはできません。 この制限は、同一オリジン ポリシー と呼ばれ、悪意のあるサイトが、別のサイトから機密データを読み取れないようにします。 ただし、他のサイトから Web API を呼び出したほうが良い場合もあります。

クロスオリジンリソース共有 (CORS) は、サーバーが同じオリジンポリシーを緩めることを可能にする W3C 標準です。 CORS を使用することで、サーバーが一部のクロス オリジン要求を、その他の要求を拒否しながら、明示的に許可することができます。 CORS は、 JSONPなど、以前の手法よりも安全で柔軟性に優れています。 このチュートリアルでは、Web API アプリケーションで CORS を有効にする方法について説明します。

チュートリアルで使用するソフトウェア

はじめに

このチュートリアルでは、ASP.NET Web API での CORS のサポートについて説明します。 まず、2つの ASP.NET プロジェクトを作成します。1つは Web API コントローラーをホストする "WebService" と呼ばれ、もう1つは WebService を呼び出す "WebClient" です。 2つのアプリケーションは異なるドメインでホストされているため、WebClient から WebService への AJAX 要求はクロスオリジン要求です。

Web サービスと web クライアントを表示します

"同じオリジン" とは何ですか?

同じスキーム、ホスト、およびポートがある場合、2つの Url のオリジンは同じになります。 (RFC 6454)

この2つの Url のオリジンは同じです。

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

これらの Url は、前の2つとは異なるオリジンを持ちます。

  • http://example.net -異なるドメイン
  • http://example.com:9000/foo.html -別のポート
  • https://example.com/foo.html -異なるスキーム
  • http://www.example.com/foo.html -異なるサブドメイン

Note

Internet Explorer では、オリジンを比較するときにポートは考慮されません。

WebService プロジェクトを作成する

Note

このセクションでは、Web API プロジェクトを作成する方法を既に理解していることを前提としています。 それ以外の場合は、「 はじめに ASP.NET Web API」を参照してください。

  1. Visual Studio を起動し、新しい ASP.NET Web アプリケーション (.NET Framework) プロジェクトを作成します。

  2. [ New ASP.NET Web アプリケーション ] ダイアログボックスで、 のプロジェクトテンプレートを選択します。 [ のフォルダーとコア参照の追加] で、[ Web API ] チェックボックスをオンにします。

    Visual Studio の [新しい ASP.NET プロジェクト] ダイアログ

  3. TestController次のコードを使用して、という名前の WEB API コントローラーを追加します。

    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 にデプロイすることができます。 (このチュートリアルのスクリーンショットでは、アプリが Azure App Service Web Apps にデプロイされます)。Web API が動作していることを確認するには、に移動し http://hostname/api/test/ ます。 hostname は、アプリケーションを配置したドメインです。 "GET: Test Message" という応答テキストが表示され " " ます。

    テストメッセージを表示している Web ブラウザー

WebClient プロジェクトを作成する

  1. 別の ASP.NET Web アプリケーション (.NET Framework) プロジェクトを作成し、 MVC プロジェクトテンプレートを選択します。 必要に応じて、[認証なし認証を 変更 する] を選択し > ます。 このチュートリアルでは、認証は必要ありません。

    Visual Studio の [New ASP.NET Project] ダイアログの MVC テンプレート

  2. ソリューションエクスプローラー で、 Views/Home/Index. cshtml というファイルを開きます。 このファイルのコードを次のコードに置き換えます。

    <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 を使用します。

  3. WebClient アプリをローカルで実行するか、別の web サイトに発行します。

[試してみる] ボタンをクリックすると、ドロップダウンボックスに表示されている HTTP メソッド (GET、POST、または PUT) を使用して、AJAX 要求が WebService アプリに送信されます。 これにより、さまざまなクロスオリジン要求を調べることができます。 現在、WebService アプリでは CORS がサポートされていないため、ボタンをクリックするとエラーが発生します。

ブラウザーで ' Try it ' エラーが発生しています

Note

Fiddlerのようなツールで HTTP トラフィックを監視すると、ブラウザーが GET 要求を送信したことがわかりますが、要求は成功しますが、AJAX 呼び出しではエラーが返されます。 同じ配信元ポリシーでは、ブラウザーが要求を 送信 できないようにする必要があることを理解しておくことが重要です。 代わりに、アプリケーションで 応答 が表示されないようにします。

Web 要求を表示している Fiddler web デバッガー

CORS を有効にする

次に、WebService アプリで CORS を有効にしましょう。 まず、CORS NuGet パッケージを追加します。 Visual Studio で、[ ツール ] メニューの [ NuGet パッケージマネージャー] を選択し、[ パッケージマネージャーコンソール] を選択します。 [パッケージマネージャーコンソール] ウィンドウで、次のコマンドを入力します。

Install-Package Microsoft.AspNet.WebApi.Cors

このコマンドを実行すると、最新のパッケージがインストールされ、コア Web API ライブラリを含むすべての依存関係が更新されます。 特定の -Version バージョンを対象にするには、フラグを使用します。 CORS パッケージには、Web API 2.0 以降が必要です。

File App _ Start/webapiconfig.cs を開きます。 Webapiconfig.cs メソッドに次のコードを追加します。

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 ます。

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 を使用します。 これにより、WebClient からのクロスオリジン要求が可能になりますが、他のすべてのドメイン間要求は引き続き許可されません。 後で、 [Enablecors] のパラメーターについてさらに詳しく説明します。

オリジン の URL の末尾にスラッシュを含めないでください。

更新された WebService アプリケーションを再デプロイします。 WebClient を更新する必要はありません。 これで、WebClient からの AJAX 要求が成功するはずです。 GET、PUT、および POST メソッドはすべて許可されています。

成功したテストメッセージを示す Web ブラウザー

CORS のしくみ

このセクションでは、HTTP メッセージのレベルでの CORS 要求の動作について説明します。 CORS がどのように機能するかを理解しておくことが重要です。これにより、 [Enablecors] 属性を適切に構成し、予期したとおりに動作しない場合にトラブルシューティングを行うことができます。

CORS 仕様には、クロスオリジン要求を有効にするための新しい HTTP ヘッダーがいくつか導入されています。 ブラウザーが CORS をサポートしている場合、クロスオリジン要求に対してこれらのヘッダーが自動的に設定されます。JavaScript コードで特別な操作を行う必要はありません。

クロスオリジン要求の例を次に示します。 "Origin" ヘッダーは、要求を行っているサイトのドメインを示します。

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

サーバーが要求を許可している場合は、アクセス制御-許可元ヘッダーが設定されます。 このヘッダーの値は Origin ヘッダーに一致します。または、ワイルドカード値 "" である場合は、 * 任意のオリジンが許可されます。

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 要求は失敗します。 具体的には、ブラウザーは要求を許可しません。 サーバーが応答を正常に返す場合でも、ブラウザーはクライアントアプリケーションで応答を使用できません。

プレフライト要求

一部の CORS 要求については、ブラウザーは "プレフライト要求" と呼ばれる追加の要求を送信してから、リソースに対する実際の要求を送信します。

次の条件に該当する場合、ブラウザーはプレフライト要求をスキップできます。

  • 要求メソッドは GET、HEAD 、または POST です。

  • アプリケーションで は、accept 、Accept-Language、Content-type、content-type、または LAST イベント ID 以外の要求ヘッダーは設定されません。

  • Content-type ヘッダー (設定されている場合) は、次のいずれかになります。

    • application/x-www-form-urlencoded
    • マルチパート/フォーム-データ
    • text/plain

要求ヘッダーに関する規則は、 XMLHttpRequest オブジェクトで setRequestHeader を呼び出すことによって、アプリケーションが設定するヘッダーに適用されます。 (CORS 仕様は、これらの "author 要求ヘッダー" を呼び出します)。この規則は、 ブラウザー が設定できるヘッダー (ユーザーエージェント、ホスト、コンテンツの長さなど) には適用されません。

プレフライト要求の例を次に示します。

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 メソッドを使用します。 次の2つの特殊なヘッダーが含まれます。

  • アクセス制御要求メソッド: 実際の要求に使用される HTTP メソッド。
  • アクセス制御要求ヘッダー: アプリケーション が実際の要求に設定した要求ヘッダーのリスト。 (この場合も、ブラウザーによって設定されるヘッダーは含まれません)。

サーバーが要求を許可していると仮定した場合の応答例を次に示します。

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

応答には、許可されたメソッドの一覧を示すアクセス制御許可のヘッダーと、必要に応じて、許可されるヘッダーの一覧を示すアクセス制御-許可ヘッダーヘッダーが含まれています。 プレフライト要求が成功した場合、前に説明したように、ブラウザーは実際の要求を送信します。

プレフライトオプション要求 (たとえば、 FiddlerPostman) を使用してエンドポイントをテストするために一般的に使用されるツールは、既定で必要なオプションヘッダーを送信しません。 Access-Control-Request-Methodとの Access-Control-Request-Headers ヘッダーが要求と共に送信されること、および OPTIONS ヘッダーが IIS を介してアプリに渡されることを確認します。

ASP.NET アプリがオプション要求を受信して処理できるように IIS を構成するには、次の構成をセクションのアプリの web.config ファイルに追加し <system.webServer><handlers> ます。

<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 がオプションの要求を処理できなくなります。 既定のモジュールの登録では、 ExtensionlessUrlHandler-Integrated-4.0 拡張子の url を使用した GET、HEAD、POST、および DEBUG 要求のみが許可されるため、の代わりに、オプションの要求がアプリに到達できるようになります。

[EnableCors] のスコープルール

CORS は、アプリケーション内のすべての Web API コントローラーに対して、アクションごと、コントローラー単位、またはグローバルに有効にすることができます。

アクションごと

単一のアクションに対して CORS を有効にするには、アクションメソッドで [Enablecors] 属性を設定します。 次の例では、メソッドに対してのみ CORS を有効にし GetItem ます。

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) { ... }
}

コントローラーごと

コントローラークラスで [Enablecors] を設定すると、コントローラー上のすべてのアクションに適用されます。 アクションに対して CORS を無効にするには、アクションに [Disablecors] 属性を追加します。 次の例では、を除くすべてのメソッドに対して CORS を有効にし 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) { ... }
}

グローバル

アプリケーション内のすべての Web API コントローラーに対して CORS を有効にするには、 EnableCorsAttribute インスタンスを enablecors メソッドに渡します。

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

複数のスコープで属性を設定した場合、優先順位は次のようになります。

  1. アクション
  2. コントローラー
  3. グローバル

許可されるオリジンの設定

[Enablecors] 属性の オリジン パラメーターは、リソースへのアクセスが許可されているオリジンを指定します。 値は、許可されたオリジンのコンマ区切りのリストです。

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

また、 * 任意のオリジンからの要求を許可するために、ワイルドカード値 "" を使用することもできます。

配信元からの要求を許可する前に、慎重に検討してください。 これは、文字どおりの web サイトが web API に対して AJAX 呼び出しを行うことができることを意味します。

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

許可される HTTP メソッドを設定する

[Enablecors] 属性の メソッド パラメーターでは、リソースへのアクセスを許可する HTTP メソッドを指定します。 すべてのメソッドを許可するには、ワイルドカード値 "" を使用し * ます。 次の例では、GET および POST 要求のみが許可されます。

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

許可された要求ヘッダーを設定する

この記事では、前に説明したプレフライト要求に、アプリケーションによって設定された HTTP ヘッダー (いわゆる "author 要求ヘッダー") を一覧表示するための、アクセス制御要求ヘッダーヘッダーが含まれている可能性があります。 [Enablecors] 属性の headers パラメーターでは、許可される作成者要求ヘッダーを指定します。 ヘッダーを許可するには、 ヘッダー を "" に設定 * します。 特定のヘッダーを許可するには、 ヘッダー を、許可されているヘッダーのコンマ区切りのリストに設定します。

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

ただし、ブラウザーでは、アクセス制御要求ヘッダーの設定方法が完全に一致しているわけではありません。 たとえば、Chrome には現在 "origin" が含まれています。 FireFox には、アプリケーションでスクリプトに設定されている場合でも、"Accept" などの標準ヘッダーは含まれません。

ヘッダー を "" 以外の値に設定する場合は、 * 少なくとも "accept"、"content-type"、および "origin" と、サポートするカスタムヘッダーを含める必要があります。

許可される応答ヘッダーを設定する

既定では、ブラウザーはアプリケーションに応答ヘッダーをすべて公開しません。 既定で使用できる応答ヘッダーは次のとおりです。

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

CORS 仕様は、これらの 単純な応答ヘッダーを呼び出します。 アプリケーションで他のヘッダーを使用できるようにするには、 [enablecors] のパラメーターを設定します。

次の例では、コントローラーのメソッドによって、 Get ' X-custom ヘッダー ' という名前のカスタムヘッダーが設定されています。 既定では、ブラウザーは、このヘッダーをクロスオリジン要求で公開しません。 ヘッダーを使用できるようにするには、 exposedHeaders に ' X-Custom ヘッダー ' を含めます。

[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;
    }
}

クロスオリジン要求で資格情報を渡す

資格情報では、CORS 要求で特別な処理を行う必要があります。 既定では、ブラウザーは、クロスオリジン要求で資格情報を送信しません。 資格情報には、cookie と HTTP 認証スキームが含まれます。 クロスオリジン要求で資格情報を送信するには、クライアントで XMLHttpRequest. withCredentials を true に設定する必要があります。

XMLHttpRequest の直接使用:

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

JQuery の場合:

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

また、サーバーは資格情報を許可する必要があります。 Web API でのクロスオリジンの資格情報を許可するには、 [Enablecors] 属性の [ ] プロパティを true に設定します。

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

このプロパティが true の場合、HTTP 応答には、アクセス制御-許可-資格情報ヘッダーが含まれます。 このヘッダーは、サーバーがクロスオリジン要求に対して資格情報を許可することをブラウザーに指示します。

ブラウザーが資格情報を送信しても、応答に有効なアクセス制御-資格情報のヘッダーが含まれていない場合、ブラウザーはアプリケーションへの応答を公開せず、AJAX 要求は失敗します。

SupportsCredentials を true に設定することに注意してください。これは、別のドメインの web サイトがユーザーの代わりに、ログインしているユーザーの資格情報をユーザーの代わりに web API に送信できることを意味します。 また、CORS 仕様では、 " * " SupportsCredentials が true の場合、オリジンをに設定することはできません。

カスタム CORS ポリシープロバイダー

[Enablecors] 属性は、 ICorsPolicyProvider インターフェイスを実装します。 属性 から派生するクラスを作成し、 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] として指定する任意の場所に属性を適用できます。

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

たとえば、カスタム CORS ポリシープロバイダーは、構成ファイルから設定を読み取ることができます。

属性を使用する代わりに、 ICorsPolicyProvider オブジェクトを作成する ICorsPolicyProviderFactory オブジェクトを登録できます。

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

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

ICorsPolicyProviderFactory を設定するには、スタートアップ時に Setcorspolicyproviderfactory 拡張メソッドを呼び出します。次に例を示します。

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

        // ...
    }
}

ブラウザーのサポート

Web API CORS パッケージは、サーバー側のテクノロジです。 ユーザーのブラウザーも CORS をサポートする必要があります。 幸い、すべての主要なブラウザーの現在のバージョンには CORS のサポートが含まれています。