Configuring route definitions

Play supports both static and dynamic request paths. If a request path cannot be matched to any of the defined routes, an Action not found error is thrown at runtime, which is rendered using the devNotFound.scala.html default template.

Dynamic paths

Dynamic paths are those that can be used for multiple requests and they may or may not result in a similar response. For example, the default assets path is a path used to serve resources:

GET           /assets/*file        controllers.Assets.at(path="/public", file)

The * symbol indicates that anything following /assets/ until a space is found is the value of the file variable.

Let's look at another way to make the path dynamic when we need to add one or more variables. For example, to get a user's details by userId we use the following code:

GET           /api/user/:userId    controllers.UserController.getUser(userId)

By default, all the variables that occur in a path are of the String type. If a conversion is required, the type should be mentioned explicitly. So, if the getUser method takes a long parameter, we would just need to specify it in this way:

GET           /api/user/:userId    controllers.UserController.getUser(userId:Long)

Using the":" prefix for userId means that the userId variable is exactly one URI part. The assets path uses any suffix indicator as the relative file path, which is required to access any file.

It is not necessary that a path should end with a variable; for example, /api/user/:userId/album can be used as a valid path to fetch all the albums stored by a user.

Multiple variables can also be used in the same path. Supposing we wished to fetch a specific album, we could use /api/user/:userId/album/:albumId.

Note

The maximum number of variables we can specify in a path is 21, since this is the maximum that the call method used in routes_routing.scala is defined to handle. Also, the request path becomes complicated and ends up with too many variables. In general, keeping the number of such parameters to less than five is a good practice.

Play also supports using regular expressions to match the variables. For example, assume that we want to restrict a string variable to consisting of only letters, such as a region code; in this case, our route can be defined as follows:

GET           /api/region/$regionId<[a-zA-Z]{2}>/user       controllers.UserController.getUserByRegion(regionId)

Notice that when we specify a regular expression for the variable in the route, it is prefixed with a $ symbol instead of the : symbol while defining the route.

The preceding route definition restricts the request by a regular expression. For example:

  • /api/region/IN/user is a valid path
  • /api/region/CABT/user and /api/region/99/user are invalid

Note

The order of preference to a route is defined by its position in the routes file. The router returns the first matching route for a given path. If the same request type and route are mapped for two different actions, the compiler does not throw an error or warning. Some IDEs indicate when duplicate route definitions occur, but it is completely the developer's responsibility to ensure that such cases do not occur.

This table summarizes the different ways of defining a dynamic path:

Sr.no.

Purpose

Special characters

Example usage(s)

1

URI path separator is part of the variable

*

/assets/*file

2

Single or multiple variables

:

/api/user/:userId

/api/user/:userId/album

/api/user/:userId/album/:albumId

3

Regular expression pattern for variables

$

/api/region/$regionId<[a-zA-Z]{2}>/user

Static paths

Static request paths are fixed and constant. They cannot support arguments in the request path. All the data required for such requests should be sent through request parameters or request bodies. For example, the actions used for signing in or signing out are given as follows:

GET           /login               controllers.Application.login

So does Play search for specific characters to identify the kind of path?

Yes, the special characters are used by RoutesFileParser to recognize whether a path is static or dynamic. The paths are defined as follows:

    def singleComponentPathPart: Parser[DynamicPart] = (":" ~> identifier) ^^ {
      case name => DynamicPart(name, """[^/]+""", encode = true)
    }

    def multipleComponentsPathPart: Parser[DynamicPart] = ("*" ~> identifier) ^^ {
      case name => DynamicPart(name, """.+""", encode = false)
    }

    def regexComponentPathPart: Parser[DynamicPart] = "$" ~> identifier ~ ("<" ~> (not(">") ~> """[^s]""".r +) <~ ">" ^^ { case c => c.mkString }) ^^ {
      case name ~ regex => DynamicPart(name, regex, encode = false)
    }

    def staticPathPart: Parser[StaticPart] = (not(":") ~> not("*") ~> not("$") ~> """[^s]""".r +) ^^ {
      case chars => StaticPart(chars.mkString)
    }

In the methods used to identify a path, the ~>, not, and ^^ methods are from scala.util.parsing.combinator.{Parser, RegexParsers}. DynamicPart and StaticPart are defined with the intention of capturing the parts of a URL, so that it's simpler to pass values to a corresponding action. They are defined as follows:

trait PathPart

case class DynamicPart(name: String, constraint: String, encode: Boolean) extends PathPart with Positional {
  override def toString = """DynamicPart("""" + name + "", """" + constraint + """"," + encode + ")" //"
}

case class StaticPart(value: String) extends PathPart {
  override def toString = """StaticPart("""" + value + """")"""
}

case class PathPattern(parts: Seq[PathPart]) {
  def has(key: String): Boolean = parts.exists {
    case DynamicPart(name, _, _) if name == key => true
    case _ => false
  }

  override def toString = parts.map {
    case DynamicPart(name, constraint, encode) => "$" + name + "<" + constraint + ">"
    case StaticPart(path) => path
  }.mkString

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

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