• 3 min read

IoT Hub message routing: now with routing on message body

IoT Hub message routing can now be done on message body! Thanks to the inundation of feedback from our customers requesting the ability to route messages based on the message body, the team prioritized the work and it’s now available for everyone to use.

IoT Hub message routing can now be done on message body! Thanks to the inundation of feedback from our customers requesting the ability to route messages based on the message body, the team prioritized the work and it’s now available for everyone to use.

Back in December, we released message routing for IoT Hub to simplify IoT solution development. Message routing allows customers to automatically route messages to different services based on customer-defined queries in IoT Hub itself, and we take care of all of the difficult implementation architecture for you. Message routing initially shipped based on message headers, and today I am happy to announce that you can now route messages based on message body for JSON messages, available today.

Message routing based on headers gives customers the ability to route messages to custom endpoints without the service cracking open the telemetry flowing through it, but it came with a limitation: customers had to add information to the headers that they weren’t including otherwise, which hindered the usefulness. Many customers wanted to be able to route directly based on the contents of the message body because that’s where the interesting information already was. Routing on message body is intuitive and allows customers full control over message routing.

It’s super simple to route based on message body: just use $body in the route query to access the message body. For example, my device uses the C# device SDK to send messages using this example code:

DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(deviceClientConnectionString); 
 
string messageBody = @"{ 
                            ""Weather"":{ 
                                ""Temperature"":50, 
                                ""Time"":""2017-03-09T00:00:00.000Z"", 
                                ""PrevTemperatures"":[ 
                                    20, 
                                    30, 
                                    40 
                                ], 
                                ""IsEnabled"":true, 
                                ""Location"":{ 
                                    ""Street"":""One Microsoft Way"", 
                                    ""City"":""Redmond"", 
                                    ""State"":""WA"" 
                                }, 
                                ""HistoricalData"":[ 
                                    { 
                                    ""Month"":""Feb"", 
                                    ""Temperature"":40 
                                    }, 
                                    { 
                                    ""Month"":""Jan"", 
                                    ""Temperature"":30 
                                    } 
                                ] 
                            } 
                        }"; 
 
// Encode message body using UTF-8 
byte[] messageBytes = Encoding.UTF8.GetBytes(messageBody); 
 
using (var message = new Message(messageBytes)) 
{ 
    // Set message body type and content encoding. 
    message.ContentEncoding = "utf-8"; 
    message.ContentType = "application/json"; 
 
    // Add other custom application properties.  
    message.Properties["Status"] = "Active";    
 
    await deviceClient.SendEventAsync(message); 
}

There are a variety of ways to route messages based on the information provided in the example message. Here are a couple of types of queries you might want to run:

  • Simple body reference
    • $body.Weather.Temperature = 50
    • $body.Weather.IsEnabled
    • $body.message.Weather.Location.State = 'WA'
  • Body array reference
    • $body.Weather.HistoricalData[0].Month = 'Feb'
  • Multiple body references
    • $body.Weather.Temperature >= $body.Weather.PrevTemperatures[0] + $body.Weather.PrevTemperatures[1]
    • $body.Weather.Temperature = 50 AND $body.message.Weather.IsEnabled
  • Combined with built-in functions
    • length($body.Weather.Location.State) = 2
    • lower($body.Weather.Location.State) = 'wa'
  • Combination with message header
    • $body.Weather.Temperature = 50 AND Status = 'Active'

In order for IoT Hub to know whether the message can be routed based on its body contents, the message must contain specific headers which describe the content and encoding of its body. In particular, messages must have both these headers for routing on message body to work:

  1. Content type of “application/json”
  2. Content encoding must match one of:
    • “utf-8”
    • “utf-16”
    • “utf-32”

If you are using the Azure IoT Device SDKs, it is pretty straightforward to set the message headers to the required properties. If you are using a third-party protocol library, you can use this table to see how the headers manifest in each of the protocols that IoT Hub supports​:

  AMQP HTTP MQTT
Content type content-type iothub-contenttype $.ct
Content encoding content-encoding iothub-contentencoding $.ce

HTTP requires a custom header to account for batched messages; we’ll be adding support for the standard headers for unbatched messages in the coming months.

The message body has to be well-formed JSON in order for IoT Hub to route based on the message body. Messages can still be routed based on message headers regardless of whether the content type/content encoding are present. Content type and content encoding are only required for IoT Hub to route based on the body of the message.

This feature was brought to you in part by the outpouring of feedback we got requesting the ability to route messages based on message body, and I want to send a huge THANK YOU to everyone who requested the functionality. As always, please continue to submit your suggestions through the Azure IoT User Voice forum or join the Azure IoT Advisors Yammer group.