Action composition in the Play Framework

The Play framework is a pretty popular web application framework that you can use to build web applications in Scala or Java. One of the building block is the Action, which is responsible for converting an HTTP request to a result.

Controller classes will contain Action generators, that are used to handle the HTTP request for a specific HTTP call, that is defined in the routes file.

There are cases, where you want to have generic functionality in your Actions. The Play framework supports Action composition to handle this scenario. We will explore the options in the Scala API.

The first thing you might want to know is that the Action object is not only a companion object of Action[T], but also an object of type ActionBuilder. From ActionBuilder, the object Action gets its many useful apply methods to construct an Action, like:

Simply extend actionBuilder

If you want to reuse generic logic in an Action, you can extend ActionBuilder, like this:

As you sort of create your own companion object, you keep all the useful apply methods, since this object is still an ActionBuilder. Therefore this still works:

Move away from inheritance

However, if we have multiple generic functions we want to compose in various ways, this method will lead to duplicate code. The solution we saw depended on inheritance and we would like to use composition to create higher abstractions. Therefore you can compose Actions. One option is to reuse code by wrapping an Action[T] (not the ActionBuilder but the real Action), like this:

While using the class based example, this custom Action can now be mixed in a custom ActionBuilder, by using the composeAction method:

CustomAction can be used as before:

But in a more generic way, because you have defined your own Custom[A], you can also do this:

Just to be clear about this, Custom is a subtype of a real Action: Action[A], whereas Action is actually an apply method on ActionBuilder that returns an Action[A].

To Composition with ActionFunctions

This is all very nice, but when you want to build pipelines of functionality on the request and response, this is still not very easy. You either have to build multiple hierarchies of Action builders or use deeply nested methods. Therefore the Play framework has the concept of an ActionFunction. This allows you to create pipelines of Actions in a composable way.

You can think of it as a function on the Request, parameterized by the specific type of request it takes in and the type of request that will be used in the next layer. ActionFunction[-R[<em>], +P[</em>]] has an invoke method like this:

So you can see that it accepts a request expressed by type parameter R but uses a request type parameterized by P in the block.

There are a few implementations of ActionFunction in the Play framework that cover some common use cases. ActionTransformer can transform the Request[A]. ActionFilter can be used to filter requests, for example for authentication and authorization. They are both an ActionRefiner, which is a more generic way to refine the request.

It turns out that ActionBuilder actually has multiple responsibilities. At the one hand, it serves as a factory for Action[T], but at the other hand, it is also an ActionFunction. And as it is the only way to construct Action[T], it is also the most common use case of an ActionFunction.

To be able to compose functionality, there are the following methods defined on ActionFunction. These can be used in the usual way. andThen first performs the ActionFunction and then moves on to the next. While the compose methods work the other way around.

Now we see that we can chain as many ActionFunctions and ActionBuilders as we like and build a pipeline of functions that need to be executed on the request and response.

An example

I will show you the outline of a use case I had and how I applied these Action composition tools to implement it. The main goal was to add some meta data to the Request[A]. I created a RequestWithContext[A] that has a field context: Map[RequestContextKey, Any].

I created ActionTransformers like:

Then I had an

to create a RequestWithContext[A] from a normal Request[A]. I also had an  ActionFunction that logs request and response. Then I could build an ApiAction ActionBuilder like so:

Which I am able to use in a regular way, like:

Some remarks

Most of what I have written in this blog post is actually explained in the Play framework documentation, here and here. However, I found it worthwhile for my own understanding to write things down in my own words as some of the nuances were not clear to me from the original documentation. Also, this post only discusses the Scala API. For Java things are somewhat different.

I hope this description of the concepts of Action composition in the Play framework help you to better understand what is going on.

Close Menu