web-dev-qa-db-ja.com

AngularからのHTTPリクエストは、POSTの代わりにOPTIONSとして送信されます

Angular.jsアプリケーションからサーバーにHTTPリクエストを送信しようとしていますが、いくつかのCORSエラーを解決する必要があります。

HTTP要求は、次のコードを使用して行われます。

functions.test = function(foo, bar) {
    return $http({
        method: 'POST',
        url: api_endpoint + 'test',
        headers: {
            'foo': 'value',
            'content-type': 'application/json'
        },
        data: {
            bar:'value'
        }
    });
};

最初の試行では、いくつかのCORSエラーが発生しました。したがって、次の行をPHPスクリプトに追加しました:

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT');
header('Access-Control-Allow-Headers: X-Requested-With, Content-Type, Origin, Authorization, Accept, Client-Security-Token, Accept-Encoding, X-Auth-Token, content-type');

最初のエラーは解消されました。

Chromeのデベロッパーコンソールに次のエラーが表示されます。

angular.js:12011オプション http:// localhost:8000/test (無名関数)

423ef03a:1 XMLHttpRequestが読み込めません http:// localhost:8000/test 。プリフライトの応答に無効なHTTPステータスコード400が含まれています

そして、ネットワークリクエストは私が期待したように見えます(HTTPステータス400も期待されます):

network request

リクエストがOPTIONSとしてlocalhostに送信され、POSTとしてリモートサーバーに送信される理由を解決する方法(および理解方法)を想像できません。この奇妙な問題を解決する方法はありますか?

16
David

OPTIONSリクエストは、いわゆる プリフライトリクエスト と呼ばれ、 Cross-Originリソース共有(CORS)の一部です。 。ブラウザはこれを特定のドメインからのリクエストが許可されているかどうかを確認するのように使用します

  1. ブラウザーがリクエストを送信しようとしています。たとえば、_application/json_コンテンツタイプを含むPOSTリクエストがあるとします。
  2. そのため、最初にプリフライトOPTIONSリクエストを送信します
  3. サーバーが2XX以外のステータス応答で応答した場合、ブラウザーは実際の要求を送信しません(とにかく拒否されることがわかっているためです)。
  4. ブラウザがプリフライトリクエストに対して_HTTP 200 OK_応答を受け取った場合、ブラウザは実際のリクエストPOSTを送信します。

理論

簡単なリクエスト

ブラウザないプレフライトリクエストを送信する場合があります。これらはいわゆるシンプルリクエストであり、次の条件で使用されます。

  • 許可されている方法の1つ:
    • GET
    • HEAD
    • POST
  • ユーザーエージェントによって自動的に設定されるヘッダー(たとえば、接続、ユーザーエージェントなど)とは別に、手動で設定できるヘッダーは次のとおりです。
    • Accept
    • _Accept-Language_
    • _Content-Language_
    • _Content-Type_(ただし、以下の追加要件に注意してください)
    • DPR
    • Downlink
    • _Save-Data_
    • _Viewport-Width_
    • Width
  • Content-Typeヘッダーに許可される値は、次のとおりです。
    • _application/x-www-form-urlencoded_
    • _multipart/form-data_
    • _text/plain_
  • リクエストで使用されるXMLHttpRequestUploadオブジェクトにイベントリスナーは登録されていません。これらは_XMLHttpRequest.upload_プロパティを使用してアクセスされます。
  • ReadableStreamオブジェクトはリクエストで使用されません。

このようなリクエストは直接送信され、サーバーはリクエストを正常に処理するか、CORSルールに一致しない場合はエラーで応答します。いずれの場合も、応答にはCORSヘッダー_Access-Control-Allow-*_が含まれます。

プレフライトリクエスト

ブラウザare実際のリクエストがシンプルリクエストの条件を満たさない場合、プリフライトリクエストを送信します。

  • _application/xml_や_application/json_などのカスタムコンテンツタイプが使用されます。
  • リクエストメソッドがGETHEADまたはPOST以外である
  • POSTメソッドは、_application/x-www-form-urlencoded_、_multipart/form-data_または_text/plain_以外のコンテンツタイプです。

preflightリクエストへの応答に次の属性があることを確認してください

  • 成功したHTTPステータスコード、つまり_200 OK_
  • ヘッダー_Access-Control-Allow-Origin: *_(ワイルドカード_*_は、任意のドメインからのリクエストを許可します。もちろん、任意の特定のドメインを使用して、ここでのアクセスを制限できます)

反対側から、サーバーはCORSリクエストを拒否する次の属性を持つpre-flightリクエストに応答を送信するだけでよい:

  • 失敗したHTTPコード(つまり、_2XX_以外)
  • 成功HTTPコード(例:_200 OK_)、ただしCORSヘッダーなし(例:_Access-Control-Allow-*_)

詳細については Mozilla Developer Networkのドキュメント または例 HTML5RocksのCORSチュートリアル を参照してください。


TL; DR回答

したがって、あなたのケースでは、適切なヘッダーが存在します--プリフライト応答のHTTPステータスコードが_200 OK_または他の成功したもの(_2XX_)

APIにNODEサーバーを使用するAngular 2アプリの作成で非常によく似た問題が発生しました。

ローカルマシンで開発しているため、POSTアプリからAPIにAngularしようとすると、クロスオリジンヘッダーの問題が発生し続けました。

以下のように(ノードサーバーで)ヘッダーを設定するとGETリクエストで機能しましたが、私のPUTリクエストは空のオブジェクトをデータベースにポストし続けました。

header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT');
header('Access-Control-Allow-Headers: X-Requested-With, Content-Type, 
Origin, Authorization, Accept, Client-Security-Token, Accept-
Encoding, X-Auth-Token, content-type');

Dawid Ferenczyの投稿 を読んだ後、PREFLIGHTリクエストがサーバーに空白のデータを送信していることがわかりました。そのため、DBエントリが空でした。そのため、NODE JSサーバーに次の行を追加しました。

  if (req.method == "OPTIONS")
    {
        res.writeHead(200, {"Content-Type": "application/json"});
        res.end();
    }

したがって、私のサーバーはPREFLIGHT要求を無視し(そしてステータス200を返し、すべてがGroovyであることをブラウザーに通知します...)、そのようにして、実際の要求が通過し、実際のデータがDBにポストされます。

2
b0rgBart3

置くだけ

if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
header("HTTP/1.1 200 ");
exit;}

あなたのサーバーサイドアプリの冒頭で、あなたは大丈夫でなければなりません。

2
tmaringgele

スプリングブートアプリケーションの場合、corsリクエストを有効にするには、それぞれのコントローラーで@CrossOrigin(origins = "*", maxAge = 3600)を使用します。

このドキュメントを参照

1
Chintan Pandya