AngularJSでのHTTPrequest(主にPOST)にはまった件 #ng_jp


[@can_i_do_web](http://twitter.com/can_i_do_web)さんに指名(?)を頂いたといことで。
[AngularJS Advent Calendar 2014 (adventarの方)](http://www.adventar.org/calendars/350) の1日目の記事です! 先陣を切らせて頂きます。

![](http://i.gyazo.com/85693d5bb6f71121c74d0aa6ab5d8562.png)


##はじめに

今回は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. 
```

![](http://i.gyazo.com/bdc94242873de48ff09caa48a61b7d53.png)

### 基本的にサーバー側で解決

[続 クロスドメインで使う XMLHttpRequest と CORS の話](http://tadtak.jugem.jp/?eid=59)にもあるようにサーバー側で全てのオリジンを許可する必要があります。

例えばPHPでAPIを作ってる場合は

```hoge.php
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設計での開発でのジレンマと、フロントエンジニアのサーバー側を実装について](http://qiita.com/n0bisuke/items/a472fcfa08c623ae094d)で書いた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側のログはこんな感じです

![](http://i.gyazo.com/c529e3db4c7a37439b0e3fdcbacba7f6.png)

#### 解決策: `$.param()` を使う

色々探しまわってたら、Stack Over Flowで見つけました。 
[How can I post data as form data instead of a request payload?](http://stackoverflow.com/questions/11442632/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設計での開発でのジレンマと、フロントエンジニアのサーバー側を実装について](http://qiita.com/n0bisuke/items/a472fcfa08c623ae094d)でも書いたのですが、API設計でのAPI結合テストでは`Access-Control-Allow-Origin.`に限らず問題発生があるかもしれないので **HTTP Header** の知識は必要ですね。

ここでかなり詰まってしまったのでこれから爆速で追い返します。