Playing with Play. REST service

After a short introduction to the Play Framework I made earlier, there is the time to code something. Today I am going to write a simple REST service. There will be some tests of it as well. At the end, I will run it so I can consume it with curl or httpie.

Ok, I should start with some domain I guess. After searching on the Internet for the enormous count of hours I have found it! I decided to base my play around the timeless example of… todos list! Yay! Yes, I know. Complication level of this domain can only be compared with the complexity of the CERN’s Hadron Collider machine. But seriously, I chose this one, so I can focus on technical details of implementing a service and keep things simple as much as possible. 😉

The key domain class is TodoItem. To make it useful, I set the following properties of it: identifier, name, due date and status (with two possible values: open and done). The service, managing todo items, is a very simple CRUD implementation. There are methods to create, edit, remove and read a single item and list all of them. And additionally, two actions allowing to change a status of a given item.

Let’s do some code. HTTP requests in Play are handled by actions. Actions are grouped in controllers. So before creating any action we actually need a controller class. Naming it TodoController sounds good.


public class TodoController extends Controller {
...
}

Every controller in Play Framework extends abstract play.mvc.Controller class. What does this base class give? The access to current HTTP context, request and response among other functions. Thanks to this we have the easy way to process a received request and create some response. The class extends other useful class, which is play.mvc.Result. This one serves a bunch of handy factory methods creating various responses with a given HTTP status code and, if applicable, a body content. The other thing is a controller class has to be public. This is required when it comes to routes mapping.

The controller class is empty. This is the time for the first action to be implemented. Here is how we can create a new item on a todo list:


    @BodyParser.Of(BodyParser.Json.class)
public Response createTodoItem() {
TodoItemRequest req = getRequest();
TodoItemId itemId = store(TodoItem.fromRequest(req));
LOGGER.info("Item " + req.getName() + " has been created with id " + itemId.getId());
return created(toJson(itemId));
}

Starting from the top we have the @BodyParser.Of(BodyParser.Json.class) annotation. It ensures a request’s body is a valid JSON format, so this is just syntactic check. If you are interested what other data formats are supported, look at the content of play.mvc.BodyParser class.

Now, it would be good to instantiate the body’s content – I assume this is JSON data with two fields: name and dueDate. How can we do this? There is Controller.request() method that gives us access to, among others, headers and body of the received request. Handling of JSON in Play, on the other hand, comes from play-json plugin that is using Jackson library under the hood. The plugin contains a neat utility – Json class – that contains useful methods to handle the format. With the class, it is quite simple to create a domain object with it. You need two function calls to do this:

  1. parse request body with request().body().asJson() – that creates JsonNode object holding the data,
  2. call Json.fromJson(JsonNode, Class<t>) to create a domain object of type T.

Next, from the request object I create an item and then save it in a repository. It is not important what this repository is. At the moment I use simple HashMap but in one of the next post I will switch to some database repository.

As the last thing in this method, a response is returned. The response created by calling play.mvc.Results.created() method has HTTP status code set to 201. As an argument, it takes the identifier in JSON format. toJson(...) method, coming from Json class, creates a JSON structure based on fields of provided object:


    return created(toJson(itemId));

There is no magic when it comes to logging in Play application. It is based on the play.Logger class which provides all needed logging API. It wraps a logging library from the classpath. The default library used one is Logback. More details about logging you can find here.

We have the action defined however it is not exposed to the outside world as an HTTP endpoint. In Play Framework, routes file from conf directory contains mappings between HTTP calls and application actions. The creation of a todo item can be mapped with the following line in the file:


    POST /todos pl.devthoughts.controllers.TodoController.addItem()

Ok, so far so good. We have the first action and HTTP endpoint exposed. But there is one thing missing actually. Yes, I have written nothing about testing a controller. While there are various ways how this can be achieved, I will focus on routes testing. This is very similar to using MockMvc from Spring Framework.

Here is how a test of an item creation can look like:


