Issue installing EPiServer.ServiceApi on Optimizely CMS 11 and Commerce 13 site

I originally posted this to the Optimizely Developer forum, but this issue has persisted for a long time. Wanted to re-post here on the off-chance someone can save me!

We have been dealing with this issue for awhile, and I went back to support ticket from a year ago and was able to progress a bit further than the forum post linked. We are on CMS 11 and Commerce 13, and I have installed the correct ServiceApi and ServiceApi.Commerce packages in the correct places, and have my Startup.cs class. The engineer from Optimizely in the ticket said the following:

“After some deliberation within teams, we managed to run the site. We saw that there was no XmlFormatterType returned in the MediaTypeFormatterCollection.
 
Please see the attached screenshot and the attached code snippet, customInit.cs.”

They provided this file, and

using EPiServer.Data.SchemaUpdates;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.ServiceApi;
using EPiServer.ServiceLocation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
using System.Web;
using System.Web.Http;

namespace CMS.Ellsworth
{
    [InitializableModule, ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class customInit : IConfigurableModule
    {


        /// <summary>
        /// Configure the IoC container before initialization.
        /// </summary>
        /// <param name="context">The context on which the container can be accessed.</param>
        public void ConfigureContainer(ServiceConfigurationContext context)
        {

        }

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <remarks>
        /// Gets called as part of the EPiServer Framework initialization sequence. Note that it will be called
        ///             only once per AppDomain, unless the method throws an exception. If an exception is thrown, the initialization
        ///             method will be called repeatedly for each request reaching the site until the method succeeds.
        /// </remarks>
        public void Initialize(InitializationEngine context)
        {
            GlobalConfiguration.Configure(config =>
            {
                config.Formatters.Add(new XmlMediaTypeFormatter());
            });
        }

        /// <summary>
        /// Preloads the module.
        /// </summary>
        /// <param name="parameters">The parameters.</param>
        /// <remarks>
        /// This method is only available to be compatible with "AlwaysRunning" applications in .NET 4 / IIS 7.
        ///             It currently serves no purpose.
        /// </remarks>
        public void Preload(string[] parameters)
        { }

        /// <summary>
        /// Resets the module into an uninitialized state.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <remarks>
        /// <para>
        /// This method is usually not called when running under a web application since the web app may be shut down very
        ///             abruptly, but your module should still implement it properly since it will make integration and unit testing
        ///             much simpler.
        /// </para>
        /// <para>
        /// Any work done by <see cref="M:EPiServer.Framework.IInitializableModule.Initialize(EPiServer.Framework.Initialization.InitializationEngine)"/> as well as any code executing on <see cref="E:EPiServer.Framework.Initialization.InitializationEngine.InitComplete"/> should be reversed.
        /// </para>
        /// </remarks>
        public void Uninitialize(InitializationEngine context)
        { }
    }
}

This gets me past the error I was getting about the ServiceApi not being initialized, but I get the following error

EPiServer.Framework.Initialization.InitializationException: Initialize action failed for Initialize on class EPiServer.ServiceApi.IntegrationInitialization, EPiServer.ServiceApi, Version=5.6.1.0, Culture=neutral, PublicKeyToken=8fe83dea738b45b7 ---> System.ArgumentException: A route named 'MS_attributerouteWebApi' is already in the route collection. Route names must be unique.
Parameter name: name
   at System.Web.Routing.RouteCollection.Add(String name, RouteBase item)
   at System.Web.Http.WebHost.Routing.HostedHttpRouteCollection.Add(String name, IHttpRoute route)
   at System.Web.Http.Routing.AttributeRoutingMapper.MapAttributeRoutes(HttpConfiguration configuration, IInlineConstraintResolver constraintResolver, IDirectRouteProvider directRouteProvider)
   at System.Web.Http.HttpConfigurationExtensions.MapHttpAttributeRoutes(HttpConfiguration configuration)
   at EPiServer.ServiceApi.IntegrationInitialization.<>c.<Initialize>b__2_0(HttpConfiguration config)
   at System.Web.Http.GlobalConfiguration.Configure(Action`1 configurationCallback)
   at EPiServer.ServiceApi.IntegrationInitialization.Initialize(InitializationEngine context)
   at EPiServer.Framework.Initialization.Internal.ModuleNode.<>c__DisplayClass2_0.<Initialize>b__0()
   at EPiServer.Framework.Initialization.Internal.ModuleNode.Execute(Action a, String key)
   at EPiServer.Framework.Initialization.Internal.ModuleNode.Initialize(InitializationEngine context)
   at EPiServer.Framework.Initialization.InitializationEngine.InitializeModules()
   --- End of inner exception stack trace ---
   at EPiServer.Framework.Initialization.InitializationEngine.InitializeModules()
   at EPiServer.Framework.Initialization.InitializationEngine.ExecuteTransition(Boolean continueTransitions)
   at EPiServer.Framework.Initialization.InitializationEngine.Initialize()
   at EPiServer.Framework.Initialization.InitializationModule.<>c.<FrameworkInitialization>b__7_0(InitializationEngine e)
   at EPiServer.Framework.Initialization.InitializationModule.EngineExecute(HostType hostType, Action`1 engineAction)
   at EPiServer.Framework.Initialization.InitializationModule.FrameworkInitialization(HostType hostType)
   at EPiServer.Global..ctor()
   at CMS.Ellsworth.EPiServerApplication..ctor()
   at ASP.global_asax..ctor() in 

There is a file, WebApiConfig.cs that does some HttpMapping and related stuff. It seems this is where the conflict is coming from. This is the existing code that is causing the above error:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Web;
using System.Web.Http;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using Newtonsoft.Json;

namespace CMS.Ellsworth.Business.Initialization
{
    [InitializableModule]
    [ModuleDependency(typeof(FrameworkInitialization))]
    public class WebApiConfig : IInitializableModule
    {
        public static string ApiRoute = "api";

