Quicksilver – ServiceApi: fixing issues

From what I heard, developers seem to love both QuickSilver – as a template – and ServiceApi – as a REST server – very much. Despite of being relatively new on the field, they are being used quite frequently – QuickSilver is the preferred choice for MVC template while many sites are using ServiceApi to update the catalogs. What’s about the combination of these two? Would it be the best-of-bread for Episerver Commerce. I would say yes, but after you have fixed the issue.

The installation of ServiceApi.Commerce package to Quicksilver site should be easy and painless. Update the database and build the project, you should be expecting to have a working site.

Not quite. You should be seeing this error:

A route named 'MS_attributerouteWebApi' is already in the route collection. Route names must be unique.
Parameter name: name

It’s because

MapHttpAttributeRoutes

is called twice (as it’s called in ServiceApi as well). So naturally, let’s try by commenting that line in SiteInitalization.cs, and build it again.

This can also be solved by adding this into appSettings:
"episerver:serviceapi:maphttpattributeroutes"

Which will signal ServiceAPI to skip calling

MapHttpAttributeRoutes

A new error would show up:

The following errors occurred while attempting to load the app.
- The OwinStartup attribute discovered in assembly 'EPiServer.Reference.Commerce.Site' referencing startup type 'EPiServer.Reference.Commerce.Site.Infrastructure.Owin.Startup' conflicts with the attribute in assembly 'EPiServer.ServiceApi' referencing startup type 'EPiServer.ServiceApi.Startup' because they have the same FriendlyName ''. Remove or rename one of the attributes, or reference the desired type directly.
To disable OWIN startup discovery, add the appSetting owin:AutomaticAppStartup with a value of "false" in your web.config.
To specify the OWIN startup Assembly, Class, or Method, add the appSetting owin:AppStartup with the fully qualified startup class or configuration method name in your web.config.

 

This is, again, caused by some duplication in QuickSilver and ServiceApi – the OwinStartupAttribute. We need to change this line in Startup.cs

To add the friendly name for the OwinStartupAttribute

[assembly: OwinStartupAttribute("quicksilver", typeof(EPiServer.Reference.Commerce.Site.Infrastructure.Owin.Startup))]

And add this to appSettings section in web.config

  <add key="owin:appStartup" value="quicksilver" />

Build it again. It should be working this time! No YSOD, which is good, still, there is something not working:

An error has occurred.

The object has not yet been initialized. Ensure that HttpConfiguration.EnsureInitialized() is called in the application's startup code after all other initialization code.

System.InvalidOperationException

at System.Web.Http.Routing.RouteCollectionRoute.get_SubRoutes() at System.Web.Http.Routing.RouteCollectionRoute.GetRouteData(String virtualPathRoot, HttpRequestMessage request) at System.Web.Http.WebHost.Routing.HttpWebRoute.GetRouteData(HttpContextBase httpContext)

This error happens because GlobalConfiguration.Configure were called multiple times – both in ServiceApi and QuickSilver. The solution? This code in SiteInitialization.cs should be changed from:

GlobalConfiguration.Configure(config =>
{
    config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.LocalOnly;
    config.Formatters.JsonFormatter.SerializerSettings = new JsonSerializerSettings();
    config.Formatters.XmlFormatter.UseXmlSerializer = true;
    config.DependencyResolver = new StructureMapResolver(context.Container);
    config.MapHttpAttributeRoutes();
});

To simply

GlobalConfiguration.Configuration.DependencyResolver = new StructureMapResolver(context.Container);

Now it’s working:

Quicksilver is working, again, now with ServiceApi
Quicksilver is working, again, now with ServiceApi

Is it done? No, not yet. By configuring QuickSilver’s Startup to be run, we are surpassing the ServiceApi’s Startup. We need to include the code somehow – so we can configure the path for ServiceApi token

This needs to be in Quicksilver Startup.Configuration:

            app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
            {
                AllowInsecureHttp = true,

                TokenEndpointPath = new PathString("/episerverapi/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(10),
                Provider = ServiceLocator.Current.GetInstance<IOAuthAuthorizationServerProvider>()
            });

            // token consumption
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

In QuickSilver 3.0+, this has been changed and should be

            app.AddCmsAspNetIdentity<ApplicationUser>();

            // Use cookie authentication
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/util/login.aspx"),
                Provider = new CookieAuthenticationProvider
                {
                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager<ApplicationUser>, ApplicationUser>(
                        validateInterval: TimeSpan.FromMinutes(30),
                        regenerateIdentity: (manager, user) => manager.GenerateUserIdentityAsync(user))
                }
            });

            // Enable bearer token authentication using ASP.NET Identity for Service Api
            app.UseServiceApiIdentityTokenAuthorization<ApplicationUserManager<ApplicationUser>, ApplicationUser>();

9 thoughts on “Quicksilver – ServiceApi: fixing issues

  1. I have problem with Quicksilver 10.4.3 with serviceapi 3.0.
    duplicate:
    I download Quicksilver version 10.4.3 (https://github.com/episerver/Quicksilver).
    setup EpiServer.ServiceApi.Commerce and follow your guide.
    error:
    There is no configuration specified for Microsoft.Owin.Security.OAuth.IOAuthAuthorizationServerProvider
    error line: Line 89: app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
    I change:
    app.UseServiceApiMembershipTokenAuthorization(new ServiceApiTokenAuthorizationOptions
    {
    AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60)
    });
    and use /episerverapi/token to get token return error: “Default Membership Provider must be specified.”
    client.BaseAddress = new Uri(“https://ecom4.dev.nis:433”);
    var fields = new Dictionary
    {
    { “grant_type”, “password” },
    { “username”, “sy.nguyen” },
    { “password”, “Jenny@211184” }
    };
    try
    {
    var response = client.PostAsync(“/episerverapi/token”, new FormUrlEncodedContent(fields)).Result;
    if (response.StatusCode == HttpStatusCode.OK)
    {
    var content = response.Content.ReadAsStringAsync().Result;
    token = Newtonsoft.Json.Linq.JObject.Parse(content).GetValue(“access_token”);
    }

    }
    i guess cause of problem is missing some configure. please help me. Many thanks.

    1. @Sy Nguyen: Please contact Episerver developer support service. I’d like to help, but I don’t know the problem already and this is outside my ability to spend time working on it.

  2. I added the source code into the Startup.cs as your suggestion :
    app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
    {
    AllowInsecureHttp = true,

    TokenEndpointPath = new PathString(“/episerverapi/token”),
    AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(10),
    Provider = ServiceLocator.Current.GetInstance()
    });

    // token consumption
    app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

    but the error message occurred as :

    Server Error in ‘/’ Application.

    No default Instance is registered and cannot be automatically determined for type ‘Microsoft.Owin.Security.OAuth.IOAuthAuthorizationServerProvider’

    There is no configuration specified for Microsoft.Owin.Security.OAuth.IOAuthAuthorizationServerProvider

    1.) Container.GetInstance(Microsoft.Owin.Security.OAuth.IOAuthAuthorizationServerProvider)

    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

    Exception Details: StructureMap.StructureMapConfigurationException: No default Instance is registered and cannot be automatically determined for type ‘Microsoft.Owin.Security.OAuth.IOAuthAuthorizationServerProvider’

    There is no configuration specified for Microsoft.Owin.Security.OAuth.IOAuthAuthorizationServerProvider

    1.) Container.GetInstance(Microsoft.Owin.Security.OAuth.IOAuthAuthorizationServerProvider)

    Source Error:

    Line 82: //EnableGoogleAccountLogin(app);
    Line 83:
    Line 84: app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
    Line 85: {
    Line 86: AllowInsecureHttp = true,

    Source File: C:\DemoSites\Quicksilver-master – Addon\Sources\EPiServer.Reference.Commerce.Site\Infrastructure\Owin\Startup.cs Line: 84

    Stack Trace:

    [StructureMapConfigurationException: No default Instance is registered and cannot be automatically determined for type ‘Microsoft.Owin.Security.OAuth.IOAuthAuthorizationServerProvider’

    There is no configuration specified for Microsoft.Owin.Security.OAuth.IOAuthAuthorizationServerProvider

    1.) Container.GetInstance(Microsoft.Owin.Security.OAuth.IOAuthAuthorizationServerProvider)
    ]
    StructureMap.SessionCache.GetDefault(Type pluginType, IPipelineGraph pipelineGraph) in c:\BuildAgent\work\a395dbde6b793293\src\StructureMap\SessionCache.cs:63
    StructureMap.Container.GetInstance(Type pluginType) in c:\BuildAgent\work\a395dbde6b793293\src\StructureMap\Container.cs:337
    EPiServer.ServiceLocation.ServiceLocatorImplBase.GetInstance(Type serviceType, String key) +55

    [ActivationException: Activation error occurred while trying to get instance of type IOAuthAuthorizationServerProvider, key “”]
    EPiServer.ServiceLocation.ServiceLocatorImplBase.GetInstance(Type serviceType, String key) +156
    EPiServer.ServiceLocation.ServiceLocatorImplBase.GetInstance() +62
    EPiServer.Reference.Commerce.Site.Infrastructure.Owin.Startup.Configuration(IAppBuilder app) in C:\DemoSites\Quicksilver-master – Addon\Sources\EPiServer.Reference.Commerce.Site\Infrastructure\Owin\Startup.cs:84

    [TargetInvocationException: Exception has been thrown by the target of an invocation.]
    System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) +0
    System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) +128
    System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +146
    Owin.Loader.c__DisplayClass12.b__b(IAppBuilder builder) +93
    Owin.Loader.c__DisplayClass1.b__0(IAppBuilder builder) +209
    Microsoft.Owin.Host.SystemWeb.OwinAppContext.Initialize(Action`1 startup) +843
    Microsoft.Owin.Host.SystemWeb.OwinBuilder.Build(Action`1 startup) +51
    Microsoft.Owin.Host.SystemWeb.OwinHttpModule.InitializeBlueprint() +101
    System.Threading.LazyInitializer.EnsureInitializedCore(T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory) +137
    Microsoft.Owin.Host.SystemWeb.OwinHttpModule.Init(HttpApplication context) +172
    System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +618
    System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +172
    System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +402
    System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +343

    [HttpException (0x80004005): Exception has been thrown by the target of an invocation.]
    System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +539
    System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +125
    System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +731

    1. In Quicksilver 3.0+ the configuration has changed, you should use this

      app.AddCmsAspNetIdentity();

      // Use cookie authentication
      app.UseCookieAuthentication(new CookieAuthenticationOptions
      {
      AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
      LoginPath = new PathString(“/util/login.aspx”),
      Provider = new CookieAuthenticationProvider
      {
      OnValidateIdentity = SecurityStampValidator.OnValidateIdentity, ApplicationUser>(
      validateInterval: TimeSpan.FromMinutes(30),
      regenerateIdentity: (manager, user) => manager.GenerateUserIdentityAsync(user))
      }
      });

      // Enable bearer token authentication using ASP.NET Identity for Service Api
      app.UseServiceApiIdentityTokenAuthorization, ApplicationUser>();

      instead

    1. I’d say probably no. It was there for a reason and we would not want it to be override-able.

      Btw – the MVC controllers work very well in Quicksilver. If you are using the controllers for C in MVC, then it should be no problem. But if you want to have some RESTful APIs built on WebAPI, then it’s another story.

  3. Hi Quan,

    today I tried checkout the latest QuickSilver from https://github.com/episerver/Quicksilver
    I installed EPiServer.ServiceApi.Commerce without any 3rd package, but always fail when try to access to https://myhost/episerverapi/token
    it often occurs when install EPiServer.ServiceApi.Commerce, for this time I also tried many time & fixed as your suggestion.
    I dont know why always feel unstable with EPiServer :), when my system is integrating with Kentico, Siteco,SharePoint,… beside EPiServer but never facing confusion issues as EPiServer even install packages of EPiServer without 3rd

    can you give me suggestion to make this more stable for any installing ?

    Thanks

  4. Hi Quan,
    It’s not with quicksilver though. But when we I install ServiceAPI for commerce, I get the below error while running the site.
    *****************
    [InitializationException: Initialize action failed for Initialize on class EPiServer.ServiceApi.IntegrationInitialization, EPiServer.ServiceApi, Version=5.6.1.0, Culture=neutral, PublicKeyToken=8fe83dea738b45b7]
    EPiServer.Framework.Initialization.InitializationEngine.InitializeModules() +872
    EPiServer.Framework.Initialization.InitializationEngine.ExecuteTransition(Boolean continueTransitions) +194
    EPiServer.Framework.Initialization.InitializationModule.EngineExecute(HostType hostType, Action`1 engineAction) +879
    EPiServer.Framework.Initialization.InitializationModule.FrameworkInitialization(HostType hostType) +226
    EPiServer.Global..ctor() +42
    Episite.Web.CMS.EPiServerApplication..ctor() +43
    ASP.global_asax..ctor() in c:\Users\test\AppData\Local\Temp\Temporary ASP.NET Files\vs\165cbfa8\5619ec34\App_global.asax.0.cs:0

    [TargetInvocationException: Exception has been thrown by the target of an invocation.]
    System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0
    System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) +142
    System.Activator.CreateInstance(Type type, Boolean nonPublic) +107
    System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, StackCrawlMark& stackMark) +1476
    System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes) +186
    System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture) +28
    System.Web.HttpRuntime.CreateNonPublicInstance(Type type, Object[] args) +80
    System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +182
    System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +369

    [HttpException (0x80004005): Exception has been thrown by the target of an invocation.]
    System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +532
    System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +111
    System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +724
    *****************
    Can you please advice what would be wrong here?
    Thanks.

    1. Hi, apologies for a late reply. That’s unfortunately not enough to identify the issue. Do you have more log?

Leave a Reply

Your email address will not be published. Required fields are marked *