How to remove attribute from NServiceBus message using Mutator

I am looking to remove an attribute from the NServiceBus message using a mutator and it is not working as expected.

I have 4 media types that flow through a common mapping model, and only for one of them, do I need to remove an attribute from the main model. I was attempting to use a mutator to update the serialized message by updating the json attributes. The resulting message is never updated however, no change is made. Is there another approach to consider? Or something I’m missing in my mutator?

Here is an example of the code:

public class RemoveContractAttributeFromMessageMutator : IMutateOutgoingMessages
{
public Task MutateOutgoing(MutateOutgoingMessageContext context)
{
var message = context.OutgoingMessage;

        if (!(message is ProjectModel)) 
            return Task.CompletedTask;
                    
        if ((message as ProjectModel).BuyingSystem != BuyingSystemType.Mercury)
            return Task.CompletedTask;

        var json = JsonConvert.SerializeObject(message);

        JObject jo = JObject.Parse(json);
        foreach (var prop in jo.Properties())
        {
            if (prop.Name == "subsidiary")
            {
                jo.Property("subsidiary").Remove();
                json = jo.ToString();
                break;
            }
        }

        context.OutgoingMessage = JsonConvert.DeserializeObject<ProjectModel>(json);            
        
        return Task.CompletedTask;
    }
}

Hi dave

Have you checked that the mutator is adequately registered?

endpointConfiguration
  .RegisterMessageMutator(new RemoveContractAttributeFromMessageMutator ());

I’ve just verified with this sample, and as long as the mutator is registered, I can completely replace the message.

Regards
Daniel

Daniel,

The message is going through the Mutator and is kind of working as expected. We have found a different issue though.

So in the above code when we deserialized the object our ProjectModel has the Subsidiary property which is not null so it was still showing up on deserialization. That part is completely expected. We can not have that property show up. I know I could alter my json serialization setting and not include null properties but that will not work either because sometimes we want to show null properties because that is how the consumer knows to null out a property or not. Also, we do not control the consumer since that is a third party.

I have tried to change the original message to just contain the json:

context.OutgoingMessage = json;

But when I do that the message doesn’t get sent. It actually just disappears. It doesn’t error, or go to retry it is just gone. In the logs I see the following message:

DEBUG NServiceBus.SerializeMessageConnector Serializing message ‘System.String, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=7cec85d7bea7798e’ with id ‘34b5c3a5-6968-459b-b1e2-ac1b00efff92’, ToString() of the message yields: {
“companyname”: “2020 - July Program - #238188”,
“parent”: 195118,
“custentity_appf_buyingsystem_id”: “8337541”,
“custentity_appf_buying_system”: “1”,
“startdate”: “6/25/2020”,
“custentity_appf_integr_lastupdatedate”: “8/13/2020 10:10:15 AM”,
“id”: 0,
“currency”: 0,
“customform”: 49,
“comments”: 0
}

I would then expect the next log statement:

NServiceBus.RoutingToDispatchConnector Destination:

But that doesn’t show up.

I also tried changing the enclosed type to string since I am using the actual json and that didn’t work either:

context.OutgoingHeaders[“NServiceBus.EnclosedMessageTypes”] = typeof(string).Name;
context.OutgoingMessage = json;

So I am trying to figure out where the message goes but more importantly I want to figure out how I can change the type of the outgoing message to a string? In the mutator and not in the handler because this mutation will not always happen.

Hi Jeremy,

We can not have that property show up. I know I could alter my json serialization setting and not include null properties but that will not work either because sometimes we want to show null properties because that is how the consumer knows to null out a property or not.

The consumer knows to null a property yet still you are doing things on the sender side? I’m a bit confused honestly. If the sender is in charge why not simply add

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]

to the property in question? And if the receiver is in charge you could be using Consumer Driven Contracts and they could declare the NullValueHandling on their side.

I have tried to change the original message to just contain the json:

No that won’t work because you are intercepting at the logical stage and a string is not a logical message. This to an exception and then automatically moves the message into the error queue (if you do the manipulation as part of an incoming message otherwise the exception bubles up the call stack and will be throw on session.Send).

If you want to stick to a mutator then you would need an outgoing transport message mutator and do something like this

public class RemoveStuffBeforeSendingMutator :
    IMutateOutgoingTransportMessages
{
    public async Task MutateOutgoing(MutateOutgoingTransportMessageContext context)
    {
        using (var mStream = new MemoryStream(context.OutgoingBody))
        using (var streamReader = new StreamReader(mStream, Encoding.UTF8))
        using (var outStream = new MemoryStream())
        {
            var jo = JObject.Parse(await streamReader.ReadToEndAsync());
            foreach (var prop in jo.Properties())
            {
                if (prop.Name == "subsidiary")
                {
                    jo.Property("subsidiary").Remove();
                    break;
                }
            }


            using (var streamWriter = new StreamWriter(outStream, Encoding.UTF8))
            using (var jsonWriter = new JsonTextWriter(streamWriter))
            {
                await jo.WriteToAsync(jsonWriter);
                await outStream.FlushAsync();
            }

            context.OutgoingBody = outStream.ToArray();
        }
    }
}

I leave it up to you to make this code more efficient and also deal only with the messages you have to manipulate.

Regards
daniel

Daniel,

Thanks for the reply.

We already have one IMutateOutgoingTransportMessages that we use for encryption of large messages. I would like to add this to a separate IMutateOutgoingTransportMessages mutator. I am guessing you can have two but is there a way to control the order they are called?

Jeremy

The order of

endpointConfiguration.RegisterMessageMutator

defines the order of execution

Regards
Daniel