        public void Initialize(InitializationEngine context)
        {
            // Enable Web API routing
            GlobalConfiguration.Configure(config =>
            {
                // Attribute routing
                config.MapHttpAttributeRoutes();

                var formatters = GlobalConfiguration.Configuration.Formatters;
                var jsonFormatter = formatters.JsonFormatter;
                var settings = jsonFormatter.SerializerSettings;

                var enumConverter = new Newtonsoft.Json.Converters.StringEnumConverter();
                jsonFormatter.SerializerSettings.Converters.Add(enumConverter);
                config.Formatters.Add(new BrowserJsonFormatter());
                settings.Formatting = Formatting.Indented;

                config.Formatters.Remove(config.Formatters.XmlFormatter);

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

        public void Uninitialize(InitializationEngine context)
        {

        }
    }
    public class BrowserJsonFormatter : JsonMediaTypeFormatter
    {
        public BrowserJsonFormatter()
        {
            this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
            this.SerializerSettings.Formatting = Formatting.Indented;
        }

        public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType)
        {
            base.SetDefaultContentHeaders(type, headers, mediaType);
            headers.ContentType = new MediaTypeHeaderValue("application/json");
        }
    }
}

I tried modifying a few lines in each file and got the site to start without throwing an exception in the IDE, but it displays this message in the webpage. I do call the EnsureInitialized() several times like it says.

{
  "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()rn   at System.Web.Http.Routing.RouteCollectionRoute.GetRouteData(String virtualPathRoot, HttpRequestMessage request)rn   at System.Web.Http.WebHost.Routing.HttpWebRoute.GetRouteData(HttpContextBase httpContext)"
}

Here is the modified code in both files that produced the above error

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using System.Web;
using System.Web.Http;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using Newtonsoft.Json;

namespace CMS.Ellsworth.Business.Initialization
{
    [InitializableModule]
    [ModuleDependency(typeof(FrameworkInitialization))]
    public class WebApiConfig : IInitializableModule
    {
        public static string ApiRoute = "api";

        public void Initialize(InitializationEngine context)
        {
            // Enable Web API routing
            GlobalConfiguration.Configure(config =>
            {
                // Attribute routing
                // config.MapHttpAttributeRoutes();

                var formatters = GlobalConfiguration.Configuration.Formatters;
                var jsonFormatter = formatters.JsonFormatter;
                var settings = jsonFormatter.SerializerSettings;

                var enumConverter = new Newtonsoft.Json.Converters.StringEnumConverter();
                jsonFormatter.SerializerSettings.Converters.Add(enumConverter);
                config.Formatters.Add(new BrowserJsonFormatter());
                settings.Formatting = Formatting.Indented;

                config.Formatters.Remove(config.Formatters.XmlFormatter);

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

                config.EnsureInitialized();
            });
        }

        public void Uninitialize(InitializationEngine context)
        {
        }
    }

    public class BrowserJsonFormatter : JsonMediaTypeFormatter
    {
        public BrowserJsonFormatter()
        {
            this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
            this.SerializerSettings.Formatting = Formatting.Indented;
        }

