@can_i_do_webさんに指名(?)を頂いたといことで。
AngularJS Advent Calendar 2014 (adventarの方) の1日目の記事です! 先陣を切らせて頂きます。
##はじめに
今回はAngularJSでHTTPrequestをするときにかなりハマった話(笑)をまとめたいと思います。
同じようにハマる人がいるかもしれないのでメモしておきます。
私のスペックは
- バックエンドのエンジニア
- Angular.JSは趣味に近い形で勉強中
- Angular.jsは雰囲気で書いていて理解はまだ追いついてない
という感じなので、初学者の人は同じエラーに出会うかもしれません
Access-Control-Allow-Origin.
今日はこのあたりでめちゃハマってました。 APIのレスポンスを返す側がクロスドメイン環境を許可していない場合にクライアント側で表示されるエラーっていう認識です。
こんな感じのエラーが出ます。
XMLHttpRequest cannot load http://localhost:1337/. Origin http://localhost:3000 is not allowed by Access-Control-Allow-Origin.
Failed to load resource: Origin http://localhost:3000 is not allowed by Access-Control-Allow-Origin.
基本的にサーバー側で解決
続 クロスドメインで使う XMLHttpRequest と CORS の話にもあるようにサーバー側で全てのオリジンを許可する必要があります。
例えばPHPでAPIを作ってる場合は
header('Access-Control-Allow-Origin: *');
とすればクロスドメイン環境でも問題無くアクセス出来ます。
クライアント側でのヘッダー問題もあった
今回は、クロスドメイン環境では分かるんですけど、同じドメイン化でも同様のエラーが発生しました。
いつもはjQueryで$.ajax
を使っていたのですが、AngularJSの場合は$http
や$resource
を使ってHTTP通信をします。この時の設定をしないとエラーが発生するようでした。
AngularJSでのhttp通信はCodeGridの記事が分かり易いです。
(https://app.codegrid.net/entry/angularjs-6)
後述するその辺の設定を(おそらく)jQueryだとデフォルトでやってくれていたんですね。
今回の構成
基本的にAPI設計になっています。
- サーバー側: Rails 4.2
- フロント側: AngularJS 1.3
生じてた問題
1. POSTしたつもりがmethodがOPTIONSになっていた
jQueryの$.ajaxと同じ形で単純にPOSTしているつもりだったのですが、
Access-Control-Allow-Origin
のエラーがコンソールに表示され、
サーガー側のログを見ると、methodがOPTIONSになっていると言われました。
API設計での開発でのジレンマと、フロントエンジニアのサーバー側を実装についてで書いたNode.jsサーバーで localhostに向けてHTTPリクエストをしてもAccess-Control-Allow-Origin. が発生していました。
サーバー側で受けとったデータを見ると
method: OPTIONS
post: {} object
といった
解決策: configの$httpProvierでHeaderを設定する
$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;application/json;charset=utf-8';
これを設定することでAccess-Control-Allow-Origin
エラーは出なくなります。
application/x-www-form-urlencoded をヘッダーに設定してURLエンコードした形でデータを渡す必要があったみたいです。
2. POSTデータのKey,Valueがおかしい
1の問題が解決されるとmethodはPOSTで送信されていました。
ただ、以下のような形でリクエストされていて、想定のリクエストと比べると 変 ですよね。
method: POST
post: { '{"user":{"email":"garden1122@karumai.com","password":"karumaimai","password_confirmation":"karumaimai"}}': '' } object
オブジェクトのキー名にPOSTリクエストのデータがurlエンコードされて全て文字列で入っちゃってる感じでした。
Rails側のログはこんな感じです
解決策: $.param()
を使う
色々探しまわってたら、Stack Over Flowで見つけました。
How can I post data as form data instead of a request payload?
データを送信するときに$.param()でパースして送信してあげると上手くいくみたいでした。
-
$http
の場合
var req = {
method: 'POST',
url: 'http://localhost:1337/',
data: $.param({fkey: "key"})
}
$http(req).success(function(){}).error(function(){});
-
$resource
の場合
UserModel.email = "aaa";
UserModel.save($.param({key: 'key'}), function(data){
console.log('seikou:',data);
});
こうすることで元々想定していた正しい形でHTTPリクエストを送ることができました :)
post: { 'user[email]': 'garden1122@karumai.com',
'user[password]': 'karumaimai',
'user[password_confirmation]': 'karumaimai' } object
ここまで来るのに大分掛かりました汗
まとめと学んだこと
今までAccess-Control-Allow-Origin
はサーバー側の設定で回避出来ると思っていたのですが、フロント側でもヘッダーの設定など設定を変えないと行けない部分があることを学びました。
まとめ
API設計での開発でのジレンマと、フロントエンジニアのサーバー側を実装についてでも書いたのですが、API設計でのAPI結合テストではAccess-Control-Allow-Origin.
に限らず問題発生があるかもしれないので HTTP Header の知識は必要ですね。
ここでかなり詰まってしまったのでこれから爆速で追い返します。