9

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.

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Andrew Davey
  • 5,441
  • 3
  • 43
  • 57
  • Take a look at what we have been working on for the WebAPIBook, it has one possible solution to this problem... https://gist.github.com/glennblock/8f18bdee15eec9c1af70 – Darrel Miller Jun 25 '13 at 13:44
  • Ah, yes, very nice. I was thinking about creating a message handler to solve this :) I'll give it a shot. Thanks. – Andrew Davey Jun 25 '13 at 14:00

3 Answers3

9

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;
}
Muhammad Rehan Saeed
  • 35,627
  • 39
  • 202
  • 311
  • Autowiring the api controllers properties does not work with RegisterHttpRequestMessage. i.e. RegisterApiControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired() . If you use this code, you lose the option to autowire properites. – Mick May 18 '16 at 02:00
5

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.

Andrew Davey
  • 5,441
  • 3
  • 43
  • 57
  • 8
    I have just added support for HttpRequestMessage dependency injection to Autofac.WebApi and it will be included in the next release. https://code.google.com/p/autofac/source/detail?r=60d369499963b89224c1f682cfbf8592eae95342 – Alex Meyer-Gleaves Jun 25 '13 at 14:55
  • I couldn't get RegisterHttpRequestMessage to work however this code did the job – Mick May 18 '16 at 01:50
0

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();
Brendan
  • 581
  • 1
  • 4
  • 17