14 min read

(For more resources related to this topic, see here.)

In order to keep the article short and to the point, we’ll only see the Java part. Keep in mind that the Scala version is little different for this level of detail.

Body parsing for better reactivity

As noted earlier, the way to manage content in Play! 2 is to use instances of body parsers. In brief, a body parser is a component that is responsible for parsing the body of an HTTP request as a stream to be converted into a predefined structure. This has a common sense ring to it, however their strength is in their way of consuming the stream—in a reactive fashion.

Reactivity, in this context, is meant to describe a process where an application won’t block on a task that is actually idle. As a stream consumption task is idle when no bytes are incoming, a body parser should behave the same. It will read and construct an internal representation of the incoming bytes. But it can also decide at any time that it has read enough to terminate and return the representation. On the other hand, if no more bytes are coming into the stream, it can relax its thread in favor of another request; it pauses its work until new bytes are received.

Thinking about an HTTP request that is sending a bunch of XML content, the underlying action can use the XML-related body parser to handle it correctly (read reactively); that is, by parsing it and providing a DOM representation.

To understand what a body parser actually is, we’ll first look at how they are used—in the actions. An action in Play! 2 represents the piece of software that is able to handle an HTTP request; therefore, they are the right place to use a body parser.

In the Java API, an action is allowed to be annotated with the Of annotation available in the BodyParser class. This annotation declares the expected type of request routed to it, and it requires a parameter that is the class of the parser that will be instantiated to parse the incoming request’s body.

The following screenshot shows an example:

Isn’t this helpful? We’ve gone from a request to a W3C document, in a single line. Functionally speaking, this works because an action is semantically a higher-order function that takes a body parser and generates a function that takes a request (and so its body) and results in an HTTP response (result). This result will then be used to construct the HTTP response by Play! 2.

In Java, it is not all that obvious how to create a higher-order function. A good way, however, to achieve this was to add an annotation. An annotation can be processed at runtime in order to execute the right body parser (in this case).

To illustrate this, we’ll have a quick look at the Scala version:

With this Scala version, it is easy to see that an action is dealing with a function from a request to a response.

There are a plenty of predefined body parsers that can be used to handle our requests, and they are all defined in the BodyParser class as static inner classes. One can have a specific behavior to be applied on its expected request body, and even though a body parser has to be implemented in Scala, a Java coder can simply extend these current implementations. Actually, they’re already providing enough control to cover all custom use cases.

So, we have in our hands tools to handle the following content types:

  • JSON

  • XML

  • URL form encoded

  • Multipart (for uploading files)

  • Text

  • Raw (fallback)

As we can see from the previous list, there is, obviously, an implementation for the x-www-form-urlencoded content type. Indeed, this is the parser we’ve used so far to retrieve data from the client side. For example, using POST requests throughout HTML forms.

But wait, we never had to add such annotations to our actions, and, moreover, we’ve never looked in the parsed result. That’s true, Play! 2, as a great framework, is already doing a lot of stuff for us. And that’s because it’s a web framework; it takes advantage of HTTP; in this case, using the content-type header.

Based on this hint, it seems obvious that Play! Framework 2 will look in this header to find the right parser to apply. So annotations are mandatory, but where did we use them previously? In the bindFromRequest method, of course. Let’s see how.

We have used form instances, and we fed them some data through the client. Those instances were applied on the request using the bindFromRequest method, and this method’s job was to look for data according to the provided content type. And, of course, this content type was set in the header by the HTML forms themselves.

Indeed, an HTTP GET will send data in the request URL (query string), where an HTTP POST will be sent with a body that contains all data encoded by default as URL parameters (that is, x-www-url-encoded).

So, we can now give an overview of what the bindFromRequest method does. When we ask a form to be filled in with data, this method will:

  • Gather data as URL-form encoded data, if any

  • Gather data from parts (if the content type is multipart-data)

  • Gather data as JSON-encoded, if any

  • Gather data from the query string (that’s why GET requests were working as well)/p>

  • Fill in the form’s data with all of them (and validate)