public class RoutesTest extends WithApplication {

@Test
public void should_create_single_todo_item() {
Result creationResult = route(new RequestBuilder()
.method(POST)
.uri("/todos")
.bodyJson(itemData("Do something", "2016-12-04"))
);

assertThat(creationResult.status()).isEqualTo(CREATED);
DocumentContext creationCtx = JsonPath.parse(contentAsString(creationResult));
assertThat(creationCtx).jsonPathAsString("$.id").isNotEmpty();

Result findingResult = route(new RequestBuilder()
.method(GET)
.uri("/todos/" + itemId(creationResult))
);

assertThat(findingResult.status()).isEqualTo(OK);
DocumentContext findingCtx = JsonPath.parse(contentAsString(findingResult));
assertThat(findingCtx).jsonPathAsString("$.name").isEqualTo("Do something");
assertThat(findingCtx).jsonPathAsString("$.dueDate").isEqualTo("2016-12-04");
assertThat(findingCtx).jsonPathAsString("$.status").isEqualTo("OPEN");
}

}

The test class extends play.test.WithApplication class. It provides a fake Play application, required for testing. Before every test, the application is started (WithApplication.startPlay()) and stopped when it is finished (`WithApplication.stopPlay()’).

The test itself is a standard JUnit test (@Test annotation) and is divided into two steps: the creation of an item and looking for it afterwards. Both requests are created with play.mvc.Http.RequestBuilder class that simplifies this greatly. In the test, I set up basic parameters only like an HTTP method, a URI of the target endpoint and a JSON body (for the first step of the test only). The body of the second request I have created with the code below. It creates an instance of a com.fasterxml.jackson.databind.node.ObjectNode class and adds two fields to it. With the Json class I do not have to create JSON from string template or with Jackson’s com.fasterxml.jackson.databind.ObjectMapper and the code is much cleaner.


    private JsonNode itemData(String name, String dueDate) {
return Json.newObject()
.put("name", name)
.put("dueDate", dueDate);
}

Next, having the builder instance configured, there is not too much philosophy when it comes to sending a request in a test. The instance is passed to the play.test.Helpers.routes() method as the parameter. It takes care of building a request based on builder set up and send it to a specified endpoint. The method returns with play.mvc.Result instance holding a response received from the endpoint. You can run test assertions on it like HTTP status check or verify a content of response’s body. I have used assertj-json library for checking the content of the received responses. It is way more useful than the standard JUnit assertions.

We can start the application using run or start task of the Activator. What is the difference? The first one starts our application in dev mode that offers a continuous compilation of changed sources and provides more details in case of an application error. Moreover, you can use play.editor configuration parameter (see application.conf file). It offers the possibility of adding a link to your IDE to the code causing an error. This feature can be quite useful, releasing us from the necessity of scrolling application logs when looking for an error cause. In the case of using start task, an application is started in prod mode and the behaviour in terms of presenting errors is different. Sources of an application are compiled once and error messages are more compact. In case of exception we get only information an error occur and it was logged (if there is no error page provided).

Whatever way you chose to start the todo application you call the exposed endpoints. Yes, this means we can create some items in our todo list, and edit them, and read them, and… Wow! This is cool! 😉

java_play_item_creation

What about other endpoints? They are implemented as well but I am not going to describe all of them here. You can check sources of my Play project to see their implementation and how do I test them. When you open TodoController class you can spot I have used Javaslang library there. While this is not the subject of this post I can say this is a really powerful library providing a lot of functional programming extensions to Java. Definitely worth to try.

OK, this is it. I have written a REST service with Play Framework. What are my findings from this lesson? I see manual handling of JSON data as a significant disadvantage. This is rather redundant step and the task could be done automatically. I miss the way requests are processed in Spring Framework. @BodyParser.Of annotation is a nice mechanism but this is not enough. Maybe I missed something in Play and need to figure out some mechanism hidden in the depths of the framework’s code. Luckily there is Json class simplifying serialisation and deserialisation of a data. What I like too is the creation of responses which is really trivial with helper methods provided. And last but not least, a testing of endpoints with Helper.routes(...) method is done quite good. I cannot write perfectly since there is no out-of-the-box mechanism to make assertions on results’ content.

There is one more thing. I have found a lot of topics in Play Framework to look deeper at. So it seems the series will have more than three or four parts. See you next time!

Leave a Reply