Web API UmbracoApiController does not use DependencyResolver
I've just started trying out the new Web API options in version 6.1.x, and I stumbled on this.
When I have a regular RenderMvcController, and I have set up my IoC for the DependencyHandler, then I am able to resolve dependencies in my controllers constructor.
If I create a constructor in a UmbracoApiController, then it throws an exception:
Type 'FooSite.Web.Api.Controllers.BasketApiController' does not have a default constructor
Looking at the source, it seems that the DependencyResolver is not used when resolving these ApiControllers, as it is for RenderMvcControllers and SurfaceControllers.
Is this a bug, or is there a different way to set up IoC for the WebAPI controllers?
Ok, for SurfaceControllers it should work however you must not have your IoC container setup correctly. You can see in your stack trace above that it stems from this line of code:
The MVC default controller factory *does* use the DependencyResolver so you are getting this exception because your IoC container is not setup correctly and based on which IoC container you are using there are various ways to set it up. For example, AutoFac is super easy to setup and you more or less just change the dependency resolver to use the autofac dependency resolver and a way you go. I know Floris is using the windsor container which i think requires more effort with setting up controller factories, etc...
I've also tested that WebApi controllers do work with IoC so this must be how you've setup your IoC container. Here's an example of how you'd setup your container for AutoFac to work for both SurfaceControllers and UmbracoApiControllers:
On app startup:
//this example will put the ApplicationContext,
//all MVC controllers and API controllers in the
//current assembly in the container.
var builder = new ContainerBuilder();
builder.RegisterInstance(ApplicationContext.Current).AsSelf();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var container = builder.Build();
//setup the webapi dependency resolver to use autofac
var resolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = resolver;
//setup the mvc dependency resolver to user autofac
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
Then we could have a SurfaceController that looks like:
[PluginController("MyArea")]
public class MyTestController : SurfaceController
{
private ApplicationContext _appContext;
public MyTestController(ApplicationContext appContext)
{
_appContext = appContext;
}
[ChildActionOnly]
public ActionResult DoThis()
{
return Content("Hello World");
}
}
And we could have an api controller that looks like:
public class MyTestApiController : UmbracoApiController
{
private ApplicationContext _appContext;
public MyTestApiController(ApplicationContext appContext)
{
_appContext = appContext;
}
public IEnumerable<string> GetStuff()
{
return new[] { "hello", "world" };
}
}
I've verified and tested that this all works so it has something to do with how you've setup your IoC container. The only place in our codebase that we are actually constructing any controllers is in the SurfaceControllerFactory and that is purely to manually inject the UmbracoContext as a ctor parameter if the DependencyResolver fails, otherwise all controller construction is done via the underlying DefaultControllerFactory which also uses the DependencyResolver. We haven't setup anything out of the ordinary for WebApi whatsoever so whatever IoC stuff you need to do for that will just work.
As for the documentation on using an IoC with Umbraco:
I've noticed that this is a difficult concept for some developers to grasp. For instance: which container to use? Why should I use an IoC in the first place? etc
Maybe we can extend the documentation with samples for all the major IoC components out there (Windsor, Ninject, Autofac, MEF (not an IoC, but it is used as such at times), etc). The Windsor container requires some additional work because you need to manually release every dependency. The Umbraco documentation should not include a 'How to choose an IoC component' page, but it is important to notify the user of potential pitfalls (You will get a lot of memory leaks with Windsor if you don't release every dependency properly).
I can see an 'Umbraco.Integration.IoC' package coming up ^^
Ok, so first off, it turned out to be a facepalm moment when I realized that i had actually done a controller registry, that only included RenderMvcControllers. I addet the others, and then it worked like a charm. And I didn't need to add the extra lines to my bootstrapper, as it worked fine with just the DependenceResolver.SetResolver().
But it wasn't for nothing as I was made aware of the release tracking stuff in Windsor that I needed to handle to avoid leaks. So thanks for that :)
@Floris I think it would be a great idea to make som IoC samples for different containers. And maybe even som reusable bits.
I'm using v6.1.6 and don't get DI working well with ApiControllers. All API calls return in a 500 error and nothing comes up in the logs
This my IOC setup code using autofac
// setup autofac
var builder = new ContainerBuilder();
builder.RegisterInstance(ApplicationContext.Current).AsSelf();
builder.RegisterControllers(Assembly.Load("MyProject.Controllers"));
builder.RegisterApiControllers(Assembly.Load("MyProject.Controllers"));
builder.RegisterType<Services.SettingsService>().As<Interfaces.ISettingsService>();
builder.RegisterType<Services.SnippetService>().As<Interfaces.ISnippetService>();
builder.RegisterType<Services.MemberService>().As<Interfaces.IMemberService>();
builder.RegisterType<Services.MailService>().As<Interfaces.IMailService>();
var container = builder.Build();
// WEB API
var resolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = resolver;
// MVC
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
This is my test api controller code
public class TestApiController : UmbracoApiController
{
public TestApiController()
{
}
/// <summary>
/// The get test.
/// </summary>
/// <returns>
/// The <see cref="string"/>.
/// </returns>
public string GetTest()
{
return "test";
}
}
At the moment I have the problem that none of my API controllers are working anymore. Also the Umbraco core api's. Like the ones used in the examine dashboard and the one called when setting domains. All return a 500 error.
When I attach a break point in the umbraco core source also none of the api controllers get hit while other breakpoints (for example in PublishedContentRequest) get hit.
In my example, i just did everything in App_Code, here's the file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using Autofac;
using Autofac.Integration.Mvc;
using Autofac.Integration.WebApi;
using Umbraco.Core;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
using System.Web.Http;
namespace Umbraco.Web.UI.App_Code
{
public class MyStartupHandler : ApplicationEventHandler
{
protected override void ApplicationInitialized(
UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
var builder = new ContainerBuilder();
//Register the UmbracoContext as a factory since the below controllers
//require this in their ctor
builder.Register(c => UmbracoContext.Current).AsSelf();
//Register the current assembly (since these are in App_Code which are dynamic assemblies)
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var container = builder.Build();
//setup the webapi dependency resolver to use autofac
var resolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = resolver;
//setup the mvc dependency resolver to user autofac
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
public class MyCoolApiController : UmbracoApiController
{
//ctor requires injected umbraco context
public MyCoolApiController(UmbracoContext umbracoContext)
: base(umbracoContext)
{
}
public object GetStuff()
{
return "hello world";
}
}
public class umbHomepageController : RenderMvcController
{
//ctor requires injected umbraco context
public umbHomepageController(UmbracoContext umbracoContext)
: base(umbracoContext)
{
}
public ActionResult Index(RenderModel model)
{
return Content("hello world");
}
}
}
IoC is now working for MVC controller hijacking for any doc type with an alias of umbHomepage and is working for WebApi controllers, for this example, if you browse to /umbraco/api/mycoolapi/getstuff
you'll see an xml rendition of Hello world.
And just to show that it does use the dependency resolver for webapi, if i remove the UmbracoContext registration and go to that webapi url again i get the following result:
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Umbraco.Web.UI.App_Code.MyCoolApiController' can be invoked with the available services and parameters: Cannot resolve parameter 'Umbraco.Web.UmbracoContext umbracoContext' of constructor 'Void .ctor(Umbraco.Web.UmbracoContext)'.
</ExceptionMessage>
<ExceptionType>Autofac.Core.DependencyResolutionException</ExceptionType>
<StackTrace>
at Autofac.Core.Activators.Reflection.ReflectionActivator.ActivateInstance(IComponentContext context, IEnumerable`1 parameters) at Autofac.Core.Resolving.InstanceLookup.Activate(IEnumerable`1 parameters) at Autofac.Core.Resolving.InstanceLookup.Execute() at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters) at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters) at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters) at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance) at Autofac.ResolutionExtensions.ResolveOptionalService(IComponentContext context, Service service, IEnumerable`1 parameters) at Autofac.Integration.WebApi.AutofacWebApiDependencyScope.GetService(Type serviceType) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator) at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
</StackTrace>
</Error>
Still having 500 errors. NOthing in log and no error message in the browser. Also the default backend API controllers (Domain and Examine) return 500 errors, even with DI turned off for API controllers.
This code works on a clean install, so I think the problem lies somewhere else. Still need some investigating to do.
After a lot of investigation I found the issue was due to a wrong setup of dependency injection.
In the end I found that below setup works for Umbraco version 7.4.3. I hope somebody else might benefit from this.
public class Global : UmbracoApplication
{
protected override void OnApplicationStarted(object sender, EventArgs e)
{
base.OnApplicationStarted(sender, e);
var builder = new ContainerBuilder();
builder.RegisterInstance(ApplicationContext.Current).AsSelf();
builder.Register(c => UmbracoContext.Current).AsSelf();
builder.RegisterInstance(new UmbracoHelper(UmbracoContext.Current)).AsSelf();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces().InstancePerLifetimeScope();
//register umbraco webapi controllers used by the admin site
builder.RegisterApiControllers(typeof(UmbracoApplication).Assembly);
var container = builder.Build();
//setup the webapi dependency resolver to use autofac
var resolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = resolver;
//setup the mvc dependency resolver to user autofac
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
Web API UmbracoApiController does not use DependencyResolver
I've just started trying out the new Web API options in version 6.1.x, and I stumbled on this.
When I have a regular RenderMvcController, and I have set up my IoC for the DependencyHandler, then I am able to resolve dependencies in my controllers constructor.
If I create a constructor in a UmbracoApiController, then it throws an exception:
Type 'FooSite.Web.Api.Controllers.BasketApiController' does not have a default constructor
Looking at the source, it seems that the DependencyResolver is not used when resolving these ApiControllers, as it is for RenderMvcControllers and SurfaceControllers.
Is this a bug, or is there a different way to set up IoC for the WebAPI controllers?
Hi, will have a look today/tomorrow and let you know!
It seems that SurfaceControllers have the same issue. If it does not have a parameterless constructur, then I get this:
I'll have a look today
I didn't get time today. If this is the case can you please log an issue and assign to me otherwise it will get lost in the abyss ?
Yup, I will log an issue. Just wanted to make sure that it was not "by design" :)
Edit: Issue created: http://issues.umbraco.org/issue/U4-2405
nah, they should be IoC compatible.
This could be a possible solution: https://gist.github.com/florisrobbemont/5821863
Ok, for SurfaceControllers it should work however you must not have your IoC container setup correctly. You can see in your stack trace above that it stems from this line of code:
The MVC default controller factory *does* use the DependencyResolver so you are getting this exception because your IoC container is not setup correctly and based on which IoC container you are using there are various ways to set it up. For example, AutoFac is super easy to setup and you more or less just change the dependency resolver to use the autofac dependency resolver and a way you go. I know Floris is using the windsor container which i think requires more effort with setting up controller factories, etc...
I've also tested that WebApi controllers do work with IoC so this must be how you've setup your IoC container. Here's an example of how you'd setup your container for AutoFac to work for both SurfaceControllers and UmbracoApiControllers:
On app startup:
Then we could have a SurfaceController that looks like:
And we could have an api controller that looks like:
I've verified and tested that this all works so it has something to do with how you've setup your IoC container. The only place in our codebase that we are actually constructing any controllers is in the SurfaceControllerFactory and that is purely to manually inject the UmbracoContext as a ctor parameter if the DependencyResolver fails, otherwise all controller construction is done via the underlying DefaultControllerFactory which also uses the DependencyResolver. We haven't setup anything out of the ordinary for WebApi whatsoever so whatever IoC stuff you need to do for that will just work.
Looks like there are some lines in there that I don't have currently.
I used this as a starting point: http://our.umbraco.org/Documentation/Reference/Mvc/using-ioc
Maybe that should be updated with this part:
@Shannon:
Awesome post! Good details.
As for the documentation on using an IoC with Umbraco:
I've noticed that this is a difficult concept for some developers to grasp. For instance: which container to use? Why should I use an IoC in the first place? etc
Maybe we can extend the documentation with samples for all the major IoC components out there (Windsor, Ninject, Autofac, MEF (not an IoC, but it is used as such at times), etc). The Windsor container requires some additional work because you need to manually release every dependency. The Umbraco documentation should not include a 'How to choose an IoC component' page, but it is important to notify the user of potential pitfalls (You will get a lot of memory leaks with Windsor if you don't release every dependency properly).
I can see an 'Umbraco.Integration.IoC' package coming up ^^
Umbaco.Integration.IoC.Windsor
Umbaco.Integration.IoC.Ninject
Umbaco.Integration.IoC.Autofac
Which have all the neccesary boiler-plate code to wire your IoC of choice into Umbraco
Ok, so first off, it turned out to be a facepalm moment when I realized that i had actually done a controller registry, that only included RenderMvcControllers. I addet the others, and then it worked like a charm. And I didn't need to add the extra lines to my bootstrapper, as it worked fine with just the DependenceResolver.SetResolver().
But it wasn't for nothing as I was made aware of the release tracking stuff in Windsor that I needed to handle to avoid leaks. So thanks for that :)
@Floris I think it would be a great idea to make som IoC samples for different containers. And maybe even som reusable bits.
Thanks for all the info.
I was too quick, I do actually need this line when using the ApiControllers:
Hi all,
I'm using v6.1.6 and don't get DI working well with ApiControllers. All API calls return in a 500 error and nothing comes up in the logs
This my IOC setup code using autofac
This is my test api controller code
Am I doing something wrong ?
Also if I put breakpoints in my API controller..They never get hit.
Dave
Anyone ?
haven't had time to follow up on this, sometime this week
At the moment I have the problem that none of my API controllers are working anymore. Also the Umbraco core api's. Like the ones used in the examine dashboard and the one called when setting domains. All return a 500 error.
When I attach a break point in the umbraco core source also none of the api controllers get hit while other breakpoints (for example in PublishedContentRequest) get hit.
Dave
Until I have time to look at what issue you are having, do these help at all?
http://issues.umbraco.org/issue/U4-2405 http://issues.umbraco.org/issue/U4-4181
Sounds slightly similar
My code matches the second issue. But no API controllers work. RenderMvcControllers and Surface controllers do.
I've just tested this with autofac and it works just fine.
first install auto-fac packages:
In my example, i just did everything in App_Code, here's the file:
IoC is now working for MVC controller hijacking for any doc type with an alias of umbHomepage and is working for WebApi controllers, for this example, if you browse to /umbraco/api/mycoolapi/getstuff
you'll see an xml rendition of Hello world.
And just to show that it does use the dependency resolver for webapi, if i remove the UmbracoContext registration and go to that webapi url again i get the following result:
Still having 500 errors. NOthing in log and no error message in the browser. Also the default backend API controllers (Domain and Examine) return 500 errors, even with DI turned off for API controllers.
This code works on a clean install, so I think the problem lies somewhere else. Still need some investigating to do.
I'm using Unity but this will definitely work. The resolver needs to implement the System.Web.Http.Dependencies.IDependencyResolver interface.
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
I had an issue in Umbraco, it kept giving the following error:
An error occured
Object reference not set to an instance of an object.
EXCEPTION DETAILS
System.NullReferenceException: Object reference not set to an instance of an object.
Umbraco.Web.Security.WebSecurity.IsAuthenticated() Umbraco.Web.Security.WebSecurity.ValidateCurrentUser(Boolean+throwExceptions)
After a lot of investigation I found the issue was due to a wrong setup of dependency injection.
In the end I found that below setup works for Umbraco version 7.4.3. I hope somebody else might benefit from this.
Where the Global.asax is set as followed:
<%@ Application Inherits="VarovaUmbraco.Global" CodeBehind="Global.asax.cs" Language="C#" %>
is working on a reply...