You might be wondering the worth of such annotations; the quick answer to that is they allow new types of parsers, but they can also enforce certain actions’ requests to match a given content type.

Another advantage of such annotations is that they allow us to extend or narrow the length of the body that can be handled. By default, 100 K are accepted, and this can be either configured (parsers.text.maxLength=42K) or passed as an argument to the annotation.

With all of this in mind, we are now ready to implement these concepts in our code, and what we’re going to do is to update our code base to create a kind of forum. A forum where one can log in, initiate a chat, reply to non-closed ones (based on their date), or even attach files to them.

Creating a forum

In this section, we’ll refactor our existing application in order to enable it to act as a forum. And, chances are high that it won’t be necessary to learn anything new; we’ll just re-use the skills gathered so far; but we’ll also use the parsing commodities that Play! 2 offers us.

Reorganizing and logging in

The very first thing we have to do is to enable a user to log in; this ability was already created in the Data controller. However, for that, we’ll update our Application controller a bit, to create a new index action that will check whether a user is logged in or not.

So, index is now the new entry point of the application and can be routed from / in the routes file. And, it’s solely meant to check if a user has logged in or not. This check is based on the session content, as we simply check whether a user’s e-mail is present in the session.

We never see what a session can be in Play! 2, but we saw that Play! 2 is completely stateless. So, a session in Play! 2 is only an encrypted map of the value stored in the cookie. Thus it cannot be that big, and definitely cannot contain full data.

If the user is present, we redirect the request to the chatroom by calling redirect with the expected action. This will prevent the browser from posting the request again if the user reloads the page. This method is called POST-redirect-GET.

Otherwise, we respond with an Unauthorized HTTP response (401) that contains the HTML login page.

The two actions (shown in the next screenshot) are so simple that we won’t cover them further, except for a single line: session().clear(). It is simply revoking the cookie’s content, which will require the subsequent request to create a new one, which then doesn’t contain the previously stored e-mail.

And finally, enter, which shows how a request’s body can easily be handled using the relevant method: asFormUrlEncoded. It should look like that shown in the following screenshot:

Indeed, one would normally have to use a form to retrieve this information for us, which would do it for us (behind the scenes); but in this case we have only a single parameter to retrieve, so a form would be overkill.

So far, so good; we are now able to create a user, log in with it, and use a login page. To target having cleaner code, it would be worth splitting the Data controller code into several pieces (matter of a good separation of subject). Hence, the Users controller is created, in which will be placed the user-related actions taken out of Data.

Now, we’ll move back to something we saw earlier but didn’t cover—the routes. Chats.allChats() action call.

Chatting

In the previous section, we were introduced to the Chats controller and its allChats action. If the names are self-descriptive, the underlying code isn’t that much.

First of all, we’re now dealing with Chat instances that must be persisted somewhere in a database, along with their underlying items.

But we’ll also prepare for the next section, which relates to multipart data (for instance, it’s helpful for file upload). That’s why we’ll add a brand new type, Image, which is also linked to Chat.

Having said that, it would be worth checking our new chat implementation:

Before we cover the Item and Image types, we’ll first go to the Chats controller to see what’s going on.

Finally, we can see our allChats action; it’s simply rendering all existing instances within a template. Even the rest of the controller is simple; everything is done in templates, which are left as exercises (we’re so good at them now!).

However, there’s still the loadChat action that contains something related to this article:

Long chatId = Long.parseLong(queryString.get("chatid")[0]);

This action handles requests asking to show a particular Chat instance, which is a resource and thus should be served using a GET request. This implies that the parameter value is stored in the query string (or in the URL itself) rather than in the request body.

Regarding query string access, it’s more interesting to analyze the following line:

Map<String,String[]> queryString = request().queryString();

In fact, all actions contextually refer to a request object, which is accessible using the request() method. This request object declares a queryString() method that returns a map of string and an array of strings. What comes next is trivial; we just get chatid out of this map (ok… in a very unsafe way).