        public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType)
        {
            base.SetDefaultContentHeaders(type, headers, mediaType);
            headers.ContentType = new MediaTypeHeaderValue("application/json");
        }
    }
}

using System.Net.Http.Formatting;
using System.Web.Http;
using EPiServer.Framework;
using EPiServer.Framework.Initialization;
using EPiServer.ServiceLocation;

namespace CMS.Ellsworth.Infrastructure
{
    [InitializableModule, ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
    public class ServiceInit : IConfigurableModule
    {
        /// <summary>
        /// Configure the IoC container before initialization.
        /// </summary>
        /// <param name="context">The context on which the container can be accessed.</param>
        public void ConfigureContainer(ServiceConfigurationContext context)
        {
        }

        /// <summary>
        /// Initializes this instance.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <remarks>
        /// Gets called as part of the EPiServer Framework initialization sequence. Note that it will be called
        ///             only once per AppDomain, unless the method throws an exception. If an exception is thrown, the initialization
        ///             method will be called repeatedly for each request reaching the site until the method succeeds.
        /// </remarks>
        public void Initialize(InitializationEngine context)
        {
            GlobalConfiguration.Configure(config =>
            {
                config.Formatters.Add(new XmlMediaTypeFormatter());
                config.EnsureInitialized();
            });
        }

        /// <summary>
        /// Preloads the module.
        /// </summary>
        /// <param name="parameters">The parameters.</param>
        /// <remarks>
        /// This method is only available to be compatible with "AlwaysRunning" applications in .NET 4 / IIS 7.
        ///             It currently serves no purpose.
        /// </remarks>
        public void Preload(string[] parameters)
        {
        }

        /// <summary>
        /// Resets the module into an uninitialized state.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <remarks>
        /// <para>
        /// This method is usually not called when running under a web application since the web app may be shut down very
        ///             abruptly, but your module should still implement it properly since it will make integration and unit testing
        ///             much simpler.
        /// </para>
        /// <para>
        /// Any work done by <see cref="M:EPiServer.Framework.IInitializableModule.Initialize(EPiServer.Framework.Initialization.InitializationEngine)"/> as well as any code executing on <see cref="E:EPiServer.Framework.Initialization.InitializationEngine.InitComplete"/> should be reversed.
        /// </para>
        /// </remarks>
        public void Uninitialize(InitializationEngine context)
        {
        }
    }
}

I’ve worked on this for weeks and we’ve had numerous support tickets over the last year or so trying to figure this out. We had Optimizely Expert Services was gracious enough to spend 5 hours free-of-charge to look into this, but they were unsuccessful.

Lastly, if I do add back in the config.MapHttpAttributeRoutes(); into the WebApiConfig.cs I get the following error: I can also confirm there is nowhere else in the codebase that calls that method or does any similar route mapping. I even commented out the RouteRegister in the Global.asax.cs.

EPiServer.Framework.Initialization.InitializationException: Initialize action failed for Initialize on class EPiServer.ServiceApi.IntegrationInitialization, EPiServer.ServiceApi, Version=5.6.1.0, Culture=neutral, PublicKeyToken=8fe83dea738b45b7 ---> System.ArgumentException: A route named 'MS_attributerouteWebApi' is already in the route collection. Route names must be unique.
Parameter name: name
   at System.Web.Routing.RouteCollection.Add(String name, RouteBase item)
   at System.Web.Http.WebHost.Routing.HostedHttpRouteCollection.Add(String name, IHttpRoute route)
   at System.Web.Http.Routing.AttributeRoutingMapper.MapAttributeRoutes(HttpConfiguration configuration, IInlineConstraintResolver constraintResolver, IDirectRouteProvider directRouteProvider)
   at System.Web.Http.HttpConfigurationExtensions.MapHttpAttributeRoutes(HttpConfiguration configuration)
   at EPiServer.ServiceApi.IntegrationInitialization.<>c.<Initialize>b__2_0(HttpConfiguration config)
   at System.Web.Http.GlobalConfiguration.Configure(Action`1 configurationCallback)
   at EPiServer.ServiceApi.IntegrationInitialization.Initialize(InitializationEngine context)
   at EPiServer.Framework.Initialization.Internal.ModuleNode.<>c__DisplayClass2_0.<Initialize>b__0()
   at EPiServer.Framework.Initialization.Internal.ModuleNode.Execute(Action a, String key)
   at EPiServer.Framework.Initialization.Internal.ModuleNode.Initialize(InitializationEngine context)
   at EPiServer.Framework.Initialization.InitializationEngine.InitializeModules()

 

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật