Experience Sitecore ! | All posts tagged 'Multisite'

Experience Sitecore !

More than 200 articles about the best DXP by Martin Miles

StackOverflow 2 questions on Multi-Site configuration: Multisite Best Practice for setting up Visual Studio Project and Manage web.config in multisite solution

Got to answer two question about multi-site configuration working with Sitecore:

1. Sitecore Multisite Best Practice for setting up Visual Studio Project (link to original question)

I'm seeking an advise on best practice that has worked for creating a asp.net MVC visual studio solution that supports multisite (multi tenant). One thing we would like to do is minimize the regression defects so that developer don't modify the wrong website code base etc.
That the solution needs to support more than 8 web sites.

2. Manage web.config in sitecore multisite solution (link to original question)

We have a multisite solution with individual visual studio solutions for each websites. Then we have master solution to build/deploy all websites.
Firstly, not sure whether it's a best practice to include web.config in Visual Studio solution. But I think all the nuget packages needs web.config to add their settings.
As a result, we have web.config for each solution. However when we deploy from master web.config gets overwritten by each sites. Could someone please suggest how this issue can be fixed?

Answers:

I have got one more alternative to address that. Currently working on a huge project, with dozens of developers and quite bureaucratic process of deployments, we have introduced the following approach:

  1. Project consists of multiple logical subparts, which in fact are individual websites.
  2. Each of those websites we set up as an MVC Area with own controllers, views etc
  3. Most important - we set up MVC Areas as individually pluggable DLLs. So each of that websites is a separate project under solution, with its own resources and statics; on build everything is copied under their required paths and DLL goes to the \bin folder.
  4. One more class library for shared (by all websites) resources, it is being referenced only by those sites, that need that functionality
  5. In Sitecore, we have the same strict principles - the content, layouts, renderings are isolated as higher as possible, no individual content may be re-used (unless from shared resources folder).

This approach serves us almost a year already and has proven for its agility, much easier deployment and fairly less code merge conflicts. If you are working under just one site - you don't need to re-compile and re-deploy everything - just replace one dll at bin folder.

Thus, combining that with your question, Approach 1 would be an answer.

References (more to read):

Regarding managing configuration, I would advise for each website (under its individual project) to create it's own App_Config/Include folder and create _project_name_.config within that folder in order to keep all site-specific settings there (for further merge into resulting config).

On build you set up (for each individual project) that file to be copied into main SITECORE_INSTANCE_WEB_ROOT\App_config\Include folder along with the rest of include patch config files.

Hope someone finds that helpful!

How to host several sites within the same Sitecore instance without specifying a hostname, just on different ports

Challenge: I have got a test server running, where I usually deploy early builds and proofs of concept for business users to acknowledge. Since recent solution has grown to several websites, so I want users to be able to access all of them. The problem occurs from that users do not have administrative permissions to their PCs and are not able to edit hosts file in order to set multiple host names for my server IP (most of them also are not aware how to do that). The good news is that despite being geographically distributed and being in different virtual networks, they do have access to the server IP address.

Unfortunately, I was not able to specify multiple host names (or IP addresses) to the server as a part of infrastructure configuration, so it became obvious that users should access those websites by IP address, moreover the same IP address to all sites. So what came first into my mind was to distinguish websites by ports within same IIS. Sounds good, but how to do that? I definitely knew that the it resolves website by the host name, not the port, as set within <sites> node of config file. I tried googling a solution but did not find anything...


Investigation: Thus, armed with Reflector and dotPeek tools I started investigating and debugging original code from Sitecore.Kernel.dll. Since a while I came across SiteResolver class, that serves a processor for httpRequestBegin pipeline:


    ...

    ...

So far, so good. The method doing resolving job is called ResolveSiteContext, so I already morally prepared to inherit from SiteResolver class and override that method, implementing site resolution by port.

