Better router for Play Framework 2.x
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
project
src
.gitignore
.travis.yml
CHANGELOG.md
LICENSE
README.md
build.sbt

README.md

Better router for Play Framework 2.0

The "why" and "how" - http://codetunes.com/2012/scala-dsl-tutorial-writing-web-framework-router

Installation

Add play-navigator to your project/Build.scala file

val appDependencies = Seq(
  "eu.teamon" %% "play-navigator" % "0.5.0"
)

val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
  resolvers += "teamon.eu repo" at "http://repo.teamon.eu"
)

From 0.5.0 only Play >= 2.2.0 is supported. Use 0.4.0 if you need to support Play < 2.2.0.

Delete conf/routes file (optional since 0.3.0, you can use both)

Create new file PROJECT_ROOT/app/controllers/nav.scala:

package controllers

import play.navigator._

object nav extends PlayNavigator {
    // Your routes definition (see below)
}

Your app/Global.scala should look like this

import play.api._
import play.api.mvc._

object Global extends GlobalSettings {

  override def onRouteRequest(request: RequestHeader) = {
    controllers.nav.onRouteRequest(request) // handle requests with play-navigator

    // optionally you can fallback to standard Play routes with
    // controllers.nav.onRouteRequest(request) orElse super.onRouteRequest(request)
  }

  override def onHandlerNotFound(request: RequestHeader) = {
    // display 404 page with routes documentation
    val result = controllers.nav.onHandlerNotFound(request)
    // Play < 2.2.0
    result 
    // Play >= 2.2.0
    Future.successful(result)
  }

}

Routes definition

// Basic. Remember to add '_' after parameterless functions.
val home  = GET   on root       to Application.index _
val index = GET   on "index"    to Application.index _
val about = GET   on "about"    to Application.about _
val foo   = POST  on "foo"      to Application.about _
val show  = GET   on "show" / * to Application.show
val ws    = GET   on "ws"       to Application.ws _
val bar   = GET   on "bar" / * / * / "blah" / * to Application.bar

// Catches /long/a/b/c/.../z
var long  = GET   on "long" / ** to Application.long

// Require extension: /ext/{param}.{ext}
GET on "ext" / * as "json" to Application.extJson
GET on "ext" / * as "xml"  to Application.extXml

// REST routes
val todos = resources("todos", Todos)

// Namespace ...
namespace("api"){
  namespace("v1"){
    GET on "index" to Application.index _
  }
}

// ... or with reverse routing support
val api = new Namespace("api"){
  val v2 = new Namespace("v2"){
    val about = GET on "about" to Application.about _
  }
}

// and back to top-level namespace
GET   on "showalt" / * to Application.show

// redirect
GET on "redirect-me" to redirect("http://google.com")

// assets
val assets = GET on "assets" / ** to { s: String => Assets.at(path="/public", s) }

Application and Todos controllers used in example

// app/controllers/Application.scala
package controllers

import play.api.mvc._

object Application extends Controller {

  def index(): Action[_] = Action {
    Ok("Applcation.index => " + routes.index())
  }

  def about(): Action[_] = Action {
    Ok("Application.about => " + routes.about() + " or " + routes.api.v2.about())
  }

  def show(id: Int): Action[_] = Action {
    Ok("Application.show(%d) => %s" format (id, routes.show(id)))
  }

  def bar(f: Float, b: Boolean, s: String): Action[_] = Action {
    Ok("Application.bar(%f, %b, %s) => %s" format (f, b, s, routes.bar(f,b,s)))
  }

  def long(path: String) = Action {
    Ok("Application.long(%s)" format path)
  }

  def extJson(id: Int) = Action { Ok("Application.extJson(%d)" format id) }
  def extXml(id: String) = Action { Ok("Application.extXml(%s)" format id) }

  import play.api.libs.iteratee._

  def ws() = WebSocket.using[String] { request =>
    val in = Iteratee.foreach[String](println).mapDone { _ =>
      println("Disconnected")
    }

    val out = Enumerator("Hello!")

    (in, out)
  }
}


// app/controllers/Todos.scala
package controllers

import play.api._
import play.api.mvc._
import navigator._

object Todos extends Controller with PlayResourcesController[Int] {
  def index() = Action { Ok("Todos.index => %s" format nav.todos.index()) }
  def `new`() = Action { Ok("Todos.new => %s" format nav.todos.`new`()) }
  def create() = Action { Ok("Todos.create => %s" format nav.todos.create()) }
  def show(id: Int) = Action { Ok("Todos.show(%d) => %s" format (id, nav.todos.show(id))) }
  def edit(id: Int) = Action { Ok("Todos.edit(%d) => %s" format (id, nav.todos.edit(id))) }
  def update(id: Int) = Action { Ok("Todos.update(%d) => %s" format (id, nav.todos.update(id))) }
  def delete(id: Int) = Action { Ok("Todos.delete(%d) => %s" format (id, nav.todos.delete(id))) }
}

Mountable routers

case class FirstModule(parent: PlayNavigator) extends PlayModule(parent) with Controller {
  val home = GET on root to first.Application.index _
  val foobar = GET on "foo" / "bar" / * to first.Application.foo
}

case class SecondModule(parent: PlayNavigator) extends PlayModule(parent) with Controller {
  val home = GET on root to (() => second.Application.index)
  val foobar = GET on "foo" / "bar" / * to second.Application.foo
}

// Main router
object nav extends PlayNavigator {
    val first = "first" --> FirstModule
    val second = "second" / "module" --> SecondModule
}

Generated routes:

/first
/first/foo/bar/*
/second/module
/second/module/foo/bar/*

and reverse routing:

nav.first.home() // => "/first"
nav.first.foo(3) // => "/first/foo/bar/3"
nav.second.home() // => "/second/module"
nav.second.foo(3) // => "/second/module/foo/bar/3"