CowboyでGETとPOSTの値を扱ってみる

HTTPではGETとPOST時に値を追加してリクエストすることがよくあります。ですので今回はGETとPOST時の値の取得の仕方について記載していきます。

## はじめに

ErlangとCowboyの環境設定については以下の記事に記載しています。気になる方は参考にしてみてください。
[CowboyでHello Worldのサンプルを動かしてみる](https://qiita.com/kobatako/items/456642c42017e17cadca)

## Cowboyでのハンドラーの設定
まずはちゃっちゃかとハンドラーの設定をします。

```erlang:sandbox_app.erl
    Dispatch = cowboy_router:compile([
        {'_', [
            {"/get", get_handler, []},
            {"/post", post_handler, []}
        ]}
    ]),
    {ok, _} = cowboy:start_clear(http, [{port, 8010}], #{
        env => #{dispatch => Dispatch}
    }),
```
`/get`パスでは`get_handler`を、`/post`パスでは`post_handler`を実行するようにします。
あとは8010ポートでリクエストを待ち受けるようにします。

## GETでの値の取得
GETでリクエストが来た際に、値を取得します。今回は`echo=[何かしら]` でリクエストがくる想定で値を取得しています。まずは全体像になります。

```erlang:get_handler.erl
init(Req0, Opts) ->
    Method = cowboy_req:method(Req0),
    #{echo := Echo} = cowboy_req:match_qs([{echo, [], undefined}], Req0),
    Req = echo(Method, Echo, Req0),
    {ok, Req, Opts}.

echo(<<"GET">>, undefined, Req) ->
    cowboy_req:reply(400, #{}, <<"Missing echo parameter.">>, Req);
echo (<<"GET">>, Echo, Req) ->
    cowboy_req:reply(200, #{
        <<"content-type">> => <<"text/plain; charset=utf-8">>
    }, Echo, Req);
echo (_, _, Req) ->
    cowboy_req:reply(405, Req).
```
`Method = cowboy_req:method(Req0),`でリクエストできたメソッドを取得します。ここで取得できるのはGETになる想定です。もしGET以外できた場合は405をレスポンスするようになっています。

`#{echo := Echo} = cowboy_req:match_qs([{echo, [], undefined}], Req0),`でリクエストされた値を取得します。第一引数にリクエストされてきたどの値を取得するかを決めます。`{echo, [], undefined}`ではechoのキーのものを取得するように設定されてます。もしechoがない場合はデフォルト値として`undefined`が返ります。

`Req = echo(Method, Echo, Req0),`で取得したリクエスト値を元にレスポンスを返します。

```erlang:get_handler.erl
echo(<<"GET">>, undefined, Req) ->
    cowboy_req:reply(400, #{}, <<"Missing echo parameter.">>, Req);
echo (<<"GET">>, Echo, Req) ->
    cowboy_req:reply(200, #{
        <<"content-type">> => <<"text/plain; charset=utf-8">>
    }, Echo, Req);
echo (_, _, Req) ->
    cowboy_req:reply(405, Req).
```

`echo`関数内ではリクエストにechoのキーがあった場合は200のステータスで取得した値をBodyにセットしてレスポンスします。
もし、echoのキーがなかった場合は、400のステータスで"Missing echo parameter."をbodyにセットしてレスポンスします。

## POSTでの値の取得

次はPOSTでのリクエスト値の取得方法になります。まじは全体像を。

```erlang:post_handler.erl
init(Req0, Opts) ->
    Method = cowboy_req:method(Req0),
    HasBody = cowboy_req:has_body(Req0),
    Req = maybe_echo(Method, HasBody, Req0),
    {ok, Req, Opts}.

maybe_echo(<<"POST">>, true, Req0) ->
    {ok, PostValue, Req} = cowboy_req:read_urlencoded_body(Req0),
    Echo = proplists:get_value(<<"echo">>, PostValue, undefined),
    echo(Echo, Req);
maybe_echo(<<"POST">>, false, Req) ->
    cowboy_req:reply(400, #{}, <<"Missing body.">>, Req);
maybe_echo(_, _, Req) ->
    cowboy_req:reply(405, Req).

echo(undefined, Req) ->
    cowboy_req:reply(400, #{}, <<"Missing echo parameter.">>, Req);
echo(Echo, Req) ->
    cowboy_req:reply(200, #{
        <<"content-type">> => <<"text/plain; charset=utf-8">>
    }, Echo, Req).
```
`Method = cowboy_req:method(Req0),`はGETと同様、HTTPのメソッドを取得します。ここではPOSTがくる想定です。
`HasBody = cowboy_req:has_body(Req0),`でリクエストがBodyを持っているかどうかを判定しています。つまり、POSTリクエスト内に値があるかどうかをチェックしています。

```erlang:post_handler.erl
maybe_echo(<<"POST">>, true, Req0) ->
    {ok, PostValue, Req} = cowboy_req:read_urlencoded_body(Req0),
    Echo = proplists:get_value(<<"echo">>, PostValue),
    echo(Echo, Req);
maybe_echo(<<"POST">>, false, Req) ->
    cowboy_req:reply(400, #{}, <<"Missing body.">>, Req);
```
上記のもので値が取得できたかどうかをチェックしてします。第二引数に`cowboy_req:has_body(Req0)`の結果が入りますので、リクエストに値が入ってた場合はtrue、入ってなかった場合はfalseの`maybe_echo`が実行されます。
`cowboy_req:has_body(Req0)`がtrueの場合はBodyの値を取得します。
`Echo = proplists:get_value(<<"echo">>, PostValue),`でリクエスト内にechoのキーの値を取得します。

```erlang:post_handler.erl
echo(undefined, Req) ->
    cowboy_req:reply(400, #{}, <<"Missing echo parameter.">>, Req);
echo(Echo, Req) ->
    cowboy_req:reply(200, #{
        <<"content-type">> => <<"text/plain; charset=utf-8">>
    }, Echo, Req).
```

最後に、echoキーがある場合はレスポンスのBodyにechoキーの値をセットします。
echoキーがなかった場合はデフォルト値のundefinedが第一引数にくるので、400のステータスでエラーを返します。

## まとめ

GETとPOSTでの値が取得できるようになりました。これとDBとを組み合わせれば、DBから値の取得や追加、更新、削除処理とができるようになりますね。
ただ、POSTの値の取得の仕方がちょっと気にくわないかな、、、もう少しCowboyで取得を簡単にできれば嬉しいですね。