But what was my excitement, when I notices that it calls SiteContextFactory.GetSiteContext passing hostname, file path and port! So it already supposes port coming from config file, doesn't it? Let's go and inspect this method:

    public static SiteContext GetSiteContext(string hostName, string fullPath, int portNumber)
    {
      fullPath = fullPath.ToLowerInvariant();
      foreach (SiteInfo info in SiteContextFactory.Sites)
      {
        if (info.Matches(hostName, fullPath, portNumber))
          return new SiteContext(info);
      }
      return (SiteContext) null;
    }

and Matches() method follows as:

    public bool Matches(string host, string folder, int portNumber)
    {
      return this.MatchesHost(host) && this.MatchesPort(portNumber) && this.MatchesFolder(folder);
    }

After looking at MatchesPort(portNumber) I chased portNumber and where it comes from. It occurred that parser expects attribute with name "port" and takes the value out of it, or sets default value(0).

Right, so in fact that proves that port number can be solely used for resolving site within same Sitecore instance. But why there is lack of references or documentation about that? In any case this blog post fills the gap, I hope.


Testing: Now it's time to test my assumption. I have created two bindings for my sitecore instance, for 80 and 8080 ports. Notice that there's no hostnames assigned:

Challenge: I have got a test server running, where I usually deploy early builds and proofs of concept for business users to acknowledge. Since recent solution has grown to several websites, so I want users to be able to access all of them. The problem occurs from that users do not have administrative permissions to their PCs and are not able to edit hosts file in order to set multiple host names for my server IP (most of them also are not aware how to do that). The good news is that despite being geographically distributed and being in different virtual networks, they do have access to the server IP address.

Unfortunately, I was not able to specify multiple host names (or IP addresses) to the server as a part of infrastructure configuration, so it became obvious that users should access those websites by IP address, moreover the same IP address to all sites. So what came first into my mind was to distinguish websites by ports within same IIS. Sounds good, but how to do that? I definitely knew that the it resolves website by the host name, not the port, as set within <sites> node of config file. I tried googling a solution but did not find anything...


Investigation: Thus, armed with Reflector and dotPeek tools I started investigating and debugging original code from Sitecore.Kernel.dll. Since a while I came across SiteResolver class, that serves a processor for httpRequestBegin pipeline:


    ...

    ...

So far, so good. The method doing resolving job is called ResolveSiteContext, so I already morally prepared to inherit from SiteResolver class and override that method, implementing site resolution by port.

But what was my excitement, when I notices that it calls SiteContextFactory.GetSiteContext passing hostname, file path and port! So it already supposes port coming from config file, doesn't it? Let's go and inspect this method:

    public static SiteContext GetSiteContext(string hostName, string fullPath, int portNumber)
    {
      fullPath = fullPath.ToLowerInvariant();
      foreach (SiteInfo info in SiteContextFactory.Sites)
      {
        if (info.Matches(hostName, fullPath, portNumber))
          return new SiteContext(info);
      }
      return (SiteContext) null;
    }

and Matches() method follows as:

    public bool Matches(string host, string folder, int portNumber)
    {
      return this.MatchesHost(host) && this.MatchesPort(portNumber) && this.MatchesFolder(folder);
    }

After looking at MatchesPort(portNumber) I chased portNumber and where it comes from. It occurred that parser expects attribute with name "port" and takes the value out of it, or sets default value(0).

Right, so in fact that proves that port number can be solely used for resolving site within same Sitecore instance. But why there is lack of references or documentation about that? In any case this blog post fills the gap, I hope.


Testing: Now it's time to test my assumption. I have created two bindings for my sitecore instance, for 80 and 8080 ports. Notice that there's no hostnames assigned:



Voila! Hope this hapens to be helpful and may save some efforts for you in future!

Within Sitecore Desktop I created two website landing pages for each of sites.
Here is te content for Primary website sitting on default 80 port:

and below is the same for Secondary website on 8080:


Finally, assign them in the config file. Notice, there is no hostname defined again. Only start item and port number:


Ok, now publish both sites to web database and try accessing them. The first one (primary) opens on default port 80 by simply entering IP address. Expected behavior! It shows content exactly as configured earlier:


Just aplyint port number (8080) to the same IP address we got routed to secondary website. As expected, again:


Voila! Hope this hapens to be helpful and may save some efforts for you in future!