暮らしているとリバースルーティングしたい日があります.例えばテンプレートの中で別のエンドポイントへのリンクを作りたいとき,直にURLを書きたくないですよね.修正に弱いし. いまどきのWebアプリケーションはエンドポイントを渡すと勝手にURLが作られるようになっているはずです.
そこでリバースルーティングによってme.url(me.someEndpoint)
みたいにするとURLがもらえる仕組みをscalatra + twirlで作ります.
TL;DR
- サーブレットに
UrlGeneratorSupport
をmix-inする - テンプレートをレンダするとき,twirlにimplicit valで呼び出し元のサーブレットと,暗黙に生えている
javax.servlet.http.HttpServletRequest
とを渡す - テンプレート側でもこれをimplicitに受け取る
環境
val ScalatraVersion = "2.7.0"
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.4.2")
リバースルーティング
リバースルーティングする方法はマニュアルに書いてあります.
要約すると,エンドポイントを名前付きで宣言しておき,url(エンドポイント)
とするとURLがもらえるとのことです.マニュアルのコードを引用します.
class MyApp extends ScalatraServlet with UrlGeneratorSupport { // When you create a route, you can save it as a variable val viewUser = get("/user/:id") { // your user action would go here } post("/user/new") { // url method provided by UrlGeneratorSupport. Pass it the route // and the params. redirect(url(viewUser, "id" -> newUser.id)) } }
これをtwirlでやるために工夫します.マニュアルに登場するurl
メソッドはUrlGeneratorSupport
をmix-inしたサーブレットに生えるので,直接テンプレートから呼び出せません.そこでサーブレット自体をテンプレートに渡します.マニュアルのコードを改変して説明します.
class MyApp extends ScalatraServlet with UrlGeneratorSupport { implicit val servlet = this // implicit化する val viewUser = get("/user/:id") { views.html.foo() } }
implicit
を使ってサーブレット自体がテンプレートに渡るようにしました.Twirl側でもこれを受け取れるようにします.
// views.html.foo
@()(impilcit servlet: MyApp)
@servlet.url(servlet.viewUser)
するとtwirlテンプレートでurl
が呼び出せるように……なりません.
could not find implicit value for parameter req: javax.servlet.http.HttpServletRequest
url
メソッドはどうやらリクエストオブジェクトを暗黙に要求するようです.これもimplicit
を使います.とはいえ元々からreq
はimplicit
なので,テンプレートで要求するだけでよいです.
@()(impilcit servlet: MyApp, req: javax.servlet.http.HttpServletRequest) @servlet.url(servlet.viewUser)
型表記が長ったらしくなったので,twirlテンプレートでは自動的にjavax.servlet.http.HttpServletRequest
がimportされるようにします.これはsbtの設定で可能です.
// build.sbt TwirlKeys.templateImports += "javax.servlet.http.HttpServletRequest"
するとこう書けます.
@()(impilcit servlet: MyApp, req: HttpServletRequest) @servlet.url(servlet.viewUser)
めでたし
まとめ
リバースルーティングを若干ムリヤリ達成できました.ところでscalatraチームはテンプレートエンジンとしてscalateをおすすめしています.
こっちを使う場合は特に何もせずにurl
が呼べるようです.こっちで良さそう.よかったですね.
追記
Scalate、コンパイラの内部に依存しすぎててメンテナンスが大変なのでtwirl使ったほうがいいのです…
— magnoliak🍧 (@magnolia_k_) 2020年8月2日
:いろいろある: