Phoenix で外部 API にリクエストする API を作ってみます。OpenWeatherMap API に問い合わせて、指定された緯度経度の天気を JSON で返却するという 無駄な挙動をさせてみましょう。
Phoenix で JSON API 作る方法はコチラを参照してください。
依存関係に HTTPoison を追加する
Elixir 版のサードパーティライブラリアーカイブ(PyPI や npm といった類のもの)には Hex があります。
ここに公開されているライブラリは Mix を使うことで簡単にプロジェクトに追加でき、またこれらの依存関係は mix.exs
というファイルで管理できます。
今回は HTTP Client として HTTPoison を利用しますので、以下のように追記します。
...
# Specifies your project dependencies
#
# Type `mix help deps` for examples and options
defp deps do
[{:phoenix, "~> 0.17"},
{:phoenix_ecto, "~> 1.1"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 2.1"},
{:phoenix_live_reload, "~> 1.0", only: :dev},
{:cowboy, "~> 1.0"},
# HTTPoison を追記
{:httpoison, "~> 0.7.2"}]
end
end
その後、以下のコマンドを実行すると、ファイルがダウンロードされます。
$ mix deps.get
サーバを起動している場合は、忘れずに再起動しておきましょう。
ルートを追加する
/api/weahter/:lat/:lon で天気を問い合わせられるようにします。
scope "/api", SampleApp do
pipe_through :api
get "/weather/:lat/:lon", PageController, :weather
end
OpenWeatherMap API に問い合わせる
コントローラに HTTP リクエストの処理を書きます。
...
def weather(conn, %{"lat" => lat, "lon" => lon}) do
HTTPoison.start
result = HTTPoison.get! "http://api.openweathermap.org/data/2.5/weather?units=metric&lat=#{lat}&lon=#{lon}"
case result do
%{status_code: 200, body: body} -> json conn, Map.get(Poison.decode!(body), "main")
%{status_code: code} -> json conn, %{error: code}
end
end
end
HTTPoison.start の後に HTTPoison.get! とすることで GET リクエストが発行されます。
OpenWeatherMap API のエンドポイントに対して入力された緯度経度を与えます。Elixir では文字列の中に #{...}
と記述することで、変数を結合させられます。
リクエスト結果は変数 result に束縛させ、case に渡します。
case 構文のなかではパターンマッチが行われます。
1つ目の句は、status_code が 200 のときにマッチします。マッチングの際に body プロパティの中身を変数 body に束縛しておき、中の処理でそれのデコードと返却をします。
Elixir の句は上から順に評価されるため、status_code が 200 以外のリクエストは自然と2つ目の句にマッチします。この場合は status_code を束縛しておき、それを返却します。
ちなみに、Elixir の関数名には !
が付いているものと付いていないものがありますが、これにはエラーを発生させるかどうかの違いがあります。
例えば上記の HTTPosion.get!
には HTTPoison.get
もあり、返却はそれぞれ以下のようになります。
iex> HTTPoison.get! "..."
%HTTPoison.Response{ ... }
iex> HTTPoison.get "..."
{:ok, %HTTPoison.Response{ ... }}
get!/1
の場合は関数実行時にエラーが発生した場合はそのままスローされます。
一方、get/1
の場合はエラーは一切スローされず {:error, ...}
という返却がされます。
エラーハンドリング次第で使い分ける、という感じですね。
リクエストしてみる
サーバを起動してリクエストしてみましょう。
六本木ヒルズの緯度経度を投げてみます。
$ curl -s "http://localhost:4000/api/weather/35.6585373/139.7151887" | jq .
{
"humidity": 100,
"pressure": 1015,
"temp": 20.21,
"temp_max": 23.33,
"temp_min": 15.56
}
ちゃんとそれっぽい数値が取れました。