Question

When configuring the Rebus bus, you can specify that it should look for the Headers.UserName header as described here. This allows you to automatically populate the Thread.CurrentPrincipal with the proper username. This works great, but I wonder if there is a good way to set the header in a standard way instead of manually setting it when sending each message.

I'm building an ASP.NET Web API based service and would like each request to set the header to be the name found in apiControllerInstance.RequestContext.Principal.Identity.Name.

A decent solution would be to create a method on a base ApiController which has access to the message bus, so you can do something like this:

public IHttpActionResult SomeAction(SomeMessage message)
{
    SendWithUser(message);
    return Ok();
}

The problem is that you have to remember to do this, so I would like to get some suggestions on how to do this in a more general fashion. Also, other headers might become relevant in the future which would also be problematic with this approach.

Update

Based on mookid's answer, I came up with the following solution (I have just verified that it works for one of my requests but I make no promises regarding robustness):

public static void SetUserNameHeaderWhenCurrentPrincipalIsPresent(this IRebusEvents events)
{
    events.MessageSent += (bus, destination, message) =>
    {
        if (HttpContext.Current.User != null && !string.IsNullOrWhiteSpace(HttpContext.Current.User.Identity.Name))
            bus.AttachHeader(message, Headers.UserName, HttpContext.Current.User.Identity.Name);
    };
}

Usage:

.Events(e => e.SetUserNameHeaderWhenCurrentPrincipalIsPresent())
Was it helpful?

Solution

You can use

Configure.With(...)
    .(...)
    .Events(e => e.MessageSent += SetUsername)

to have SetUsername called for each outgoing message, and that's where you can do a lookup on HttpContext.Current

Please let me know how that works out for youor if you think something else is missing :)

OTHER TIPS

Try to use the decorator pattern combined with autofac. The following example illustrate a possible solution:

public class RebusConfig  {

    protected override void Load(ContainerBuilder builder)
    {
        //Autofac configuration to decorate Rebus
        builder.RegisterDecorator<IBus>(
                    (c, inner) => new RebusMetadataDecorator(
                            inner, c.Resolve<HttpRequestMessage>()),
                    fromKey: "Metadata")
                     .InstancePerRequest();

    }

    public override SetContainer(IContainer container){
            //Configure the desired rebus
            var bus = Rebus.Config.Configure.With(new AutofacContainerAdapter(container));

            //Reconfigure rebus to support decorator
            //Note: I want to improve this step!
            var containerUpdate = new ContainerBuilder();

            containerUpdate
                  .RegisterInstance(bus)
                  .SingleInstance()
                  .Named<IBus>("Metadata");

            containerUpdate.Update(container);
     }          
}           

//Rebus Decorator               
public class RebusMetadataDecorator : IBus
{
        private  HttpRequestMessage requestMessage;
        private  IBus bus;

        public RebusMetadataDecorator(
            IBus bus, HttpRequestMessage requestMessage)
        {
            this.requestMessage = requestMessage;
            this.bus = bus;
        }

        public void Publish<TEvent>(TEvent message)
        {
            SetHttpMetadataToHeader(bus, message, metadataProvider, prefix);
            bus.Publish<TEvent>(message);
        }
}

The header can be added to the send message in a nice way. Example below.

private readonly IBus messageBus;
...
var expirationHeader = new Dictionary<string, string>
{
    { Headers.TimeToBeReceived, "00:00:30" },
};

this.messageBus.Send(reqMsg, expirationHeader).ConfigureAwait(false);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top