Chapter 3. Building Routes

In this chapter, we will be covering the following topics:

  • Defining the services supported by an application
  • The flow of requests received
  • Configuring routes
  • Handling assets

Introduction to Play routes

All the supported routes are specified within a single file: routes (by default). This makes it all the easier to figure out which one would be ideal.

The routes file is compiled and if any errors occur, the compilation fails.

However, the routes file is not a Scala object. So how does the compiler know what to do with the routes file? To find this out, let's perform the following steps:

  1. Let's create a project that displays a Hello, World! page. Now, define the index.scala.html home page as follows:
    <!DOCTYPE html>
    <html>
        <head>
            <title>Home</title>
        </head>
        <body>
            <h1>Hello, World!</h1>
        </body>
    </html>
  2. We will use this in our controller in this way:
    package controllers
    
    import play.api.mvc._
    object AppController extends Controller {
    
      def index = Action {
        Ok(views.html.index())
      }
    
    }
  3. All we need to view our page is an entry in the routes file:
    # Home page
    GET           /                    controllers.AppController.index
  4. Now compile the project. You will notice that a routes_routing.scala file is now available in the HelloWorld/target/scala-2.10/src_managed/main directory. The contents of the file will be similar to the following code snippet:
    import play.core._
    import play.core.Router._
    import play.core.j._
    
    import play.api.mvc._
    
    
    import Router.queryString
    
    object Routes extends Router.Routes {
    
    private var _prefix = "/"
    
    def setPrefix(prefix: String) {
      _prefix = prefix
      List[(String,Routes)]().foreach {
        case (p, router) => router.setPrefix(prefix + (if(prefix.endsWith("/")) "" else "/") + p)
      }
    }
    
    def prefix = _prefix
    
    lazy val defaultPrefix = { if(Routes.prefix.endsWith("/")) "" else "/" }
    
    
    // @LINE:5
    private[this] lazy val controllers_AppController_index0 = Route("GET", PathPattern(List(StaticPart(Routes.prefix))))
            
    def documentation = List(("""GET""", prefix,"""controllers.AppController.index""")).foldLeft(List.empty[(String,String,String)]) { (s,e) => e.asInstanceOf[Any] match {
      case r @ (_,_,_) => s :+ r.asInstanceOf[(String,String,String)]
      case l => s ++ l.asInstanceOf[List[(String,String,String)]]
    }}
          
    
    def routes:PartialFunction[RequestHeader,Handler] = {
    
    // @LINE:5
    case controllers_AppController_index0(params) => {
       call {
            invokeHandler(controllers.AppController.index, HandlerDef(this, "controllers.AppController", "index", Nil,"GET", """ Routes
     This file defines all application routes (Higher priority routes first)
     ~~~~
     Home page""", Routes.prefix + """"""))
       }
    }
            
    }
    
    }

So, Play generates Scala code from the routes file. A routes partial function is created using the routes file. The call method takes a function that returns a handler and defines the parameters to be passed to it. It is defined to handle 0 to 21 parameters.

The invokeHandler method is defined as follows:

def invokeHandler[T](call: => T, handler: HandlerDef)(implicit d: HandlerInvoker[T]): Handler = {
      d.call(call, handler) match {
        case javaAction: play.core.j.JavaAction => new play.core.j.JavaAction with RequestTaggingHandler {
          def invocation = javaAction.invocation
          val annotations = javaAction.annotations
          val parser = javaAction.annotations.parser
          def tagRequest(rh: RequestHeader) = doTagRequest(rh, handler)
        }
        case action: EssentialAction => new EssentialAction with RequestTaggingHandler {
          def apply(rh: RequestHeader) = action(rh)
          def tagRequest(rh: RequestHeader) = doTagRequest(rh, handler)
        }
        case ws @ WebSocket(f) => {
          WebSocket[ws.FRAMES_TYPE](rh => f(doTagRequest(rh, handler)))(ws.frameFormatter)
        }
        case handler => handler
      }

The result from d.call(call and handler) is matched to the predefined play.core.j.JavaAction, EssentialAction, and WebSocket types (all of which extend the handler trait) and their result is returned.

HandlerDef is a class, which is defined as follows:

case class HandlerDef(ref: AnyRef, routerPackage: String, controller: String, method: String, parameterTypes: Seq[Class[_]], verb: String, comments: String, path: String)

Automatic generation of routes_routing.scala

Let's have a look at how the routes_routing.scala file is generated.

Play utilizes the features provided by Simple Build Tool (SBT) to add a source generation task. A source generation task should generate sources in a subdirectory of sourceManaged and return a sequence of the files generated.

The SBT documentation can be found at http://www.scala-sbt.org/0.13.2/docs/Howto/generatefiles.html.

The usage can be seen in PlaySettings.scala, as follows:

sourceGenerators in Compile <+= (state, confDirectory, sourceManaged in Compile, routesImport, generateReverseRouter, generateRefReverseRouter, namespaceReverseRouter) map {     
  (s, cd, sm, ri, grr, grrr, nrr) => RouteFiles(s, Seq(cd), sm, ri, grr, grrr, nrr)    
},

RouteFiles is defined in the PlaySourceGenerators trait, which handles the Scala code generation for routes and views. Yes, even views are transformed to Scala code. For example, an index.template.scala file is available for the HelloWorld project at HelloWorld/target/scala-2.10/src_managed/main/views/html.

The definition for RouteFiles calls the RoutesCompiler.compile method and then returns the file paths where the source will be generated. The compile method parses the file using RouteFileParser and then generates the Scala code using the generateRouter method.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.11.247