With my on-going effort to write more documentation and articles for Lift I’ve decided to write a walk through of Lifts dispatching and rewriting mechanisms.

Before we start this discussion, its important that you know (and understand) the importance of partial functions in scala. If your not familiar, check out this article – it should fill you in on all the particulars.

Application Boot

If your not familiar with Lift or are new, you should understand that anything of consequence that changes the application environment must in someway hook into the boot-up cycle – the default looks something like this:


class Boot {
  def boot {
    // where lift will look for snippets
    LiftRules.addToPackages("eu.getintheloop.tutorial")
  }
}

Ok, so not a great deal going on there… this is however a bare-bones lift boot class. In our case, we want to add a rewrite so that the following mappings take place:


/product/some-product-link

/** maps onto **/

./webapp/product-display.html

Its important to note that rewriting is just that, its not used for redirects or any other such activity – its 100% for URI translation and interpretation.

Adding a Rewrite

Pretty much all of application configuration within Lift is done through the LiftRules object – its the central place for configuration PFs and operation vars. So, how do we use it? Well, first add the following to the top of your Boot.scala file.


import _root_.net.liftweb.http.LiftRules

Next, add the following code below the snippet package definition:


LiftRules.rewrite.prepend(NamedPF("ProductExampleRewrite") {
  case RewriteRequest(
      ParsePath("product" :: product :: Nil, _, _,_), _, _) => 
    RewriteResponse(
      "product-display" :: Nil, Map("product" -> product)
  )
})

Rewrite matching in detail

So lets step through this part by part… we already know about the LiftRules object and what its for, and one of its var properties is “rewrite” – a RuleSeq – and has the notion of both prepending values, and appending them. In this case, we prepend this rewrite rule, meaning that it will execute before any other rewrite rules previously added to the rewrite var.

List[String] is used to match the incoming path – lets take a closer look at our request object and the parameters we pass. From the scaladocs, we can see that RewriteRequest object has the following signiture:


/**
 * options for RewriteRequest
 */
case class RewriteRequest(
  val path : ParsePath, 
  val requestType : RequestType, 
  val httpRequest : HttpServletRequest
) 

/**
 * options for ParsePath
 */
case class ParsePath(
  val partPath : List[String], 
  val suffix : String, 
  val absolute : Boolean, 
  val endSlash : Boolean
) 

/**
 * options for requestType object
 */

GetRequest
PostRequest
PutRequest
DeleteRequest

The first argument in the RewriteRequest is a ParsePath, this is one of the primary URI matching mechanisms and enables you to define detailed paramaters on which request to match and which to ignore. Lets take a look at some various RewriteRequest examples:


/**
 * example 1.
 * matches: GET /some/demo/path
 */

RewriteRequest(
  ParsePath("some" :: "demo" :: "path" :: Nil, "", true, false), 
  GetRequest, _
)

/**
 * example 2.
 * matches: PUT /some/image.png
 */

RewriteRequest(
  ParsePath("some" :: "image" :: Nil, "png", true, false), 
  PutRequest, _
)

/**
 * example 3.
 * matches: * /some/demo/
 */

RewriteRequest(
  ParsePath("some" :: "demo" :: Nil, "", true, true), _, _
)

/**
 * example 4.
 * matches: GET /product/<item>/details
 */

RewriteRequest(
  ParsePath("product" :: item :: "details" :: Nil, "", true, true), 
    GetRequest, _
)

So far we’ve seen how to configure the incoming request – to complete the picture, lets now take our request handling and couple that up with matching the response and mapping parameters so they are then available in the rest of our code via S.param.

The RewriteRequest object has several overload apply methods to keep the verbosity of your code to a minimum – namely, these overloads are:


def apply(path : ParsePath, params : Map[String, String])
def apply(path : List[String])
def apply(path : List[String], suffix : String)
def apply(path : List[String], params : Map[String, String])

We can see that this gives us a bunch of flexibility depending on our needs – straight rewrite, rewrite with params etc etc. Lets take a look at some examples:


/**
 * rewrite to a html file in webapp/show.html with no params
 */

RewriteResponse("show" :: Nil)

/**
 * rewrite to a html file in webapp/show.html with 
 * a paramater called "product" from
 * a parameter placeholder called product
 */

RewriteResponse("show" :: Nil, Map("product" -> product))

/**
 * rewrite to a html file in webapp/example.pdf with 
 * no params, but a pdf suffix
 */

RewriteResponse("example" :: Nil, "pdf")

We have now looked at both requests and response – so moving back to our original example I hope you can see how it now works. One thing we have no explored is how to access the various parameters you might configure in your snippets / other application code. The answer my friends, is simple:


S.param("product").openOr("fail over product")

By now I presume you are a rewriting guru!... that might be a bit overboard, but I hope you found this guide informative and useful.

9 Responses to “URL Rewriting with the Lift Framework”

  1. Ben James Says:

    Thanks for this, it’s really helped me understand how URL rewriting works. It’s something glaringly lacking from the “starting with lift” tutorial.

  2. Tim Perrett Says:

    Thanks for the feedback Ben – glad you found it useful.

  3. Bill Bartmann-_ Says:

    Cool site, love the info.

  4. Grégory Desvaux Says:

    Interesting and very clear. Thanks.

  5. Magnus Says:

    I agree: A good article. A little bit easier to understand than the same sections in The Definitive guide to Lift.

  6. Randin Says:

    Nice blog, you might consider adding/linking this to the Liftweb wiki.

  7. Randin Says:

    Haha, opps, that will teach me to comment :)

  8. Spyware Blockers Says:

    Just wanted to say thank you! for all the great info found on your site, even helped me with my job recently :) keep it up!

  9. José Leal Says:

    Nice. Thanks a lot, keep up with the good work

Leave a Reply