Until now, we have been able to log in and access the chatroom, where we can create or show chat instances. But we’re still unable to reply to a chat. That’s what will be tackled now.

For that, we need to create an action that will, based on a chat ID, post a new message linked to the logged in user, and then attach this message as an item of the underlying Chat instance.

For this, we must update the Item class with persistence information. Afterwards, we’ll be able to update the Chats controller in order to create instances.

Ok, it’s like a beefed-up POJO; let’s jump into the action that will create Item instances.

The workflow to post a message for a user starts by enabling him/her to participate in a chat. This is done by loading it (using the loadChat action) where the user will be able to post a new message (an overview of the UI will be presented at the end of this article for illustration only).

The following screenshot shows how it can be done:

Observe how the user was recovered using the session.

Still, nothing cumbersome to review here, we’ve just re-used a lot of stuff we’ve already covered. The action receives a POST request in which information about the message is given, and then we can bind the request to itemForm and finally save to the database the item contained in the resulting form.

At most, we should notice that we’re still free to encode the body as we want, and also that the chat ID is not a part of the form but a part of the action signature—that’s because it is a part of the URL (routing).

We’ve almost finished our forum; the only thing needed is to enable users to post images.

Handling multipart content types

The HTTP protocol is ready to accept, from a client, a lot of data and/or large chunks of data, at once. A way to achieve this is to use a specific encoding type: multipart/form-data. Such requests will have a body that can hold several data pieces formatted differently and attributed with different names. So, Play! 2 is a web framework that fits into HTTP as much as possible; that’s why it deals with such requests goods, and provides an API that hides almost all of the tricky parts.

In this section, we’ll see how one could upload an image along with some caption text that will be attached to a specific chat.

Before diving into the workflow, let’s first create the holding structure: Image.

This newly introduced type is not hard to understand as well; only two things should be pointed out:

  • The pic() method that relies on the filePath field to recover the file itself. It uses a File instance to memorize subsequent calls.

  • The enum type that prepares the action logic to filter the incoming files based on the given MIME type.

This logic could also be defined in the validate method.

These instances are always locked in with the connected user who uploaded it and will be added to a Chat instance. This will allow a chatroom to display all attached images with their caption beside the messages themselves.

Now we’re ready to look at the file upload itself by paying some attention to the last action of the Chats controller, that is, receiveImage.

As we are used to simplifying the code (Play! 2 is there to ease our work, after all) and to get straight to the point, we reflected this in our receiveImage action..

In a very few lines, we declared a new action that expects requests to be multipart encoded containing at least two parts, where the first is a map of data (no matter how this map is encoded) to fill in imageForm (essentially a caption). The second will be the image part.

After binding the request with the form and verifying that no errors have occurred, we can move to the body content in order to recover the binary data that was sent along with its metadata: the file content, its content type, its length, and so on.

That was quite an intuitive thing to do – asking the body to be parsed as a multipart/multidata and and get it as an Http.MultipartFormData object, which has a getFile method that returns an Http.MultipartFormData.FilePart value. To understand why we didn’t specify a body parser, recall that Play! 2 is able, most of the time, to discover which method fits best by itself. The Http.MultipartFormData. FilePart type is not only allowing us to recover the content as a file, but also its key in the multipart body, its filename header, and (especially) its content type.

Having all of these things in hand, we are now able to check the content-type validity against the image’s enum, and to store the image by getting the file path of the provided file.

This file path will target the Temp directory of your machine. In the real world, the file should be relocated in a dedicated folder or maybe on an S3 repository.

Et voilà! We have now learned about some of the features that can provide a very simple forum. The following screenshot shows what it could look like (without any efforts on the design, of course). First, the forms to show and enter archived and active chats:

On entering an active chat, let’s say the one named Today, we reach a page similar to the one shown next:

Using the Attach an image form, we can select an image on our filesystem to be sent to the server. The result obtained is shown as follows:

Until now, we have spoken about handling various content types coming from the outside world, but what about our application having to render content other than HTML? That’s what we’re about to see next.

LEAVE A REPLY

Please enter your comment!
Please enter your name here