Blog

spray-routingのディレクティブを活用しよう


初めまして、Lodeoの土井です。

今回はspray-routingのDirectiveに焦点を絞って、
Lodeoでどんな風に書いているか書きます。

spray-routing

spray-routingではリクエスト・レスポンスの処理を
次のような形式で処理していきます。

本家のMinimal Exampleです。

http://localhost:8080/helloにGetでアクセスすると、
<h1>Say hello to spray</h1>を返却するというサンプルです。
構文的には比較的分かりやすいのではないでしょうか。

ここで出てくるpath, get, completeがDirectiveと呼ばれ、
個々のリクエストを処理するために使います。

Directive

Directiveは次の4つの機能を提供します

  1. 内部Routeに渡す前のRequestContext※の変換
  2. RequestContext※に基いたフィルタリング
  3. RequestContext※から値を抽出
  4. リクエストの完了

※ RequestContext: ルーティング構造間で渡される軽量なオブジェクト

先の例で言えば、get, pathは2のフィルタリング、
completeは4のリクエストの完了といったところです。

Directiveを触ってみよう

さて、Directiveを使ってもう少し書き進めてみましょう。

ユーザ登録を想定して次のような処理を行います。

  1. userクッキーから作成者idを取得
  2. フォームパラメータからname, ageを取得
  3. nameが空文字列でなく、ageが0歳以上をバリデーション
  4. dtoに包んで返却
  5. ユーザを作成

ソースは次のようになります。

ネストが随分と深くなっていますね…。

ある程度はまとめて書くこともできるのですが、
条件やpathの記述が増えるほどに理解が困難になってしまいます。

Custom Directive登場

そこで、活躍するのがCustom Directiveです。
既存のDirectiveを組み合わせて1つのDirectiveとして使うことができます。
(もちろん、スクラッチから作成することも可能です)

上記の例のリクエストパラメータの抽出を
Custom Directiveに切り出すと次のようになります。

リクエストの処理は少しスッキリしたでしょうか?

一方でextractRequestの部分がとんでもないことになっていますね。
これでは先程の方がまだましだったようにさえ思えます。

Directive1

そこで活躍するのが1つの値を抽出するようなDirective(aliasに則り以降Directive1)です。
こちらにはmapとflatMapが提供されています。つまりforで書けます。

随分スッキリしました。

forのガード条件はそのままですと、
withFilterがないよとwarningがでます。

formFieldsの後にfilterでつなげてしまうか、
withFilterを実装してしまうのが良いかと思います。

Spec

Custom Directiveの部分のテストは下記のように書いています。

まさかのvar登場です。
こちらによりマーシャルというResponse用のデータ変換がされる前にデータを抽出しています。

(より良い方法があったらこっそり教えて下さい…)

まとめ

spray-routingのキーコンセプトの1つであるDirectiveについて、
Custom Directiveを使ってすっきりと書く方法を取り上げてみました。

リクエスト情報の展開にはunmarshaller等便利な機能が他にもありますが、
やや理解の難易度があがってしまう印象です。

そのため、Custom Directiveを適切に活用することで
既存の機能を活かしつつ、学習コストを抑えて実装を進めていけるのではないでしょうか。

Directiveについて本ブログを執筆の際に、
Binding Directives in Sprayという記事を紹介いただきました。

Directive1をscalazのMonadで定義しなおしたり、
Directive0をDirective1でリフティングしたりと目から鱗です。
興味のある方は参照してみてください。

それではみなさん、よいscalaライフを!

 

Copyright about logo
Copyright (c) 2002-2015 EPFL

Author

アバター
admin