Edited at

Phoenixで作ったアプリでCORSする

More than 1 year has passed since last update.


TL;DR



  • cors_plugを入れる

  • routingのpipelineにplug CORSPlugを追加

  • 各routeにOPTIONSを追加


動機

phoenixで作ったアプリのAPIをchrome拡張から叩こうとしたらCORSでハマって1時間苦労しました。

そもそもCORSをよく知らなかったのが大きくハマった原因。分かってる人なら「ああ、こうすれば解決するね」ってすぐ分かると思います。


環境


  • Ubuntu 16.04

サーバ側


  • Elixir 1.3.4

  • Phoenix 1.2.1

  • CORSPlug 1.1.3

クライアント側


  • chrome 55

  • superagent 3.3.1


流れ


1. Chromeに怒られる

chrome拡張からapiを叩いたら

XMLHttpRequest cannot load http://localhost/api/items. Origin null is not allowed by Access-Control-Allow-Origin.

とか言われる。

phoenixではcors_plugを使えばCORSに対応できるらしい。


mix.exs

  defp deps do

[
# ...
{:cors_plug, "~> 1.2"} # 追加
]
end

$ mix deps.get


web/router.ex

  pipeline :api do

plug :accepts, ["json"]
plug CORSPlug # 追加
end

今回は全てのホストに対して許可していますが、特定のホストに対してCORSを許可したい時は、この辺を読んでください。

cors_plugのREADMEには「lib/your_app/endpoint.exに追加するのを勧める」って書いてあるのですが、web/router.exじゃダメなのでしょうか・・・?


2. また怒られる

以上でGETメソッドは叩けるようになったのですが、CREATEメソッドを叩くと今度はまた別のエラーがでました。

OPTIONS http://localhost:4000/api/items 404 (Not Found)

XMLHttpRequest cannot load http://localhost:4000/api/items. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'chrome-extension://fjimhgkchedkbdhbohdgabmafmfgcipg' is therefore not allowed access. The response had HTTP status code 404.

どうも事前にOPTIONSも叩くようなのでそれ用のルートも追加します。


web/router.ex

  scope "/", PhoenixApp do

resources "/items", ItemController
options "/items", ItemController, :options # 追加
options "/items/:id", ItemController, :options # 追加
end

コントローラー側に手を加える必要はありません。

これで問題なくCREATEメソッドのapiも叩けるようになりました。


関係ない話

production用のDockerイメージを作って、そちらでも動かしたりしているのですが、phoenixアプリをビルドしたDockerイメージに対してdocker-compose.ymlvolumes: - .:/appと書いていたせいで_buildが上書きされ、ソースを書き換える→docker-compose buildしても挙動が変わらずとても無駄な苦労をしました。

ちなみにDockerfile内でADD . /appしておくとに_buildのディレクトリが使いまわせるため、phoenixアプリのコンパイル時にライブラリのコンパイルが走らず時間の節約になります。phoenixアプリのコンパイルは再度されるため、上記のような変なことは起こりません。

この辺りの話はまた別の機会に。


参考


Mithril.js と Phoenixで作ったAPIを連携 - Qiita

CORS in Phoenix | Leigh Halliday