Friday, 10 February 2017

NServiceBus sends data on Azure Service Bus encoded as Base64 / UTF8

I was writing an Azure Function to receive Azure Service Bus messages sent by an NServiceBus endpoint.

Naively, I thought I could simply decode the body as a string. I was wrong.

I would get the error:
Exception while executing function: Functions.OnOrderCreated. mscorlib: Exception has been thrown by the target of an invocation. System.Runtime.Serialization: Expecting element 'string' from namespace ''.. Encountered 'Element' with name 'base64Binary', namespace ''. .

After an investigation, I discovered that NServiceBus sets the property NServiceBus.Transport.Encoding to the value wcf/byte-array.

The message body looks like this:

<base64Binary xmlns="">77u/eyJBbGxvY2F0aW9uSWQiOiIx...... Simplified ....</base64Binary>

and if you base 64 decode the element content and encode using UTF8 you get the following output in the debugger window:

{"AllocationId":"18475fa4-c696-4992-a559-f48b7cab4cab","CreatedDate":"2017-02-10T11:18:03.7281344+00:00","CurrencyCode":"GBP","SizeSchema":"UK","Products":..... Simplified .... }

However if you then try and use JSONConvert to deseriaize it into a typed object, it errors with:

"Unexpected character encountered while parsing value: . Path '', line 0, position 0."

This is because there are a hidden three bytes which are not shown in the debugger window but which are shown if you look at the byte array. The three bytes are the Byte Order Mark: EF BB BF (239 187 191). These need to be stripped out.

The code below - used in Linqpad - demonstrates a way of getting at the inner content.

void Main()
 Uri uri = ServiceBusEnvironment.CreateServiceUri("sb", "mytest", string.Empty);
 string topicName = "bundle-1";
 string subscriptionName = "Linqpad";
 string name = "RootManageSharedAccessKey";
 string key = "";
 string connectionString = "Endpoint=sb://;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=";
 TokenProvider tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(name, key);
 NamespaceManager namespaceManager = NamespaceManager.CreateFromConnectionString(connectionString);
 SubscriptionClient Client = SubscriptionClient.CreateFromConnectionString(connectionString, topicName, subscriptionName);
 OnMessageOptions options = new OnMessageOptions();
 options.AutoComplete = false;
 options.AutoRenewTimeout = TimeSpan.FromMinutes(1);
 Client.OnMessage((message) =>
   // Console.WriteLine("Transport encoding: " + message.Properties["NServiceBus.Transport.Encoding"]);
   var xml = message.GetBody();   
   var body = Convert.FromBase64String(xml.InnerText);
   var contentWithBom = Encoding.UTF8.GetString(body);
   // content.Dump();
   // Remove the BOM from the string
   var contentWithoutBom = Encoding.UTF8.GetString(body, 3, body.Length - 3);
    var orderCreated = JsonConvert.DeserializeObject(contentWithoutBom);
  catch (Exception error)
   // Indicates a problem, unlock message in subscription.
 }, options);

 string input = "";
 Console.WriteLine("Press x to stop");
 while (!input.ToLower().StartsWith("x"))
  input = Console.ReadLine();

No comments:

Post a Comment