In this section, we look at how you can hook into Lift’s HTTP dispatching, which is the system that lets you return any subtype of LiftResponse. This could be a markup response, some kind of binary response such as a PDF, or pretty much anything else you can transport via HTTP. It’s fairly common practice in other web frameworks to use strings to set the headers, content type, and other response parameters, and then just flush this heap of strings to the browser. Lift takes a slightly different approach
Listing 8.2 Using AsIntunapply to add type checking to rewrites
175 Dispatching and web services
and provides an extensible, strongly typed mechanism for servicing content. This is known as the LiftResponse trait, and Lift has a raft of premade subtypes that give you, as the developer, a simple API for putting together structures that will be sent to the browser.
A common use case for dispatching is the creation of web services, and often those services follow a RESTful Resource Orientated Architecture (ROA) design.
Here you will see the high-level DSL that can assist you in creating basic web services and also cover the lower-level dispatch table, so you can implement your own dis- patchers and even conduct content negation to deliver the right content to the right client (you could deliver HTML content to a browser, but PDF content to a mobile eReader, for example).
NOTE Resource-oriented architecture (ROA) is discussed in the Wikipedia article: http://en.wikipedia.org/wiki/Resource_oriented_architecture. For more information about content negotiation, see the discussion in RFC 2616 at http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html.
Before we get started, it’s important to understand that Lift can service HTTP dis- patching in either stateful or stateless modes. There is a slight overhead in using state- ful dispatching, because it’s creating a session if one doesn’t already exist, but the key thing that should decide your implementation route is the use case in which it’s being applied. For example, if you need to use session variables to hold information over multiple requests, then stateless dispatching won’t work for you, but otherwise state- less dispatching should be good for most use cases. Either way, it’s a fairly minimal dif- ference, and the only change in implementation is the LiftRules property that you append to. For stateful dispatching, use
LiftRules.dispatch.append(...)
and for stateless dispatching, use
LiftRules.statelessDispatchTable.append(...)
All of the examples we cover here will work well irrespective of which dispatcher you wire things up to—it’s completely down to your use case.
In terms of its implementation, all HTTP dispatching in Lift is based upon two things: a request, or Req instance, and a response, which is a LiftResponse subtype.
As we just mentioned, there are a couple of different ways to interact with this process, and both have their respective merits. We cover more specific details of both Req and LiftResponse later in the chapter, but, for now, just understand that these are the input and output types used by Lift to service the process.
Let’s get on with looking at how to implement a small example using the high-level dispatching DSL.
8.3.1 Using the HTTP dispatch DSL
Before we get into anything more complicated, such as REST and content negotiation, let’s first check out how you can create simple services using Lift’s dispatch DSL. The next listing shows the most basic example.
import net.liftweb.http.rest.RestHelper object BasicDispatchUsage extends RestHelper { serve {
case "my" :: "sample" :: _ Get _ => <b>Static</b>
} }
This small example implements the RestHelper trait in a singleton object called BasicDispatchUsage. Inheriting from RestHelper delivers a whole set of functional- ity via special helper types and implicit conversions to keep your implementation code as light as possible. The main method inherited from the RestHelper is the serve method, which allows you to define case statements and use specialized helpers like Get to define your HTTP resources. As you can see, the case statement in listing 8.3 defines a URL using a List[String] (via the :: cons operator) that will match /my/
sample/** and serve it with the most basic XML response.
With this definition in place, the only thing that must also be done is to tell the appropriate dispatcher about this service definition object:
LiftRules.dispatch.append(BasicDispatchUsage)
This is rather nice, you’ll probably agree. All you need to do is reference the singleton object that extends RestHelper, and no other wiring is needed. Of course, this exam- ple is rather trivial, but it illustrates the point at hand rather nicely. At compile time Lift is able to determine the type of response conversion it should be doing. In this instance, it defines an XML literal in the code, which the Scala compiler sees as type scala.xml.Elem. It also sees that within the serve block there are implicit conversions in scope that can take a scala.xml.Elem and convert it to the correct LiftResponse subtype, which in this instance is a NodeResponse, because we’re serving XML.
Let’s just pause for a moment to review the LiftResponse mechanism that we’ve briefly touched on in the past couple of paragraphs. All dispatch methods in your Lift application, no matter how they’re constructed, must return a subtype of Lift- Response. Out of the box, Lift comes with a whole raft of response types for serving XML, JSON, Atom, and so on, and each response type typically takes care of any appro- priate options that need setting; nevertheless, you can override any behavior as you see fit.
In Lift’s dispatching system, anything that will ultimately return something to the browser must yield a subtype of LiftResponse. With this in mind, it’s useful to be aware of the wide variety of prebaked LiftResponse types available. Table 8.2 details
Listing 8.3 Using the HTTP dispatch DSL
177 Dispatching and web services
some of the types you’ll likely find yourself using fairly regularly, with examples of their usage. There are other types that fill the whole spectrum of HTTP response pos- sibilities—these are just the common usages. Also bear in mind that several of these response types have companion objects with overloaded apply methods, meaning that they can often be used with different arguments to control behavior in a more specific way or to provide a more simplistic API.
The syntax of the DSL relies quite heavily on Scala currying and implicit conversions to achieve its construction, so let’s look at a few examples that illustrate how the types are built up into the bare metal LiftRules.DispatchPF that all of Lift’s dispatching is based upon.
NOTE Broadly speaking, curried functions are those that have multiple argu- ment lists. Currying can be a fairly deep subject in and of itself, but for more in-depth information on currying, see Joshua D. Suereth’s Scala in Depth.
At a very base level, the dispatching mechanism boils down to this:
PartialFunction[Req, () => Box[LiftResponse]]
Table 8.2 Commonly used LiftResponse subtypes
Response type HTTP
code Usage
OkResponse 200 OkResponse()
JsonResponse 200 JsonResponse(JString
➥("This is JSON"))
PlainTextResponse 200 PlainTextResponse("Your message")
XmlResponse 200 XmlResponse(<sample />)
AcceptedResponse 202 AcceptedResponse()
PermRedirectResponse 301 PermRedirectResponse("/new/
➥path", req)
RedirectResponse 302 RedirectResponse("/new/path")
BadResponse 400 BadResponse()
UnauthorizedResponse 401 UnauthorizedResponse("Magical ➥Realm")
ForbiddenResponse 403 ForbiddenResponse("Noaccess ➥for you")
NotFoundResponse 404 NotFoundResponse()
InternalServerErrorResponse 500 InternalServerErrorResponse()
That is to say, a request comes in and your code will return a Function0 that has a boxed LiftResponse. The DSL makes this whole process easier for you by abstracting and providing sensible defaults via implicit conversions. For example, if you’re return- ing an XML response, the RestHelper has conversions that translate the scala .xml.Node into ()=>Box[Node].
There are many conversions to make working with the DSL as seamless as possible.
The following listing demonstrates four different statements that all create XML responses but make use of the RestHelper at different levels of abstraction.
object SecondDispatchUsage extends RestHelper { serve {
case "sample" :: "one" :: _ XmlGet _ => <b>Static</b>
case "sample" :: "two" :: Nil XmlGet _ => <b>Static</b>
case XmlGet("sample" :: "three" :: Nil, _) => <b>Static</b>
case Req("sample" :: "four" :: Nil, "xml", GetRequest) =>
<b>Static</b>
} }
Listing 8.4 defines four cases that illustrate the various routes you can use with the dis- patching DSL. The first shows a basic implementation that simply services /sample/
one/*/*.xml and is very similar to what was defined in listing 8.2, but as this version uses the XmlGet helper, so the incoming request must present an .xml extension and the content accept header must be text/xml. If these conditions aren’t met, Lift will return a 404 Not Found response to the calling client. This is an important difference from the definition in listing 8.2, which would serve XML irrespective of what the caller could actually accept.
The second case is nearly identical to the first, with a subtle difference; the request URL that’s defined is suffixed with Nil. The result of this is that the incoming URL must match /sample/two.xml exactly, or a 404 Not Found will be returned. To clarify this a little further, the URL in the first case was defined as follows:
"sample" :: "one" :: _
This trailing underscore essentially means any other pattern. Behind the scenes, this uses Scala pattern matching and a language feature called extractors to determine the resulting types, which in this instance are (List[String],Req). Extractors are a fairly complex topic, so we don’t dwell on them here—just understand how you can use the underscore to ignore parts of the pattern.
NOTE For more information on extractors, see the “Extractor Objects” page in the “Tour of Scala”: http://www.scala-lang.org/node/112.
The third case utilizes the exact same extractor, XmlGet, but you may find this usage a little easier to get your head around. Here you can see that the exact same syntax is in
Listing 8.4 Understanding dispatch DSL type hierarchy
179 Dispatching and web services
play for the URL, followed by another underscore to ignore the Req instance and sat- isfy the extractor pattern.
Finally, the fourth case involves the most verbose DSL usage. So far, we’ve been ignoring the Req instance with these patterns, in favor of the prebaked extractors sup- plied by the RestHelper trait, but here we’re using the raw Req instance directly to match on.
You may be wondering what the advantages are of accessing the Req directly, as compared to using the built-in extractors. There are a couple of considerations when choosing a route of implementation:
■ The Req instance has a whole bunch of helper methods. For example, if you wanted to service a particular request only if the incoming request came from an iPhone, you could put a guard on the match statement with req.isIPhone.
Depending upon your use case, these helper methods may be something you want to access, but otherwise the prebaked DSL extractors may be the ticket for you.
■ It’s a matter of preference. The DSL uses a fair amount of currying and what is known as infix operation pattern (http://www.scala-lang.org/docu/files/
ScalaReference.pdf, section 8.1.10); some people really like this, and others don’t. Ultimately, with choice comes personal preference.
The dispatch DSL gives you the tools to quickly create HTTP services, so let’s look at a more concrete example. In the next section, we make a base REST service.
8.3.2 Basic REST service
We’ve spent some time looking at the RestHelper dispatch DSL, and hopefully you can see how this can quickly assist you to construct HTTP services. Let’s create a short and very basic example with a couple of services to put that overview into prac- tice. This example will be based on the premise of a bookstore and creating a sim- ple read-only web service that you can use to get a list of stock and to query the stock by publisher.
To get started, let’s define some simple domain objects to model and store the stock list. To implement this simply, create a case class that can model a Book at the most sim- plistic level, and a singleton Bookshop object that contains the actual list of books. This listing shows the appropriate code.
case class Book(publisher: String, title: String) object Bookshop {
val stock = List(
Book("Bloomsbury", "Harry Potter and the Deathly Hallows"), Book("Bloomsbury", "Harry Potter and the Goblet of Fire"), Book("Manning", "Scala in Depth"),
Book("Manning", "Lift in Action") )
}
Listing 8.5 Bookstore domain classes
This is an extremely simple example, where Book is the model and the singleton Bookshop object holds a list of books, which represents the stock. Although this may seem trivial, this example is really about the dispatching, rather than a complex domain model or persistence system, so we just need some data to interact with. In practice, you’d likely be accessing a database or domain model to grab live data and serve that to the client.
Let’s get on with creating the basic service. The goal here is to create two services with the following HTTP resources:
GET - /bookshop/books.xml
GET - /bookshop/books/<publisher>.xml
Having earlier constructed an XML dispatching service in section 8.3.1 by using the RestHelper, you can probably guess how to implement the first resource in this task. The following listing shows an implementation for retrieving a list of all the books in stock.
import net.liftweb.http.rest.RestHelper
object BookshopHttpServiceBasic extends RestHelper { serve {
case "bookshop" :: "books" :: Nil XmlGet _ =>
<books>{Bookshop.stock.flatMap{b =>
<book publisher={b.publisher} title={b.title}/>}
}</books>
} }
Here again the RestHelper trait is used as a base for the implementation object. And again, the definition in the case statement should be pretty familiar from the preced- ing section. As this is to be an XML service, you simply implement the XmlGet type.
The <books> definition is the interesting part here, though, because you pull the list from the static Bookshop object and iterate through that list, creating XML elements.
The RestHelper brings an implicit conversion into scope that converts Elem to Lift’s NodeResponse. Simple enough.
Right now, this service can tell the caller what books are currently in stock, but it gives the client no way to filter or query the bookstore’s stock. To remedy this, let’s add another service that allows the caller to filter the stock by publisher. We need to refactor a touch so that we don’t duplicate the XML generation. The following listing shows the updated implementation.
object BookshopHttpServiceBasic extends RestHelper { serve {
case "bookshop" :: "books" :: Nil XmlGet _ =>
response(Bookshop.stock)
Listing 8.6 Retrieving a list of bookstore stock items
Listing 8.7 Adding the filtering resource
181 Dispatching and web services
case "bookshop" :: "books" :: publisher :: Nil XmlGet _ =>
response(Bookshop.stock.filter(
_.publisher equalsIgnoreCase publisher)) }
private def response(in: List[Book]) = <books>{in.flatMap(b =>
<book publisher={b.publisher} title={b.title}/>) }</books>
}
The List[Book]=>NodeSeq function has been moved into a separate method called response, which is detailed at B. More importantly, because the query by publisher URL requires some dynamic input (name of publisher), you specify this in the second case statement B. Notice how publisher is unquoted—it’s not a static String value but a placeholder for a variable value of type String for the value that makes up that URL. Handily, this placeholder can be used on the right side of the case statement, so you can simply pass the value directly into the creation of the filter predicate B, which removes all items from the list that don’t match the input.
To give a concrete example, if you accessed the URL,
GET - /bookshop/books/manning.xml
the response would be
<books>
<book title="Scala in Depth" publisher="Manning"/>
<book title="Lift in Action" publisher="Manning"/>
</books>
That’s all there is to it. This kind of implementation works well for most circumstances because it’s very simple, very straightforward, and quick to create.
DISPATCH GUARDS
Lift also has a rather nice mechanism that allows you to apply guards to your dispatch services by defining a partial function to check the incoming request. In short, this gives you the following style of syntax:
LiftRules.dispatch.append(onMondays guard BookshopService)
Parts of this declaration should look familiar, but it’s likely that the onMondays and guard appear to be somewhat undefined. In this context, onMondaysis a simplistic par- tial function that defines the parameters under which this service should operate.
Consider the definition of onMondays:
import net.liftweb.util.Helpers._
import java.util.Calendar
val onMondays: PartialFunction[Req, Unit] = { case _ if day(now) == Calendar.MONDAY =>
}
Notice that the partial function simply compares today’s day of the week to see if it’s Monday, with the result being that this will only evaluate to true when it’s actually
Response builder
B
Monday. As a result, when onMondays is applied to the service definition with the guard keyword, the service will only respond to requests on Mondays.
In practice, it would be more likely that you’d be checking for authentication or something else meaningful, but the same pattern holds true. In order to make this work, it’s necessary to have the Helpers._ import statement so that the required implicit conversions are available to the compiler.
Let’s throw another requirement into the mix: what if you needed to represent a single set of logic in multiple formats? You could perhaps use function passing, or maybe even some kind of workflow system to determine the output, but neither of those solutions feel very Scala-ish. Scala’s type system is incredibly powerful and can do quite amazing things. In the next section, we show you a rather advanced tech- nique for producing REST services in multiple representation formats while utilizing only a single implementation of a service method.
8.3.3 Advanced multiformat REST service
Before we dive into this section, we need to add a slight disclaimer: this section is advanced. It may be some time, if ever, before you feel comfortable with this kind of implementation, but we really want to show you some of the amazing things that can be done with Scala and Lift to yield a nice clean API.
This advanced example builds on the simple bookstore example from the previous section; the Bookshop and Book types are exactly the same. In this case, though, we add the requirement of multiple service representations, so we go through a very dif- ferent route of implementation.
Let’s step back and consider the problem. We only want to write the actual service method once, but we need to have multiple response representations. In a broad sense, this can be modeled as T =>LiftResponse. That is to say, an input type is con- verted to a LiftResponse. If this input type was the output from our single method definition, we could design an API that implicitly converts the single method output to a given LiftResponse. The API we’ll end up with is going to look like this:
case "bookshop" :: "books" :: Nil XmlGet _ => list[XmlResponse]
where the list method looks like this:
def list[R : Return[List[Book]]#As]: R = Return(Bookshop.stock)
You don’t need to worry about this right now. Just be aware that what we’re driving at here is to make an implementation that doesn’t care about the resulting output format.
The following listing shows a simple structure we can use to start modeling the input => output relationship this section started with.
trait ReturnAs[A, B]{
def as(a: A): B }
Listing 8.8 Modeling representational types