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:
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>();
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.
@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.
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
In Quicksilver 3.0+ the configuration has changed, you should use this
app.AddCmsAspNetIdentity();
// Use cookie authentication, ApplicationUser>(
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString(“/util/login.aspx”),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => manager.GenerateUserIdentityAsync(user))
}
});
// Enable bearer token authentication using ASP.NET Identity for Service Api, ApplicationUser>();
app.UseServiceApiIdentityTokenAuthorization
instead
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.
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
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.
Hi, apologies for a late reply. That’s unfortunately not enough to identify the issue. Do you have more log?