MessageConverterBeans Or: How I learned to stop converting manually and trust in Spring
We have all been there. We picked that surprisingly simple user story that wanted us to directly return a String given as a
PathParameter. The story is implemented and all our new endpoints return the String-parameter they get in the request when suddenly a new criterion we have never noticed before materializes in the story’s DoD. The String should be returned as a
PNG containing the String as a QR-Code.
With the review coming closer some really good advice is needed. Reimplementing every single endpoint to return
ResponseEntities and write byte-Arrays instead of simple Strings takes way too long and is way too much work.
What if… there was a simple yet almost ingenious solution to convert
Http-Messages the way we want them. Maybe with just a single Bean available in the context which will then be utilized the very moment we need a
PNG containing a QR-Code. A Bean converting a
HttpMessage, something like a
Luckily, Spring has us covered.
HttpMessageConverters do pretty much what their name implies. Whenever a
HttpRequestneeds to be converted to an object (and vice versa) Spring selects a matching
HttpMessageConverterto do this job.
A few of these MessageConverters are enabled by default and perform their jobs without us even noticing. Have you ever wondered why our DTOs are returned as a JSON-structure without ever using a JSON-serializer explicitly or why a JSON-object sent to an endpoint shows up as an object of just that class we expect?
That’s because a matching
HttpMessageConverter— namely the
MappingJackson2HttpMessageConverter— is available on the classpath as it comes as a transitive dependency of the Spring Boot Starter.
The Jackson converter of course comes with some magic to handle all kinds of DTOs without the need to explicitly implement a matching MessageConverter. Still, the idea is all the same.
But how does this help us with the review coming closer and closer?
Obviously, all we have to do is implement our own
MessageConverter which converts a String to a PNG image containing the QR-Code, place a matching Bean in the context, and call our endpoints in a way so the converter is used.
The first step is to implement a
QrGeneratorService which creates a
BufferedImage containing the QR-Code. For this task we will use the ZXing (“zebra crossing”) library. ZXing unfortunately is in Maintenance Mode only, but it still is a really good library to create and detect all sorts of 1D and 2D-Codes.
All code mentioned here and a complete demo application is available on github
QrGeneratorService is a straight-forward Service class containing only one method accepting a String and the edge length of the square QR-Code which then creates a
BufferedImage containing said QR-Code.
O nce the creation of the
BufferedImageis implemented, our next step is to convert the
Stringreturn value from our endpoint and return a
PNG instead. Therefore we have to implement the
HttpMessageConverter interface is quite simple with only five well-named methods to be implemented:
Since we don’t want to read QR-Codes but only write them, our implementation is reduced to three of these methods:
getSupportedMediaTypes(...) methods are only used to determine what the
HttpMessasgeConverter supports, the
write(...)method is where the magic happens. Here the content is converted and written to the
We do not convert incoming messages in our converter therefore
read(...)won’t even be called.
write(...) method is called with three parameters: The
String to be converted, the
MediaType requested and the
HttpOutputMessage we write the converted result to.
We then first create a BufferedImage with the QR-Code and set the
contentType of the body of the
The second step is to actually write the
PNG to the outputMessage’s
That’s all — at least everything concerning the HTTP part. Functionality to create the
PNG is encapsulated in the
writeBufferedImageAsContentTypeToOutputStream(...) method. If you want to have a look at the details on how to create a
PNG from a
BufferedImage you can of course find that code on github.
Now that we have completed the conversion part our next task is to tell Spring to actually use it.
This means: We have to put the
Bean into the context. To place the Bean in the application’s context we create a
Configurationclass containing the
And that’s it. To ensure our client gets the information in the format it wants we can go two ways:
One way is to extend the
GetMapping for that endpoint and explicitly state that this endpoint produces a
PNG image. This means that whoever calls this client will always receive a
PNG containing the QR-Code.
With this configuration the controller returns a PNG even if the
Accept header is not set:
Another way would be to let the client decide whether they want a
PNG or just the plain text. In this case we don’t simply configure the
MediaType the endpoint produces, but look at what the client requests in the
Acceptheader first. When a client requests an
image/png but our controller-method only returns a String, Spring will use our
MessageConverter to convert the
If the client requests a format Spring does not have a converter for status
406 Not Acceptableis returned.
produces parameter set in the
GetMapping the application returns a
PNGonly if an
image/png is requested. If the
Accept header contains
text/plain the actual plain text is returned instead:
Both ways have their pros and cons. I personally find it a very intriguing idea that we can let the client decide which representation it wants for the requested data and that we can handle the conversion without any explicit implementation in the Controller itself.
Assuming we wanted to not only provide the data as an QR-Code but also as some sort of barcode we could define a custom
MediaType in the context of this application and register a matching converter. That’s all.
curl -H “Accept: image/x-qr-png”might then return a QR-Code,
curl -H “Accept: image/x-barcode-png” might return a barcode. Both without the need of adding new endpoints. All we would have to do is add another
So, what did we learn?
a) Read your User-Stories properly ;)
HttpMessageConverters provide an easy-to-use and intuitively understandable way to convert Objects into all kind of formats. The conversion itself is encapsulated in message converters and Spring automatically picks the right converter based on the object to be converted and the
MediaType to be produced. Many conversions are actually made and used without us even noticing.
When the default converters don’t suit our needs we can easily implement our own converter and it integrates just as the default ones.
From a testing and reusability point of view the segregated development of the converter is just perfect. With
ConditionalBeans we can even configure the available converters at start time with just a change in configuration.
I hope you enjoyed this article about a mechanism that’s certainly used in almost every Spring-based backend and I hope I could help you understand, how to use
HttpMessageConvertersto improve your
API. What was your most memorable moment when a criterion suddenly materialized?
Thanks for reading! If you have any comments, suggestions, or questions please leave a comment, drop me a message on medium, or DM me on twitter. You might also be interested in the other posts published in the Digital Frontiers blog, announced on our Twitter account.