AngularJSはクロスオリジンリソースに対してOPTIONS HTTPリクエストを実行します


264

テンプレートファイルを配信するアセットホストが別のドメインにあるクロスオリジンリソースと通信するようにAngularJSを設定しようとしています。そのため、angularが実行するXHRリクエストはクロスドメインでなければなりません。これを機能させるために、HTTPリクエスト用の適切なCORSヘッダーをサーバーに追加しましたが、機能していないようです。問題は、ブラウザー(クロム)でHTTPリクエストを検査すると、アセットファイルに送信されるリクエストがOPTIONSリクエスト(GETリクエストであるはず)であることです。

これがAngularJSのバグなのか、何かを設定する必要があるのか​​わかりません。XHRラッパーはOPTIONS HTTPリクエストを作成できないので、ブラウザーがGETリクエストを実行する前に最初にアセットをダウンロードできるかどうかを判断しようとしているようです。この場合、資産ホストにもCORSヘッダー(Access-Control-Allow-Origin:http : //asset.host ..。)を設定する必要がありますか?

回答:


226

OPTIONSリクエストは決してAngularJSのバグではありません。これが、Cross-Origin Resource Sharing標準がブラウザに動作を要求する方法です。このドキュメントを参照してください:https : //developer.mozilla.org/en-US/docs/HTTP_access_control。ここで、「概要」セクションには次のように記載されています。

Cross-Origin Resource Sharing標準は、サーバーがWebブラウザーを使用してその情報を読み取ることが許可されているオリジンのセットを記述できるようにする新しいHTTPヘッダーを追加することで機能します。さらに、ユーザーデータに副作用を引き起こす可能性のあるHTTPリクエストメソッド(特に、GET以外のHTTPメソッド、または特定のMIMEタイプでのPOSTの使用)。この仕様では、ブラウザがリクエストを「プリフライト」し、サポートされているメソッドをHTTP OPTIONSリクエストヘッダーでサーバーから要求し、サーバーから「承認」されると、実際のリクエストを実際のHTTPリクエストメソッドで送信することを義務付けています。サーバーは、「クレデンシャル」(CookieおよびHTTP認証データを含む)をリクエストとともに送信する必要があるかどうかをクライアントに通知することもできます。

セットアップはサーバー自体とサポートする予定のHTTP動詞によって異なるため、すべてのWWWサーバーで機能する一般的なソリューションを提供することは非常に困難です。サーバーから送信する必要のある正確なヘッダーの詳細が記載されたこの優れた記事(http://www.html5rocks.com/en/tutorials/cors/)をお読みになることをお勧めします。


23
@matskoこれを解決するために何をしたか、詳しく説明できますか?AngularJS $resource POSTリクエストがバックエンドExpressJSサーバーへのOPTIONSリクエストを生成する(同じホスト上にあるがポートが異なる)同じ問題に直面しています。
dbau 2012年

6
すべての反対投票者- すべてのWebサーバーに正確な構成設定を提供することはほぼ不可能です-この場合、答えは10ページになります。代わりに、詳細を提供する記事にリンクしました。
pkozlowski.opensource 2013

5
答えを規定することはできませんが、ブラウザが要求するすべてのヘッダーをカバーするヘッダーをOPTIONS応答に追加する必要があります。私の場合は、chromeを使用して、ヘッダーは「accept」と「x」でした。 -requested-with '。Chromeでは、ネットワークリクエストを見て、Chromeが何を要求しているかを確認することで、何を追加する必要があるかを理解しました。nodejs / expressjsをバッキングとして使用しているので、必要なすべてのヘッダーをカバーするOPTIONSリクエストへの応答を返すサービスを作成しました。-1この答えを使用して何をすべきかを理解できなかったため、自分でそれを理解しなければなりませんでした。
Ed Sykes

2
私はそれが2年以上後であることを知っていますが... OPはGETリクエストを複数回参照します(強調は私が追加しました):«[...]アセットファイルに送信されたリクエストはOPTIONSリクエストです(GETである必要があります)リクエスト)» そして«ブラウザは、GETリクエストを実行する前に、最初アセットをダウンロードすることが「許可」されているかどうかを把握しようとしています»。では、どうしてAngularJSのバグではないのでしょうか?GETではプリフライトリクエストを禁止すべきではありませんか?
15

4
@polettixのGETリクエストでも、カスタムヘッダーが使用されている場合、ブラウザーでプリフライトリクエストをトリガーできます。リンクした記事html5rocks.com/en/tutorials/cors/#toc-making-a-cors-requestの「not-so-simple requests」を確認してください。ここでも、AngularJSではなく、ブラウザのメカニズムです。
pkozlowski.opensource 2015

70

Angular 1.2.0rc1 +の場合、resourceUrlWhitelistを追加する必要があります。

1.2:リリースバージョンでは、escapeForRegexp関数が追加されたため、文字列をエスケープする必要がなくなりました。あなたは直接URLを追加することができます

'http://sub*.assets.example.com/**' 

サブフォルダには**を必ず追加してください。ここでは1.2の作業jsbinは、次のとおりです。 http://jsbin.com/olavok/145/edit


1.2.0rc:まだrcバージョンを使用している場合、Angular 1.2.0rc1ソリューションは次のようになります。

.config(['$sceDelegateProvider', function($sceDelegateProvider) {
     $sceDelegateProvider.resourceUrlWhitelist(['self', /^https?:\/\/(cdn\.)?yourdomain.com/]);
 }])

ここでは、それは1.2.0rc1のために働くjsbinの例は次のとおりです。 http://jsbin.com/olavok/144/edit


1.2より前:古いバージョン(http://better-inter.net/enabling-cors-in-angular-js/を参照)の場合、次の2行を構成に追加する必要があります。

$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];

ここでは、前のバージョン1.2で動作しjsbinの例は次のとおりです。 http://jsbin.com/olavok/11/edit


これは私にとってはうまくいった。nodejs / expressを使用したサーバー側の適切なマジックは次のとおりです。gist.github.com/ dirkk0 / 5967221
dirkk0

14
これはGETリクエストでのみ機能しますが、クロスドメインでのPOSTリクエストの解決策はまだ見つかりません。
2013

2
この回答を上記のuser2304582回答と組み合わせると、POSTリクエストで機能するはずです。それを送信した外部サーバーからのPOSTリクエストを受け入れるようにサーバーに指示する必要があります
Charlie Martin

これは私にはそれ自体で機能するようには見えません...したがって-1です。プリフライトチェックの一部としてOPTIONSリクエストに応答する同じURLのものが必要です。これがうまくいく理由を説明できますか?
Ed Sykes

1
@EdSykes、私は1.2の回答を更新し、動作するjsbinの例を追加しました。うまくいけば、それはあなたのためにそれを解決するはずです。[JSで実行]ボタンを必ず押してください。
JStark 2013年

62

注: Angularの最新バージョンで動作するかどうかは不明です。

元の:

OPTIONSリクエストを上書きすることもできます(Chromeでのみテストされました)。

app.config(['$httpProvider', function ($httpProvider) {
  //Reset headers to avoid OPTIONS request (aka preflight)
  $httpProvider.defaults.headers.common = {};
  $httpProvider.defaults.headers.post = {};
  $httpProvider.defaults.headers.put = {};
  $httpProvider.defaults.headers.patch = {};
}]);

1
Chrome、FF、IE 10でうまく動作します。IE9以下は、Windows 7またはXPマシンでネイティブにテストする必要があります。
DOM

3
また一つは、グローバルそれだけで$リソースの方法でこれを設定していないことができます$resource('http://yourserver/yourentity/:id', {}, {query: {method: 'GET'});
h7r

3
これで何か問題はありますか?とてもシンプルに見えますが、答えはスレッドの奥深くにありますか?編集:まったく機能しませんでした。
Sebastialonso

このコードはどこに行きますか?app.js、controller.js、または正確にどこに。
Murlidhar Fichadia 16

このコードは、getリクエストに対しては何もしません。getにも別のルールを構成に追加しました
kushalvm '19年

34

サービスは、OPTIONS次のようなヘッダーでリクエストに応答する必要があります。

Access-Control-Allow-Origin: [the same origin from the request]
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: [the same ACCESS-CONTROL-REQUEST-HEADERS from request]

ここに良いドキュメントがあります:http : //www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server


1
[リクエストからの同じACCESS-CONTROL-REQUEST-HEADERS]の部分について詳しく説明するには、別の回答で述べたように、ブラウザーが追加するOPTIONSリクエストを確認する必要があります。次に、構築するサービス(またはWebサーバー)にそれらのヘッダーを追加します。次のようなexpressjsの場合:ejs.options( ' '、function(request、response){response.header( 'Access-Control-Allow-Origin'、 ' '); response.header( 'Access-Control-Allow-Methods '、' GET、PUT、POST、DELETE '); response.header(' Access-Control-Allow-Headers '、' Content-Type、Authorization、accept、x-requested-with '); response.send(); });
Ed Sykes

1
Ed Sykesのコメントは非常に正確です。OPTIONS応答で送信されるヘッダーとPOST応答で送信されるヘッダーは完全に同じである必要があります。たとえば、Access-Control-Allow-Origin:*はAccess-と同じではありません。 Control-Allow-Origin:*スペースのため。
ルシア

20

同じ文書は言う

単純な要求(上記で説明)とは異なり、「プリフライト」要求は、実際の要求を送信しても安全かどうかを判断するために、最初に他のドメインのリソースにHTTP OPTIONS要求ヘッダーを送信します。クロスサイトリクエストは、ユーザーデータに影響を与える可能性があるため、このようにプリフライトされます。特に、リクエストは次の場合にプリフライトされます。

GETまたはPOST以外のメソッドを使用します。また、POSTを使用して、application / x-www-form-urlencoded、multipart / form-data、text / plain以外のContent-Typeでリクエストデータを送信する場合、たとえば、POSTリクエストがXMLペイロードをサーバーに送信する場合application / xmlまたはtext / xmlを使用すると、リクエストはプリフライトされます。

リクエストにカスタムヘッダーを設定します(たとえば、リクエストはX-PINGOTHERなどのヘッダーを使用します)

元の要求がカスタムヘッダーなしのGetである場合、ブラウザーは、現在のようにOptions要求を行わないでください。問題は、オプション要求を強制するヘッダーX-Requested-Withを生成することです。このヘッダーを削除する方法については、https://github.com/angular/angular.js/pull/1454を参照してください


10

これは私の問題を修正しました:

$http.defaults.headers.post["Content-Type"] = "text/plain";

10

nodeJSサーバーを使用している場合は、このライブラリを使用できます。https://github.com/expressjs/cors

var express = require('express')
  , cors = require('cors')
  , app = express();

app.use(cors());

そしてあなたができる後npm update


4

ASP.NETでこの問題を修正した方法は次のとおりです

  • 最初に、nugetパッケージMicrosoft.AspNet.WebApi.Corsを追加する必要があります

  • 次に、App_Start \ WebApiConfig.csファイルを変更します。

    public static class WebApiConfig    
    {
       public static void Register(HttpConfiguration config)
       {
          config.EnableCors();
    
          ...
       }    
    }
  • この属性をコントローラークラスに追加します

    [EnableCors(origins: "*", headers: "*", methods: "*")]
    public class MyController : ApiController
    {  
        [AcceptVerbs("POST")]
        public IHttpActionResult Post([FromBody]YourDataType data)
        {
             ...
             return Ok(result);
        }
    }
  • このようにしてjsonをアクションに送ることができました

    $http({
            method: 'POST',
            data: JSON.stringify(data),
            url: 'actionurl',
            headers: {
                'Content-Type': 'application/json; charset=UTF-8'
            }
        }).then(...)

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


2

どういうわけか私は変更して修正しました

<add name="Access-Control-Allow-Headers" 
     value="Origin, X-Requested-With, Content-Type, Accept, Authorization" 
     />

<add name="Access-Control-Allow-Headers" 
     value="Origin, Content-Type, Accept, Authorization" 
     />

0

pkozlowskiのコメントに完全に記載されています。AngularJS 1.2.6とASP.NET Web APIを使用したソリューションを使用していましたが、AngularJSを1.3.3にアップグレードすると、リクエストが失敗しました。

  • Web APIサーバーのソリューションは、設定メソッドの最初にOPTIONSリクエストの処理を追加することでした(このブログ投稿の詳細):

    app.Use(async (context, next) =>
    {
        IOwinRequest req = context.Request;
        IOwinResponse res = context.Response;
        if (req.Path.StartsWithSegments(new PathString("/Token")))
        {
            var origin = req.Headers.Get("Origin");
            if (!string.IsNullOrEmpty(origin))
            {
                res.Headers.Set("Access-Control-Allow-Origin", origin);
            }
            if (req.Method == "OPTIONS")
            {
                res.StatusCode = 200;
                res.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Methods", "GET", "POST");
                res.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Headers", "authorization", "content-type");
                return;
            }
        }
        await next();
    });

上記ではうまくいきません。CORSをASP.NET WEB API 2.2以降のAngularJSで使用できるようにするためのはるかに簡単なソリューションがあります。NugetからMicrosoft WebAPI CORSパッケージを取得し、WebAPI構成ファイルで取得します... var cors = new EnableCorsAttribute( "www.example.com"、 " "、 " "); config.EnableCors(cors); 詳細はこちらMicrosoftのサイト上にあるasp.net/web-api/overview/security/...
kmcnamee

0

REST APIにJerseyを使用している場合は、以下のようにすることができます

Webサービスの実装を変更する必要はありません。

Jersey 2.xについて説明します

1)最初に以下に示すようにResponseFilterを追加します

import java.io.IOException;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;

public class CorsResponseFilter implements ContainerResponseFilter {

@Override
public void filter(ContainerRequestContext requestContext,   ContainerResponseContext responseContext)
    throws IOException {
        responseContext.getHeaders().add("Access-Control-Allow-Origin","*");
        responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");

  }
}

2)次にweb.xmlのjerseyサーブレット宣言に以下を追加します

    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>YOUR PACKAGE.CorsResponseFilter</param-value>
    </init-param>

