Search code examples
c#asp.net-web-apiautofacasp.net-web-api-routing

Exception trying to use attribute routing with autofac


I have a problem with dependency injection when I'm trying to add custom route attribute on my action. Everything is working fine when I'm trying to use standard methods like Get, Post etc but when I'm calling FindUsersByRole action I'm getting an error:

System.InvalidOperationException: An error occurred when trying to create a controller of type 'UsersController'. Make sure that the controller has a parameterless public constructor.

Stack trace:

at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) 
at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()

Inner exception:

System.ArgumentException: Type 'Novelis.WebApi.Controllers.UsersController' does not have a default constructor

StackTrace:

at System.Linq.Expressions.Expression.New(Type type) 
at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType) 
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)

Here is part of my controller:

[Authorize]
public class UsersController : ApiController
{
    private readonly IUsersRepository _usersRepository;

    public UsersController(IUsersRepository usersRepository)
    {
        _usersRepository = usersRepository;
    }

    //This is working fine
    [HttpGet]
    [ResponseType(typeof(List<UserInfoDto>))]
    public IHttpActionResult Get()
    {
        IList<UserInfoDto> values = _usersRepository.SelectAll();

        if (values == null || !values.Any())
            return Ok();

        return Json(new { collection = values });
    }

    //here I get an error
    [HttpGet]
    [Route("api/test/{roleId}/books")]
    public IHttpActionResult FindUsersByRole(int roleId)
    {
        // association code

        return Ok("test");
    }
}

Here are the urls I'm calling:

localhost:81/api/Users - working fine

localhost:81/api/test/1/books - Error

I'm also posting my config classes:

Global.asax:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    GlobalConfiguration.Configure(WebApiConfig.Register);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    AutofacConfig.Register();
}

AutofacConfig:

public class AutofacConfig
{
    public static void Register()
    {
        var builder = new ContainerBuilder();

        builder.RegisterModule(new AutoMapperModule());
        builder.RegisterApiControllers(typeof(WebApiApplication).Assembly).PropertiesAutowired();
        builder.RegisterModule(new NlogModule());
        builder.RegisterModule(new RepositoryModule());

        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
        GlobalConfiguration.Configuration.DependencyResolver =
             new AutofacWebApiDependencyResolver(container);
    }
}

WebApiConfig:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        config.Filters.Add(new ExceptionHandlingAttribute());
    }
}

Startup:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        HttpConfiguration config = new HttpConfiguration();
        config.MapHttpAttributeRoutes();
        ConfigureOAuth(app);
        app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
        app.UseWebApi(config);
    }
}

Solution

  • You are using two different HttpConfiguration. The one provided to you by IIS in GlobalConfiguration and the one you manually created in Startup

    That is the cause of your problem.

    Remove all the HttpConfiguration code in Startup.cs as it is causing conflicts with IIS and OWIN. That is usually done for self hosted OWIN Web Api.

    Updated Startup

    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            ConfigureOAuth(app);
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);        
        }
    }
    

    everything else could remain as before.