暮らしているとリバースルーティングしたい日があります.例えばテンプレートの中で別のエンドポイントへのリンクを作りたいとき,直に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日
:いろいろある: