﻿# Message Mutators


Message mutators allow mutation of messages in the pipeline.

NServiceBus supports two categories of message mutators:


## Logical message mutators

Message mutators change/react to individual messages being sent or received. The `IMutateOutgoingMessages` or `IMutateIncomingMessages` interfaces allow the implementation of hooks for the sending and receiving sides.

Mutators can be used to perform actions such as validation of outgoing/incoming messages.


### IMutateIncomingMessages

<!-- snippet: IMutateIncomingMessages -->

```cs
public class MutateIncomingMessages :
    IMutateIncomingMessages
{
    public Task MutateIncoming(MutateIncomingMessageContext context)
    {
        // the incoming headers
        var headers = context.Headers;

        // the incoming message
        // optionally replace the message instance by setting context.Message
        var message = context.Message;

        return Task.CompletedTask;
    }
}
```

<!-- endsnippet -->


### IMutateOutgoingMessages

<!-- snippet: IMutateOutgoingMessages -->

```cs
public class MutateOutgoingMessages :
    IMutateOutgoingMessages
{
    public Task MutateOutgoing(MutateOutgoingMessageContext context)
    {
        // the outgoing headers
        var outgoingHeaders = context.OutgoingHeaders;

        if (context.TryGetIncomingMessage(out var incomingMessage))
        {
            // do something with the incoming message
        }

        if (context.TryGetIncomingHeaders(out var incomingHeaders))
        {
            // do something with the incoming headers
        }

        // the outgoing message
        // optionally replace the message instance by setting context.OutgoingMessage
        var outgoingMessage = context.OutgoingMessage;

        return Task.CompletedTask;
    }
}
```

<!-- endsnippet -->


## Transport message mutators

Transport message mutators work on the serialized transport message and are useful for compression, header manipulation, etc.
Create transport message mutators by implementing the `IMutateIncomingTransportMessages` or `IMutateOutgoingTransportMessages` interfaces.


### IMutateIncomingTransportMessages

<!-- snippet: IMutateIncomingTransportMessages -->

```cs
public class MutateIncomingTransportMessages :
    IMutateIncomingTransportMessages
{
    public Task MutateIncoming(MutateIncomingTransportMessageContext context)
    {
        // the bytes of the incoming messages.
        var bytes = context.Body;

        // optionally replace the Body
        context.Body = ServiceThatChangesBody.Mutate(context.Body);

        // the incoming headers
        var headers = context.Headers;

        // optional manipulate headers

        // add a header
        headers.Add("MyHeaderKey1", "MyHeaderValue");

        // remove a header
        headers.Remove("MyHeaderKey2");

        return Task.CompletedTask;
    }
}
```

<!-- endsnippet -->


### IMutateOutgoingTransportMessages

<!-- snippet: IMutateOutgoingTransportMessages -->

```cs
public class MutateOutgoingTransportMessages :
    IMutateOutgoingTransportMessages
{
    public Task MutateOutgoing(MutateOutgoingTransportMessageContext context)
    {
        if (context.TryGetIncomingMessage(out var incomingMessage))
        {
            // do something with the incoming message
        }

        if (context.TryGetIncomingHeaders(out var incomingHeaders))
        {
            // do something with the incoming headers
        }

        // the outgoing message
        var outgoingMessage = context.OutgoingMessage;

        // the bytes containing the serialized outgoing messages.
        var bytes = context.OutgoingBody;

        // optionally replace the Body.
        // this can be done using any information from the context
        context.OutgoingBody = ServiceThatChangesBody.Mutate(context.OutgoingMessage);

        // the outgoing headers
        var headers = context.OutgoingHeaders;

        // optional manipulate headers

        // add a header
        headers.Add("MyHeaderKey1", "MyHeaderValue");

        // remove a header
        headers.Remove("MyHeaderKey2");

        return Task.CompletedTask;
    }
}
```

<!-- endsnippet -->


## Registering a mutator

Mutators are registered using:

<!-- snippet: MutatorRegistration -->

```cs
endpointConfiguration.RegisterMessageMutator(new MyIncomingMessageMutator());
endpointConfiguration.RegisterMessageMutator(new MyOutgoingTransportMessageMutator());
```

<!-- endsnippet -->

> [!NOTE]
> Mutators are non-deterministic in terms of order of execution. If more fine-grained control is required over the pipeline see [Pipeline Introduction](/nservicebus/pipeline/manipulate-with-behaviors.md).


## When a mutator throws an exception

If an incoming mutator throws an exception, the message aborts, rolls back to the queue, and [recoverability](/nservicebus/recoverability/index.md) is applied.

If an outgoing mutator throws an exception, the exception bubbles up to the method performing the Send or Publish. If the operation is performed on a context in the pipeline the message aborts, rolls back to the queue, and [recoverability](/nservicebus/recoverability/index.md) is applied. If the operation is performed on the message session the exception might bubble up to the user code or tear down the application domain if not properly handled.


> [!NOTE]
> All NServiceBus extension points that return [`Task`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task) cannot return a null Task. These APIs must return an instance of a Task, i.e. a pending Task or a [`CompletedTask`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.completedtask), or be marked [`async`](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/async). For extension points that return a [`Task<T>`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1), return the value directly (for async methods) or wrap the value in a [`Task.FromResult(value)`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.fromresult).


## Mutators versus Behaviors


### Shared concepts and functionality

Both mutators and behaviors:

 * Can manipulate pipeline state
 * Can be executed in the incoming or outgoing pipeline
 * Bubble exceptions up the pipeline and handle them by the [recoverability](/nservicebus/recoverability/index.md) mechanism


### Differences

Note that these are relative differences. So, for example, a behavior is only "high complexity" in comparison to a mutator.

|                                   | Mutator | Behavior |
|-----------------------------------|---------|----------|
| Complexity to implement           | Low     | High     |
| Flexibility                       | Low     | High     |
| Location in pipeline              | Fixed   | Flexible |
| Complexity to test                | Low     | Medium*  |
| Can control nested action         | No      | Yes      |
| Affects call stack depth          | No      | Yes      |
| Can replace an existing behavior  | No      | Yes      |

* For more information refer to the [testing behaviors](/samples/unit-testing/index.md) unit testing sample.

