この記事は F# Advent Calendar 2015 の 7 日目です。
Suave は F# のウェブアプリケーションフレームワークです。 .NET Framework や Mono の動作する環境上で動きます。
最近 Suave を使って API を作る機会があったので,その方法について簡単に紹介します。
Suave とその他パッケージのインストール
Suave は NuGet からインストール可能です。以下のパッケージを導入します。
- Suave
Suave には単体テスト用のパッケージも公式で配布されています。今回はテスティングフレームワークとして Persimmon を例に挙げます。
- Suave.Testing
- Persimmon
REST API を作る
REST API では "/path/{id}/{property}" のような形式の URL に対して特定の HTTP メソッドでリクエストを送ります。つまりサーバー側では以下のことができなければなりません。
- パスからパラメーターを取得
- HTTP メソッドのフィルタリング
パスからパラメーターを取得
パスからパラメーターを取得するには, pathScan
関数を利用します。ちなみにパラメーターがない場合は path
関数を利用します。
pathScan
関数は, printf
関数と類似した形式で,型安全にパスからパラメーターを取得することができます。パラメーターを利用した処理は,取得したパラメーターを引数として WebPart を返すような関数として定義します。具体的には以下のような形式になります。
pathScan "/path/%d/%s" (fun (id, property) -> // WebPart を返す )
何の説明もなく WebPart という言葉が出てきました。 Suave では WebPart という型を >>=
というパイプで接続して処理を連結します。
webPart_1 >>= webPart_2 >>= ... >>= webPart_n
詳しい説明は割愛しますが, WebPart の型は HttpContext -> Async<HttpContext option>
だと言えば,わかる人もいると思います。ざっくり言えば,定義した条件マッチした場合に非同期処理を行い,そうでなければ失敗するような関数です。
HTTP メソッドのフィルタリング
Suave には HTTP メソッドと同名の WebPart があらかじめ定義されています。 GET メソッドに対して GET
関数, POST メソッドに対して POST
関数といった感じです。
エンドポイントの作成
上記を組み合わせて,以下のようにエンドポイントを定義できます。
open Suave.Http.Applicatives open Suave.Http.Writers let endpoint = GET >>= setMimeType "application/json" >>= request (fun req -> pathScan "/path/%d/%s" (fun (id, property) -> // 実際の処理 ))
API の作成
実際の API ではエンドポイントが複数あるので,それを列挙する必要があります。それには choose
関数を利用します。
open Suave.Http let api = choose [ endpoint endpoint_2 // 中略 endpoint_n ]
API サーバーを起動する
以下のようにして API サーバーを起動します。
open System.Net open Suave.Types open Suave.Web let config = { defaultConfig with bindings = [HttpBinding.mk HTTP IPAddress.Any 80us] } startWebServer config api
実際には 2 つ目の引数の設定を編集して,ロギングやエラーハンドリングといった処理を指定してやる必要があるでしょう。
単体テストを作成する
サーバーを起動する startWebServer
のシグネチャは SuaveConfig -> WebPart -> unit
です。 unit の代わりに何かテストの結果を返してくれるような関数があれば,テストができるのではないかと考えられます。実際には runWith
という,まさに SuaveConfig -> WebPart -> SuaveTestCtx
という,テスト結果を返す関数が定義されています。
SuaveTestCtx は名前の通りコンテキストなので,そこからレスポンスを取り出すような関数が必要になります。 reqResp
関数を利用することで,リクエストを投げてレスポンスを取得するという一連の流れをテストすることができます。実際には reqResp
のパラメーターをあらかじめいくつか設定してレスポンス文字列を返す req
関数を使う場合が多いかもしれません。
以下のような形で, Suave のテストを行うことが可能です。
open Suave.Testing open Suave.Types open Persimmon /// GET リクエストのレスポンス文字列を取得する。 let get path = runWith config api |> req HttpMethod.GET path None let ``/path/1/foo にリクエストを投げて JSON レスポンスを得る`` = test { let content = get "/path/1/foo" do! assertEquals content """{"propertyName":"foo","propertyValue":"bar"}""" }
まとめ
Suave を利用すると簡単に REST API が作れるというお話でした。
ちなみにこの Suave を手本にして R で API を作れるようにしたのが shadowy というライブラリーです。まだ機能的に足りていないところが多いので,誰か手伝ってください。