How to Register Custom Routes in Sitecore 9 Helix

Or, how to fix "The object has not yet been initialized. Ensure that HttpConfiguration EnsureInitialized() is called"

Various guides on the net describe how to register routes in Sitecore and also plain old ASP.NET. However, in the context of Sitecore (especially newer versions), you may run into the following error:

{"Message":"An error has occurred.","ExceptionMessage":"The object has not yet been initialized. Ensure that HttpConfiguration.EnsureInitialized() is called in the application's startup code after all other initialization code.","ExceptionType":"System.InvalidOperationException","StackTrace":" at System.Web.Http.Routing.RouteCollectionRoute.get_SubRoutes()\r\n at System.Web.Http.Routing.RouteCollectionRoute.GetRouteData(String virtualPathRoot, HttpRequestMessage request)\r\n at System.Web.Http.WebHost.Routing.HttpWebRoute.GetRouteData(HttpContextBase httpContext)"}

In order to overcome this issue or prevent any future issues, make sure to register your routes properly.

Create the following files in your Helix project:

  • /App_Config/Include/Feature.config
  • RegisterFeatureRoute.cs
  • /Controllers/FeatureController.cs


/App_Config/Include/Feature.config


<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>
    <pipelines>
      <initialize>
        <processor type="Fishtank.Foundation.Feature.RegisterFeatureRoute, Fishtank.Foundation.Feature" patch:before="processor[@type='Sitecore.Mvc.Pipelines.Loader.InitializeRoutes, Sitecore.Mvc']" />
      </initialize>
    </pipelines>
  </sitecore>
</configuration>


RegisterFeatureRoute.cs


using System.Web.Http;
using System.Web.Routing;
using Sitecore.Pipelines;

namespace Fishtank.Foundation.Feature
{
    public class RegisterFeatuerRoute
    {
        public virtual void Process(PipelineArgs args)
        {
            RegisterRoute(RouteTable.Routes);
        }

        protected virtual void RegisterRoute(RouteCollection routes)
        {
            RouteTable.Routes.MapHttpRoute("routeName", 
                "your-route-here", /* do not include a forward slash in front of the route */
                new {controller = "SiteMap", action  = "Generate"} /* controller name should not have the "Controller" suffix */
            );
        }
    }
}


/Controllers/FeatureController.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Web.Http;
using Sitecore.Data.Items;
using Sitecore.Links;
using Sitecore.Mvc.Extensions;
using Sitecore.Services.Infrastructure.Web.Http;
using Sitecore.Sites;

namespace Fishtank.Foundation.Feature.Controllers
{
    // If you want to take advantage of Sitecore security, use the ServicesApiController. If you want to use the .NET controller, use ApiController
    public class FeatureController : ServicesApiController
    {
        [HttpGet]
        public IHttpActionResult Generate()
        {
            var resp = new HttpResponseMessage
            {
                Content = new StringContent("Some Content", Encoding.UTF8, "text/xml")
            };
			
			// Your code here

            return ResponseMessage(resp);
        }
	}
}

Consider the difference between the ServicesApiController and the ApiController class inheritance. For many simple APIs, the Sitecore ServicesApiController class wrapper will cause issues in distributed environments with CM and CD servers. If you want a controller that will work without extra considerations and configurations, use the standard .NET ApiController while noting the possible security issues that may arise.

Keep on coding,

Marcel

Fish