November Happy Hour will be moved to Thursday December 5th.

Issues getting ServiceApi to intall and start

Vote:
 

Hello,

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()\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)"
}


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. If anyone is internal at Opti, Ticket #1029270 was the most helpful one and has the base I'm building on.


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()

 

#324160
Edited, Jun 24, 2024 18:24
Vote:
 
#324648
Jun 28, 2024 12:27
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.