0

私はこの問題を解決しようとあきらめました。

IISのweb.configに関連する「Access-Control-Allow-Methods」が含まれていたので、Angularコードに設定を追加して実験しましたが、数時間かけてChromeにクロスドメインのJSON Webサービスを呼び出させようとしたところ、無残に諦めました。

最後に、私はASP.NetハンドラWebページダムを追加し、得たことは、私のJSON Webサービスを呼び出し、結果を返すように。2分で稼働しました。

これが私が使ったコードです:

public class LoadJSONData : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/plain";

        string URL = "......";

        using (var client = new HttpClient())
        {
            // New code:
            client.BaseAddress = new Uri(URL);
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            client.DefaultRequestHeaders.Add("Authorization", "Basic AUTHORIZATION_STRING");

            HttpResponseMessage response = client.GetAsync(URL).Result;
            if (response.IsSuccessStatusCode)
            {
                var content = response.Content.ReadAsStringAsync().Result;
                context.Response.Write("Success: " + content);
            }
            else
            {
                context.Response.Write(response.StatusCode + " : Message - " + response.ReasonPhrase);
            }
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

そして、私のAngularコントローラーで...

$http.get("/Handlers/LoadJSONData.ashx")
   .success(function (data) {
      ....
   });

これを行うためのより簡単な/より一般的な方法があると確信していますが、人生は短すぎます...

これは私にとってはうまくいきました、そして私は今通常の仕事を続けることができます!!


0

IIS MVC 5 / Angular CLI(はい、私はあなたの問題がAngular JSにあることをよく知っています)APIを使用するプロジェクトの場合、次のようにしました:

<system.webServer>ノードの下のweb.config

    <staticContent>
      <remove fileExtension=".woff2" />
      <mimeMap fileExtension=".woff2" mimeType="font/woff2" />
    </staticContent>
    <httpProtocol>
      <customHeaders>
        <clear />
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type, atv2" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS"/>
      </customHeaders>
    </httpProtocol>

global.asax.cs

protected void Application_BeginRequest() {
  if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OrdinalIgnoreCase) && Request.HttpMethod == "OPTIONS") {
    Response.Flush();
    Response.End();
  }
}

これにより、MVCとWebAPIの両方の問題が修正され、他のすべてを実行する必要がなくなります。次に、関連するヘッダー情報に自動的に追加されるHttpInterceptorをAngular CLIプロジェクトで作成しました。これが同様の状況で誰かを助けることを願っています。


0

パーティーに少し遅れて、

APIとしてAngular 7(または5/6/7)とPHPを使用していてもこのエラーが発生する場合は、エンドポイント(PHP API)に次のヘッダーオプションを追加してみてください。

 header("Access-Control-Allow-Origin: *");
 header("Access-Control-Allow-Methods: PUT, GET, POST, PUT, OPTIONS, DELETE, PATCH");
 header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization");

:必要なのはだけですAccess-Control-Allow-Methods。ただし、ここでは他の2つとAccess-Control-Allow-Originを貼り付けAccess-Control-Allow-Headersています。これは、Angular AppがAPIと適切に通信するために、これらすべてを適切に設定する必要があるためです。

これが誰かを助けることを願っています。

乾杯。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.