Igor Kromin |   Consultant. Coder. Blogger. Tinkerer. Gamer.

Jersey makes it really easy to customise how your RESTful service accepts input and how it produces output. I've written previously about how, with a few lines of code it's possible to use Jackson to convert your POJOs to JSON when returning data from your service. This article looks at the other end of the lifecycle - the input, specifically how a JAX-RS service can process a POST message body and create a POJO as input to a Resource method parameter. In other words - how do you implement a custom message body reader?

First lets look at the Resource class...
 HelloPostResource.java
@Path("/hello")
@Produces({ "text/plain" })
public class HelloPostResource {
@POST
public String sayHello(Hello hello)
{
return "Hello " + hello.getName();
}
}


Quite straight forward. It's a resource that accepts POST requests on the /hello URI. The input is a Hello type object. The output is a String that ends up being something like "Hello {name}".

The Hello object is just as simple, it's a DTO that holds one value - the name...
 Hello.java
public class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}


Getting back to the HelloPostResource code, you might be thinking that there aren't any parameter annotations on the Hello object in the sayHello() method. That is correct. The hello object is instead populated by a MessageBodyReader. Lets see how that is done...
 MyMessageBodyReader.java
public class MyMessageBodyReader implements MessageBodyReader<Hello> {
@Override
public boolean isReadable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return (type == Hello.class);
}
@Override
public Hello readFrom(Class<Hello> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders,
InputStream inputStream)
throws IOException, WebApplicationException
{
try (BufferedReader br = new BufferedReader(
new InputStreamReader(inputStream))) {
String name = br.readLine();
Hello hello = new Hello();
hello.setName(name);
return hello;
}
}
}


There are two methods that the MessageBodyReader interface provides. The first - isReadable() - is used to determine whether that particular MessageBodyReader implementation is capable of handling the necessary data conversion. The second - readFrom() does the actual conversion from the POST message body to a Hello object.



When Jersey attempts to process the sayHello(Hello hello) method it invokes all registered MessageBodyReader implementations and the first that returns true from its isReadable() method will be called to process the POST body. In the case of MyMessageBodyReader, true is returned if the input type is equal to Hello.class. This is a very simple check but works in this case. It's possible to create more elaborate checks that take annotations and the media type into account by using the relevant parameters passed to isReadable().

The readFrom() method has all of the same parameters as isReadable() plus a map of HTTP headers and the input stream representing the POST body itself. In the case of MyMessageBodyReader, the only input parameter that is used is the input stream. The implementation in this case is very simplistic - the first line of the input stream is read and used as the 'name' attribute, any other lines are ignored. Then, the Hello object is instantiated, it's name is set and the object is returned. That is the object that gets passed along to the sayHello(Hello hello) method on the HelloPostResource class.

In a more complex implementation the input stream could be used together with a JSON or XML parser to read the body text and convert to Java objects for example.

Invoking the service using Postman renders these results...
jerseypost1.png


It works as expected! A real implementation would of course be more involved, however the code shown in this article shows how to implement a custom message body reader that can be used as a template.

Don't forget to register() the MyMessageBodyReader class or add the @Provider annotation if you're using automatic feature discovery.

-i

Hope you found this post useful...

...so please read on! I love writing articles that provide beneficial information, tips and examples to my readers. All information on my blog is provided free of charge and I encourage you to share it as you wish. There is a small favour I ask in return however - engage in comments below, provide feedback, and if you see mistakes let me know.

If you want to show additional support and help me pay for web hosting and domain name registration, donations, no matter how small, are always welcome!

Use of any information contained in this blog post/article is subject to this disclaimer.
comments powered by Disqus
Other posts you may like...