write product url without httpContext or outside an EPiServer page

Vote:
 

Hi

I have run into some issues with writing product urls

in my setup I have a async EPiServer scheduled task, that handles indexing content and products,

in this task I would like to write urls onto my index so that I dont have to write the end user urls everytime I search.

But I get a HttpContext can not be null exception when trying to write the urls with UrlResolver.GetUrl()

 

I have tried all the different options on the UrlResolver for writing urls but all with the same result.

 

Then I reverted to storing the ContentReference in my index and tried to handle the url writing in my ServiceStack service that handles searches.

This time around all content urls are written correctly but I can't get it to write product urls correctly.

I use the same ContentReference from the index for getting the product on EPiServer pages and there it works perfectly but I cant get it to write urls in the ServiceStack Service

 

Anybody else tried something similar?

#80548
Jan 24, 2014 13:37
Vote:
 

The same issue came up in this thread: http://world.episerver.com/Modules/Forum/Pages/Thread.aspx?id=79015

I suggested a workaround but apparently it was not enough in that case, but maybe it is for you?

We need to dig a bit deeper into the issue from that thread and it is now handled as a support case.

#80564
Jan 25, 2014 8:57
Vote:
 

Thanks for the help Magnus, I will try out your suggestions monday, the context fix could be a good and easy way to go. 

#80569
Jan 26, 2014 0:00
Vote:
 

Hi Magnus

I solved my issue with parts of the suggestions you linked to and a bit of debugging and write custom handling.

I started by configuring the HttpContextBase registration as you pointed out in the thread you linked by adding the following module

[ModuleDependency(typeof(EPiServer.Commerce.Initialization.InitializationModule))]
public class FixUrlResolverHttpContext : IConfigurableModule
{
  public void Initialize(InitializationEngine context) {}
  public void Preload(string[] parameters) {}
  public void Uninitialize(InitializationEngine context) {}
  public void ConfigureContainer(ServiceConfigurationContext context)
  {
  context.Container.Configure(
    c =>
    c.For<ServiceAccessor<HttpContextBase>>()
      .Use(() => HttpContext.Current != null ? new HttpContextWrapper(HttpContext.Current) : null));
  }
}

    

In the code below I try to write the url with the normal handling if that fails i revert to resolving the url with the PartialRouter

this means that I am able to use the normal handling of url's in when I am on EPiServer pages, but revert to custom handeling when out of the EPiServer context.

 

    public class CustomUrlResolver
    {
        public Url GetUrl<TContent>(TContent content) where TContent : IContent, ILocalizable
        {
            Url resultUrl = null;

            var urlResolver = ServiceLocator.Current.GetInstance<UrlResolver>();
            var httpContext = ServiceLocator.Current.GetInstance<HttpContextBase>();
            var siteDefinitionRepository = ServiceLocator.Current.GetInstance<SiteDefinitionRepository>();

            string urlString = urlResolver.GetUrl(content.ContentLink, content.Language.Name, new VirtualPathArguments
            {
                ContextMode = ContextMode.Default,
                RequestContext =
                    new RequestContext
                    {
                        HttpContext = httpContext,
                        RouteData = new RouteData()
                    },
                RouteValues = new RouteValueDictionary()
            });

            if (string.IsNullOrEmpty(urlString) && content is CatalogContentBase)
            {
                var routeHandler = ServiceLocator.Current.GetInstance<PartialRouteHandler>();
                var routers = routeHandler.GetOutgoingRouters(typeof(CatalogContentBase));

                foreach (var partialRouter in routers)
                {
                    var partialRouteData = partialRouter.GetPartialVirtualPath(content, content.Language.Name,
                        new RouteValueDictionary
                        {
                            {RoutingConstants.NodeKey, content.ContentLink.ToReferenceWithoutVersion()},
                            {RoutingConstants.LanguageKey, content.Language.Name},
                        }, new RequestContext
                        {
                            RouteData = new RouteData(),
                            HttpContext = httpContext
                        });

                    if (partialRouteData != null)
                    {
                        //NOTE: EPiServer can't find StartPage of site when out of context so we set the correct start page
                        if (ContentReference.IsNullOrEmpty(partialRouteData.BasePathRoot))
                            partialRouteData.BasePathRoot = siteDefinitionRepository.List().First().StartPage;

                        if (!string.IsNullOrWhiteSpace(partialRouteData.PartialVirtualPath))
                        {
                            //NOTE: we dont want edit urls' in our index so we use ContextMode.Default
                            var baseUrl = urlResolver.GetUrl(partialRouteData.BasePathRoot, content.Language.Name, new VirtualPathArguments
                            {
                                ContextMode = ContextMode.Default,
                                RequestContext =
                                    new RequestContext
                                    {
                                        HttpContext = httpContext,
                                        RouteData = new RouteData()
                                    },
                                RouteValues = new RouteValueDictionary()
                            });

                            // combine base url with virtualPath
                            urlString = baseUrl + partialRouteData.PartialVirtualPath;
                            break;
                        }
                    }
                }
            }

            if (!string.IsNullOrWhiteSpace(urlString))
            {
                // remove hostname and protocol if included, this can be skipped if you want to use absolute urls
                resultUrl = new Url(new Url(urlString).GetComponents(UriComponents.PathAndQuery, UriFormat.SafeUnescaped));
            }

            return resultUrl;
        }
    }

    

There are quite a lot of places in the Routing that EPiServer used HttpContext.Current and reverts to default values if current is null

Couldn't all HttpContext.Current be replaced by ServiceLocator.Current.GetInstance<HttpContextBase>() I think that could solve the issue, or at least make i possible to fake the HttpContext when something failes in routing.

 

Best Regards

Thomas.

 

#80675
Jan 29, 2014 10:36
Vote:
 

Thanks for posting your workaround so it can benefit others, and demonstrate to us how cumbersome it is to do this so we can improve. We have both the ServiceAccessor<HttpContext> issue and the get-URL-without-template issue registered as bugs and I hope they will be fixed soon.

I haven't looked at the usages of HttpContext.Current but I know that there may be some heavily used execution paths where the container lookup actually impacts performance to such a degree that it motivates using the static accessor directly, however I doubt that that is the case everywhere if it is used in many places.

#80696
Jan 30, 2014 8:47
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.