MessageConverterBeans Or: How I learned to stop converting manually and trust in Spring

Photo by Nathan Watson on Unsplash

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 HttpMessageConverterBean.

Luckily, Spring has us covered.

Spring’s 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.

Photo by Photo Boards on Unsplash

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

The 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 theBufferedImageis implemented, our next step is to convert the Stringreturn value from our endpoint and return a PNG instead. Therefore we have to implement the MessageConverteritself. 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: canWrite(...), write(...), and getSupportedMediaTypes(...).

While the canWrite(...) and 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 HttpOutputMessage.

We do not convert incoming messages in our converter therefore canRead(...) always returns false and thus read(...) won’t even be called.

The 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 HttpOutpuMessage:

The second step is to actually write the PNG to the outputMessage’s body :

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.

Photo by Hans Ripa on Unsplash

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 Bean definition:

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:

Screenshot of Postman with the request and response if the controller method is producing imge/png content.

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 Stringto a PNG.

If the client requests a format Spring does not have a converter for status 406 Not Acceptable is returned.

Without the 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:

Screenshots of the request an response with the accept-header set to “image/png” respectively “text/plain”.

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 ConverterBean.

Photo by Claudio Schwarz on Unsplash

So, what did we learn?

a) Read your User-Stories properly ;)

b) Spring’s 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.

--

--

--

Dies ist das Blog der Digital Frontiers GmbH & Co. KG (http://www.digitalfrontiers.de). Hier veröffentlichen wir zu Themen, die uns interessieren und bewegen.

Recommended from Medium

Asynchronous Promise

Protect your Notes from Unwanted Edits

MVVM-C Xcode Template to reduce development time

Zara replica — part 4

Custom View Controllers Transitions in Swift

Animations in SwiftUI with examples

Animations in SwiftUI with examples

3 ways of styling SwiftUI views

Building an iOS game in 2 months

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Matthias Seifert

Matthias Seifert

More from Medium

What happens when I type www.xyz.com in my browser and hit enter

Getting started with git and Github

Control your Browser with Playwright

What is GitHub Copilot?