Search code examples
c#asp.net-web-apiasp.net-mvc-5asp.net-web-api2attributerouting

Using WebAPI 2.2 inside of an existing ASP.NET MVC 5 project


I am using WebAPI 2.2, with Attribute Routing, inside of an existing MVC 5 project. I intend to migrate the entire website over to WebAPI, but it will take some time. I got everything working, but I am concerned I may be doing something wrong.

This SO post seems to suggest I should be calling GlobalConfiguration.Configure(WebApiConfig.Register) in the Global.asax.cs file.

If I simply remove the HttpConfiguration argument typically provided in WebApiConfig.Register(), and simply call GlobalConfiguration.Configure(x => x.MapHttpAttributeRoutes()) within the WebApiConfig.Register() method - the WebAPI endpoints respond with the desired results.

So this is what I end up with:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        WebApiConfig.Register();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}


class WebApiConfig
{
    public static void Register()
    {
        GlobalConfiguration.Configure(x => x.MapHttpAttributeRoutes());
    }
}

Is there anything wrong with this approach?


Solution

  • So it turns out there are simply two ways to fix the configuration issue that comes up when WebAPI 2.2 is added to an existing project. I was doing BOTH fixes which became clear to me when I read the code.

    The following:

    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            WebApiConfig.Register();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
    
    
    class WebApiConfig
    {
        public static void Register()
        {
            GlobalConfiguration.Configure(x => x.MapHttpAttributeRoutes());
        }
    }
    

    Is virtually the same as doing the following:

    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            //WebApiConfig.Register();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
    
    class WebApiConfig
    {
        public static void Register(HttpConfiguration configuration)
        {
            configuration.MapHttpAttributeRoutes();
        }
    }
    

    It seems I was simply experiencing a mental lapse :)

    It should have been obvious that GlobalConfiguration.Configure(x => x.MapHttpAttributeRoutes()) is basically doing the same thing as GlobalConfiguration.Configure(WebApiConfig.Register).

    It logically follows that these should produce the same result. Here is the Microsoft code for GlobalConfiguration in the System.Web.Http namespace:

    /// <summary>
    /// Provides a global <see cref="T:System.Web.Http.HttpConfiguration"/> for ASP.NET applications.
    /// </summary>
    public static class GlobalConfiguration
    {
        private static Lazy<HttpConfiguration> _configuration = CreateConfiguration();
    
        ///... code excluded for brevity
    
        /// <summary>
        /// Gets the global <see cref="T:System.Web.Http.HttpConfiguration"/>.
        /// </summary>
        public static HttpConfiguration Configuration
        {
            get { return _configuration.Value; }
        }
    
        /// <summary>
        /// Performs configuration for <see cref="GlobalConfiguration.Configuration"/> and ensures that it is
        /// initialized.
        /// </summary>
        /// <param name="configurationCallback">The callback that will perform the configuration.</param>
        public static void Configure(Action<HttpConfiguration> configurationCallback)
        {
            if (configurationCallback == null)
            {
                throw new ArgumentNullException("configurationCallback");
            }
    
            configurationCallback.Invoke(Configuration);
            Configuration.EnsureInitialized();
        }
    
        private static Lazy<HttpConfiguration> CreateConfiguration()
        {
            return new Lazy<HttpConfiguration>(() =>
            {
                HttpConfiguration config = new HttpConfiguration(new HostedHttpRouteCollection(RouteTable.Routes));
                ServicesContainer services = config.Services;
                Contract.Assert(services != null);
                services.Replace(typeof(IAssembliesResolver), new WebHostAssembliesResolver());
                services.Replace(typeof(IHttpControllerTypeResolver), new WebHostHttpControllerTypeResolver());
                services.Replace(typeof(IHostBufferPolicySelector), new WebHostBufferPolicySelector());
                services.Replace(typeof(IExceptionHandler),
                    new WebHostExceptionHandler(services.GetExceptionHandler()));
                return config;
            });
        }
    
        ///... code excluded for brevity
    }