Keep controllers clean with custom action builders

When writing controllers, there are often elements that are needed over and over again. Maybe you always seem to be getting the same objects from a repository, or you want to do authentication. Repeating simple operations like these leave controller actions bloated, and hard to read. Play Framework offers a simple and powerful way to abstract these repeated elements.

Anatomy of an action

In Play, as with most web frameworks, an action is a method of a controller that turns a request into a response. More specifically though, the Play framework contains an ActionBuilder trait which, among other things, gives a method signature for invokeBlock:

This trait is used to create the Action object, with a trivial implementation of invokeBlock:

This means that in the standard implementation of an action method, the block you provide is executed unconditionally.

Extending the action

It is possible to replace this rather boring default Action object with your own. By implementing the ActionBuilder trait and only conditionally executing the provided block, you can quickly implement a common action requirement. In this example I’ll use authentication checking.

Let’s assume we have a user service that will give us a user when we give a valid token:

And that this service is being injected into our controller:

So we can see that this controller has only 2 actions, but a lot of duplication. In both actions it is attempting to get the user, and if the user service returns a None, it is issuing a 401 error.

The first thing we can do to make this neater is to move that logic into a custom Action object:

Note that as the AuthAction object uses the userService, it needs to be placed inside the controller definition.

The existing actions can now be rewritten to use this new AuthAction object, which removes the duplication. Let’s do the first action method first:

Much better! This is now a clean action that is very easy to read and understand. Let\s try for the second action:

Hmm… that’s not a big improvement. The problem here is that the user object is needed in the action block, and it is not being passed in from the AuthAction object.

Extending the request

The solution to getting objects from the AuthAction object into the action block is to extend the request itself. Since the request is already being passed into the block, this approach involves the least refactoring, especially in situations where there are many controller actions.

Play Framework provides a class called WrappedRequest which extends Request and accepts a Request, effectively allowing for decoration of a request. Defining a new Request that supports a User object is made simple:

A few minor changes to the AuthAction object ensure that the UserRequest is passed into the block:

All that’s changed there is that the type given to the ActionBuilder trait specifies the new Request type, and that the block is called with an instance of UserRequest, which is decorating the original request.

Finally, we can tidy up the second controller action by using the user property of the new UserRequest:

Conclusion

By using custom ActionBuilders and decorating requests it is possible to greatly reduce duplication and keep controller actions slim. This allows for more readable code that is much easier to maintain.

Leave a Reply

Your email address will not be published. Required fields are marked *