Sanitization with Azure Service Bus Transport

Component: Azure Service Bus Transport
NuGet Package NServiceBus.Azure.Transports.WindowsAzureServiceBus (7.x)
Target NServiceBus Version: 6.x

Prerequisites

An environment variable named AzureServiceBus.ConnectionString with the connection string for the Azure Service Bus namespace.

Azure Service Bus Transport

This sample utilizes the Azure Service Bus Transport.

Code walk-through

This sample has two endpoints

  • Publisher
  • Subscriber
public class SomeEvent :
    IEvent
{
    public Guid EventId { get; set; }
}

Publisher

Publisher publishes SomeEvent.

Subscriber

Subscriber subscribes to and handles SomeEvent. The topology used by the endpoints is ForwardingTopology, which creates a subscription named Subscriber and creates a rule for each event the endpoint subscribes to.

SomeEvent full name is Shared.Messages.In.A.Deep.Nested.Namespace.Nested.Events.SomeEvent. That is 72 characters which exceed the maximum 50 characters limit for a rule name. An attempt to use such a long rule name will result in the following exception:

Invalid Rule name 'Shared.Messages.In.A.Deep.Nested.Namespace.Nested.Events.SomeEvent' that cannot be used with Azure Service Bus. Rule name exceeds maximum allowed length or contains invalid characters. Check for invalid characters, shorten the name, or use 'Sanitization().UseStrategy()' configuration extension.`

Creating custom sanitization

Registering custom sanitization

For the purpose of this sample, custom sanitization based on a SHA1 hashing algorithm will be used:

class Sha1Sanitization :
    ISanitizationStrategy
{
    public string Sanitize(string entityPathOrName, EntityType entityType)
    {
        // remove invalid characters
        if (entityType == EntityType.Queue || entityType == EntityType.Topic)
        {
            var regexQueueAndTopicValidCharacters = new Regex(@"[^a-zA-Z0-9\-\._\/]");
            var regexLeadingAndTrailingForwardSlashes = new Regex(@"^\/|\/$");

            entityPathOrName = regexQueueAndTopicValidCharacters.Replace(entityPathOrName, string.Empty);
            entityPathOrName = regexLeadingAndTrailingForwardSlashes.Replace(entityPathOrName, string.Empty);
        }

        if (entityType == EntityType.Subscription || entityType == EntityType.Rule)
        {
            var rgx = new Regex(@"[^a-zA-Z0-9\-\._]");
            entityPathOrName = rgx.Replace(entityPathOrName, "");
        }

        var entityPathOrNameMaxLength = 0;

        switch (entityType)
        {
            case EntityType.Queue:
            case EntityType.Topic:
                entityPathOrNameMaxLength = 260;
                break;
            case EntityType.Subscription:
            case EntityType.Rule:
                entityPathOrNameMaxLength = 50;
                break;
        }

        // hash if too long
        if (entityPathOrName.Length > entityPathOrNameMaxLength)
        {
            entityPathOrName = SHA1DeterministicNameBuilder.Build(entityPathOrName);
        }

        return entityPathOrName;
    }
}

Generated hash (20 bytes) will be formatted into eight groups of 8 characters each for readability:

static class SHA1DeterministicNameBuilder
{
    public static string Build(string input)
    {
        using (var provider = new SHA1CryptoServiceProvider())
        {
            var inputBytes = Encoding.Default.GetBytes(input);
            var hashBytes = provider.ComputeHash(inputBytes);

            var hashBuilder = new StringBuilder(string.Join("", hashBytes.Select(x => x.ToString("x2"))));
            foreach (var delimeterIndex in new[]
            {
                5,
                11,
                17,
                23,
                29,
                35,
                41
            })
            {
                hashBuilder.Insert(delimeterIndex, "-");
            }
            return hashBuilder.ToString();
        }
    }
}

Custom strategy registration:

var sanitization = transport.Sanitization();
sanitization.UseStrategy<Sha1Sanitization>();

Generated rule name:

Samples

Related Articles


Last modified