Andrew Davey
Andrew Davey

Reputation: 5451

Inject WebAPI UrlHelper into service using Autofac

I have a service used by a few controllers in my WebAPI project. The service needs to generate URLs, so ideally it would get a UrlHelper via a constructor parameter.

public class MyService
{
    public MyService(UrlHelper urlHelper) { ... }
}

I'm using Autofac as my IoC container. How can I register UrlHelper in the container? It needs a HttpRequestMessage and I can't figure out how to get the "current" message.

Upvotes: 9

Views: 3823

Answers (3)

Muhammad Rehan Saeed
Muhammad Rehan Saeed

Reputation: 38417

Use the RegisterHttpRequestMessage method to register the current request and then you can also register the URL helper like so:

public static IContainer SetupContainer(HttpConfiguration config)
{
    var containerBuilder = new ContainerBuilder();

    // Register your Web API.
    containerBuilder.RegisterApiControllers(Assembly.GetExecutingAssembly());
    containerBuilder.RegisterHttpRequestMessage(config);
    containerBuilder.Register(x => new UrlHelper(x.Resolve<HttpRequestMessage>()));  
    containerBuilder.RegisterWebApiFilterProvider(config);
    containerBuilder.RegisterWebApiModelBinderProvider();

    // Register your other types...

    var container = containerBuilder.Build();

    // Set the dependency resolver to be Autofac.
    config.DependencyResolver = new AutofacWebApiDependencyResolver(container);

    return container;
}

Upvotes: 9

Andrew Davey
Andrew Davey

Reputation: 5451

Based on Darrel Miller's comment, I created the following:

A simple container class to hold a reference to the "current" HttpRequestMessage

public class CurrentRequest
{
    public HttpRequestMessage Value { get; set; }
}

A message handler that will store the current request

public class CurrentRequestHandler : DelegatingHandler
{
    protected async override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        var scope = request.GetDependencyScope();
        var currentRequest = (CurrentRequest)scope.GetService(typeof(CurrentRequest));
        currentRequest.Value = request;
        return await base.SendAsync(request, cancellationToken);
    }
}

In Global.asax, when configuring WebAPI, add the message handler.

GlobalConfiguration.Configuration.MessageHandlers.Insert(0, new CurrentRequestHandler());

Then, configure the Autofac container to let it construct UrlHelper, getting the current request from the CurrentRequest object.

var builder = new ContainerBuilder();
builder.RegisterType<CurrentRequest>().InstancePerApiRequest();
builder.Register(c => new UrlHelper(c.Resolve<CurrentRequest>().Value));
builder.RegisterType<MyService>();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
...
container = builder.Build();

UrlHelper can then be injected into the MyService just like any other dependency.

Thanks to Darrel for pointing me in the right direction.

Upvotes: 5

Brendan
Brendan

Reputation: 581

Best I can think of is make your service implement IAutofacActionFilter like so

public class MyService : IAutofacActionFilter
    {
        private UrlHelper _urlHelper;

        public void DoSomething()
        {
            var link = Url.Link("SomeRoute", new {});
        }

        private UrlHelper Url
        {
            get
            {
                if(_urlHelper == null)
                    throw new InvalidOperationException();

                return _urlHelper;
            }
        }

        public void OnActionExecuted(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
        {            
        }

        public void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            this._urlHelper = actionContext.Request.GetUrlHelper();
        }
    }

and then use something like the following Autofac config

builder.RegisterType<MyService>()
                 .AsWebApiActionFilterFor<SomeController>()
                 .AsSelf()
                 .InstancePerApiRequest();

Upvotes: 0

Related Questions