RESTful APIs With the Play Framework — Part 2
We continue our look at creating RESTful APIs with this helpful framework by developing web services and exploring how to handle JSON in our code.
Join the DZone community and get the full member experience.
Join For FreeIn the first part of this series of articles, we talked about the main features of the Play Framework, its basic structure, and how to deploy our applications. In this part, we will talk about how to develop RESTful Services. If you remember (if not, we invite you to read the first part of this series of articles) we mentioned that Play Framework is Reactive, which means that we can develop Asynchronous and Synchronous Services. For the purposes of this second article, we will focus on Synchronous Services.
Developing Web Services
Let's look in more detail at the index action in HomeController.
public Result index(){
return ok(views.html.index.render());
}
Remember that RESTful is a first-class citizen in the Play Framework, which means that all actions are RESTful Services. The structure of an action is simple, it always returns an object of type Result (play.mvc.Result). A Result is the representation of an HTTP result with a state code, headers, and a body that is sent to the client.
For example, in the index action, we use the ok
method to return a Result that renders HTML in the body. Instead of rendering HTML in the body, we can send plain text. For this, we will add a new action in our HomeController call it plainText
.
public Result plainText(){
return ok("This is just a text message.");
}
Once we have added the action, we must add this to the routes file in order to execute it.
GET /plainText controllers.HomeController.plainText
Now we can compare the differences between call the index and plain actions from Insomnia.
Index Response
Index Headers
plainText Response
plainText Headers
The first difference, as you can imagine, between both is the body, since for the index we render HTML, while for plainText
we just show plain text. The following difference lies in the Header Content-Type, for index return "text/html" and for plainText "text/plain."
Handling JSON
When we develop Web Services we seek to use an information exchange protocol, such as JSON. The Play Framework is based on the Jackson library for the handling JSON, using the JsonNode object \, and leaning on the play.libs.Json API. To start our journey developing RESTful Services that return JSON, we will start by importing play.libs.Json in our HomeController.
import play.libs.Json;
The first thing we will do is render some JSON using the HashMap
object, adding an action called jsonMap to ourHomeController, which will look like this:
public Result jsonMap(){
HashMap<String, Object> result = new HashMap<String, Object>(){
{
put("str", "String");
put("int", 123);
}
};
return ok(Json.toJson(result));
}
As you can see, the only thing we did here was create a HashMap, to which we added two elements "str" and "int" with their respective values. Then, using the toJson
method of the JSON API, we indicated that we want to return the HashMap in JSON format. To see how the consumption of this new service looks from Insomnia, we will define the call to this action in our routes file.
GET /json/map controllers.HomeController.jsonMap
jsonMap Response
jsonMap Headers
This is an example to render a HashMap as JSON. To continue, we will do the same thing with an object. For this, we will define a class called Invoice, which will be inside the package com.auth0.beans that we will create at the same level as the controllers directory, which can be seen in the image below.
Note: for readability of the article we do not include the get and set methods of the Invoice class.
package com.auth0.beans;
import java.math.BigDecimal;
import java.time.LocalDate;
public class Invoice {
private String name;
private String address;
private String idNumber;
private String code;
private LocalDate date;
private BigDecimal amount;
public Invoice(){}
public Invoice(String name, String address, String idNumber, String code,
LocalDate date, BigDecimal amount){
this.name = name;
this.address = address;
this.idNumber = idNumber;
this.code = code;
this.date = date;
this.amount = amount;
}
}
Then we will add the action jsonObject
in our HomeController, in which we will create an object Invoice
and we will use the same method toJson
of the JSON API to return it as a response to the client. The action would be like the following code:
public Result jsonObject(){
Invoice invoice = new Invoice("Perico de los Palotes", "City", "123456-7"
"002245", LocalDate.now(), new BigDecimal(1293));
return ok(Json.toJson(invoice));
}
We will define the call to this action in our routes file
GET/json/objectcontrollers.HomeController.jsonObject
jsonObject Response
jsonObject Headers
To finish with the JSONs Handling, we will do it by obtaining a JSON in the request of our RESTful service. For them we will add the jsonCatch action in our HomeController. In it we will obtain a JsonNode from the body of our request, then we will use the fromJson method of the Json API to convert this JsonNode into the object we want, in this case Invoice. To finish we will return a String with some of the data of our Invoice object to guarantee that we have obtained the information that we sent in the test. The final result would look like the following code:
public Result jsonCatch(){
JsonNode jsonNode = request().body().asJson();
Invoice invoice = Json.fromJson(jsonNode, Invoice.class);
DateTimeFormatter f = DateTimeFormatter.ofPattern("dd/MM/yyyy");
return ok(invoice.getCode() + " | " + invoice.getIdNumber() + " | "
+ f.format(invoice.getDate()));
}
We will define the call to this action in our routes file
GET/json/catchcontrollers.HomeController.jsonCatch
jsonCatch Request and Response
jsonCatch Headers
Results in More Detail
We have seen, throughout our examples, that actions always return the Result object, which we have previously indicated is the representation of an HTTP response with a status code, headers, and a body that is sent to the client. The Controller object already has a series of methods that allows us to create Result
, those methods inherit them from the Results
class (play.mvc.Results). In the case of the examples that we have listed in this article, only the ok
method has been used, which returns the status code 200. Addiontionally, in the body we have rendered HTML, text, and JSON.
But there are other methods in Results
that can help us. For example, in circumstances where we need to return a status code different than 200, some of the most common are:
notFound(); //Return state code 404
badRequest(); //Return state code 400
internalServerError(); //return state code 500
status(203); //return a custom state code
Imagine that, if in our jsonCatch action we don't obtain the JSON that we expect in the body, we should indicate a badRequest. To exemplify this, we will add a new action called jsonBadRequest
to our HomeController
public Result jsonBadRequest(){
JsonNode jsonNode = request().body().asJson();
if(jsonNode == null){
return badRequest("Expecting Json data");
}else{
Invoice invoice = Json.fromJson(jsonNode, Invoice.class);
DateTimeFormatter f = DateTimeFormatter.ofPattern("dd/MM/yyyy");
return ok(invoice.getCode() + " | " + invoice.getIdNumber()
+ " | " + f.format(invoice.getDate()));
}
}
In this example, we are returning a bit of plain text in the badRequest, just like in ok
. Here we can also render HTML, text, or send JSON. In order to try this new action in Insomnia, we must add it to our routes file.
GET/json/badRequestcontrollers.HomeController.jsonBadRequest
jsonBadRequest Request and Response
jsonBadRequest Headers
To further delve into the methods inherited from Results you can see the official documentation of play.mvc.Results. As we mentioned previously, in the index example, an HTML template is rendered. To adapt it to what we want, we have to make it no longer render the HTML, but, instead, render what we want.
In this article, we have talked about how to develop Synchronous RESTful Services in the Play Framework by returning and obtaining JSON. You can access the source code by visiting the repository on. In the next series of these articles, we will be talking about the use of JWT for a more secure information exchange.
Opinions expressed by DZone contributors are their own.
Comments