Last Updated on 30/03/2020 by Patryk Bandurski
Recently I have been working on a really simple case. When an HTTP server returned an error and not empty body I needed to embed this body into an error structure. Although this is simple case I got a problem with implementing it in Mule 4. In the previous Mule edition it was a little bit simpler. So let’s see what it is all about.
Case
First we have a look at the case that I have to implement. I have a mule application that is calling external HTTP server via HTTP request component. When the server respond with an error like 4xx, 5xx and returned body will not be empty we would like to forward that response to the calling application.
On the diagram below I have depicted situation when HTTP server sent 400 Bad request response back to our application. Then I attach returned body to my custom error response structure that will be returned to the calling application.
When we receive an error response it may happen that not all of them will have the same content type. Here are three examples:
400 Bad Request:
{
"errors": [
{
"status": "400",
"title": "Invalid Attribute",
"detail": "First name must contain at least three characters."
}
]
}
500 Internal Server Error:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><myErrorEnvelope>
<internalCode>123</internalCode>
<description>error details</description>
</myErrorEnvelope>
404 Not Found:
"Not Found"
I would like to return to my client following response:
{
"code": "{{my custom title}}",
"description": "{{detailed description}}",
"content": "{{response body returned by the HTTP server}}"
}
Design
Simple assignment
I could simply assign returned content like in the script below. As you can see the field content will have returned payload. As it is described in this article exceptions are packed into an error object. Response that HTTP request component received will be packed into the errorMesage property of the error object.
%dw 2.0
output application/json
---
{
code: error.errorMessage.attributes.statusCode,
description: "Description",
content: error.errorMessage.payload
}
This solution is the simple one, however DataWeave transform component will translate the error response into a JSON format. If we do not care to see the original response this may be a good solution. In my case I need to see the exact response in the format returned by the server. That is why I found out the write funtion.
Write function
Write function is designed to convert incoming object into a text string. Function signature is as follows:
write(object: Any, format: String, conf: Object): String
Arguments:
- object to write. Any type is acceptable. It may be an xml document, json, plain text etc.
- output format. Only types supported by DataWeave. By default application/dw is used.
- additional configuration.
On the diagram below you can find three sample cases. In all cases we would like to have written payload as XML. In the first case we have an XML document as an input. The write function takes it and transformes it into an XML string.
The second case have a JSON input with one root property. The write function takes it and transforms it into an XML string. Transformation works here because JSON have one root property and XML expects that.
The third case have a JSON input with two properties on the root level. The write function tries to transformt it however it would result with malformed XML document. Therefore it throws an error.
So here I have the transformation when the write function is used. As you can see I have applied content type to be application/xml.
%dw 2.0
output application/json
---
{
code: error.errorMessage.attributes.statusCode,
description: "Description",
content: write(error.errorMessage.payload, "application/xml")
}
The write function does expect now that the supplied object is XML. Here is a sample output of that transformation.
{
"code": 400,
"description": "Description",
"content": "<?xml version='1.0' encoding='windows-1252'?>\n<root>\n <message>Bad request</message>\n</root>"
}
Okey. But I have tell you earlier that I can expect different payload types. What can I do?
¯\_(ツ)_/¯
So how about change the type to text/plain. It would be the most generic option. But It won’t work, sadly. DataWeave will return an error that for example application/json object can’t be transformed into text/plain.
In order to fix this is to place component such as Set Payload or Set Variable and set its mimeType attribute to value text/plain. After that it will work smoothly.
So here is the final transformation:
%dw 2.0
output application/json
---
{
code: error.errorMessage.attributes.statusCode,
description: "Description",
content: write(payload, "text/plain")
}
Summary
Write function is very helpful and I found out that it may be useful in variety of places during your application design. I found it a bit problematic that it performs also validation so when I have received invalid data this function will throw an error.
See you soon :).