locked
Using weboptimizer with single wwwroot for multiple sites RRS feed

  • Question

  • User-1262787652 posted
    ASP.NET 5 MVC Core application serves multiple sites using LigerShark WebOptimizer ( https://github.com/ligershark/WebOptimizer )
    
        https://example.com/store1
        https://example.com/store2
        https://example2.com
    
    All those sites should served from wwwroot directory containing same files for those urls.
    Sites are defined in hosts.json file:
    
        {
          "EevaHosts": {
            "example.com/store1": {}
            "example.com/store2": {}
            "example2.com": {}
          }
        }
    
    I tried code below to force WebOptimizer to use same wwwwroot directory for every site in Debian Linux but got exception for https://example.com/store1/site.js
    
    > No files found matching "/store1/js/site.js" exist in
    > "/var/www/appbasedir/wwwroot/"
    
    How to force web optimizer to use same wwwwroot directory for all sites ?
    If Weboptimizer middleware is removed, static files are serverd properly.
    
    In StartUp.cs:
    
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        { ...
    
            var eevakonf = new ConfigurationBuilder().AddJsonFile("hosts.json").Build();
    
            foreach (var host1 in eevakonf.GetSection("EevaHosts").GetChildren())
            {
                if (!host1.Key.Contains("/"))
                    app.UseWebOptimizer();
                else
                    app.UseWebOptimizer(env, new FileProviderOptions[] { new FileProviderOptions()
                    {
                        // example.com/store1 -> /store1
                        RequestPath = new PathString(RequestPathExtract(host1)),
                        FileProvider = env.WebRootFileProvider
                    }
                    });
            }
    
            // Using single call causes the same exception:
            //HashSet<FileProviderOptions> fp = new();
            //foreach (var host1 in eevakonf.GetSection("EevaHosts").GetChildren())
            //{
            //    if (host1.Key.Contains("/"))
            //        fp.Add(new FileProviderOptions() {
            //            RequestPath = new PathString(RequestPathExtract(host1)) ,
            //            FileProvider = env.WebRootFileProvider
            //        });
            //}
            //app.UseWebOptimizer(env, fp.ToArray());
    
            foreach (var host in eevakonf.GetSection("EevaHosts").GetChildren())
            {
                if (!host.Key.Contains("/"))
                    app.UseStaticFiles();
                else
                {
                    app.UseStaticFiles(new StaticFileOptions
                    {
                        RequestPath = new PathString(RequestPathExtract(host))
                    });
                }
            }
          }
    
    
            static string RequestPathExtract(IConfigurationSection host)
            {
                return "/" + StrExtract(host.Key, "/");
            }
    
         static string StrExtract(string cSearchExpression, string cBeginDelim)
        {
            int nbpos = At(cBeginDelim, cSearchExpression);
            return cSearchExpression[(nbpos + cBeginDelim.Length - 1)..];
        }
    
    

    Monday, April 19, 2021 8:48 PM

All replies

  • User-474980206 posted

    your code makes no sense, nor does the title help much. you should not call

     app.UseWebOptimizer();

    or

     app.UseStaticFiles()

    more than once. also the order the middleware is installed is important, WebOptiomizer must come before the StaticFile middleware. Typically (if well written) middleware will detect its been registered, and ignore the additional adds.

    maybe you wanted a setting or environment variable that define the site, and you picked the settings for the site. but it would be better to just config each site correctly. 

    Monday, April 19, 2021 9:53 PM
  • User-1262787652 posted
    UseStaticFiles() 

    Does not accept multiple path bases. In my code multiple UseStaticFiles calls are running in chain and each call eliminates one par5th base from request.

    How to specify path bases /store1, /store2, /store3  and /   on single UseStaticFiles() call?

     app.UseWebOptimizer();

    accepts path bases as array. So it allows multiple path bases in single call but UseStaticFiles() accepts only one path base.

    Single application serves all sites at same time. So it is not possible to use environment variable to set single path base . As you see in code UseWebOptimizer() is called before UseStaticFiles().

    Monday, April 19, 2021 10:56 PM
  • User-474980206 posted

    Asp.net core only supports one www root file provider. You should only call each of the middleware once. Use folders in the root to separate sites.

    Tuesday, April 20, 2021 2:58 PM
  • User-1262787652 posted

    Multiple calls worked:

            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {  
                app.UseWebOptimizer();
                app.UseWebOptimizer(env, new FileProviderOptions[] { new FileProviderOptions()
                        {
                            RequestPath = new PathString("/store1"),
                            FileProvider = env.WebRootFileProvider
                        }
                        });
                app.UseWebOptimizer(env, new FileProviderOptions[] { new FileProviderOptions()
                        {
                            RequestPath = new PathString("/store2"),
                            FileProvider = env.WebRootFileProvider
                        }
                        });
                app.UseStaticFiles();
                app.UseStaticFiles(new StaticFileOptions
                {
                    RequestPath = new PathString("/store1")
                });
                app.UseStaticFiles(new StaticFileOptions
                {
                    RequestPath = new PathString("/store2")
                });

    Why wwwroot directory should duplicated for every site if this works OK ?

    There are large number of sites and wwwroot contains large number of files. Duplication looks not reasonable.

    Tuesday, April 20, 2021 4:50 PM
  • User-474980206 posted

    still do not understand what problem you are trying to solve.  if you want urls /store1/foo.js and /store2/foo.js to point to the same files, why not just use /foo.js?

     

    Tuesday, April 20, 2021 5:25 PM
  • User-1262787652 posted

    https://example.com/store1

    and

    https://example.com/store2

    are different web sites. They have different users, different databases, belong to different companies. 

    They are served by same application and share same in wwwroot directory.

    Addresses must be different:

    https://example.com/store1/js/foo.js

    and

    https://example.com/store2/js/foo.js

    Sites should refer files only by its own virtual path, /store1/...  and  /store2/.... 

    Both those addresses should return same optimized file,  wwwroot/js/foo.js 

    Tuesday, April 20, 2021 5:44 PM
  • User475983607 posted

    As far as I can tell you have two different domains that point to the same application.  There's no problem other than your understanding of hosting an application.  

    Tuesday, April 20, 2021 6:17 PM
  • User-1262787652 posted

    One domain, example.com  hosts different sites in different root paths.

    Addresses prefixed with

    https://example.com/site1

    belong to one site.

    Addresses prefixed with

    https://example.com/site2

    belond to other site.

    static files are in wwwroot directory. 

    I asked how to remove /site1 and /site2 prefixes from path so that wwwroot/js/foo.js file is returned for urls example.com/site1/js/foo.js and example.com/site2/foo.js

    Andrus.

    Tuesday, April 20, 2021 7:42 PM
  • User-474980206 posted

    if they are different website (deployments) why configure for all sites? if its multi-tenant in a single site (more common) why different static file routes?

     

    Tuesday, April 20, 2021 7:44 PM
  • User-1262787652 posted

    This maybe separate web site or multi-tenant site depending on customer requirements. Same application should work in all cases.

    Application should run if customer assigns it to main domain example.com  or to some virtual directory like  example.com/mygreatstore.

    Customer may also create multiple stores in same domain like  example.com/foodstore  and  example.com/shoesstore.

    It may also host multiple domains in same server like example1.com and example2.com.

    So it looks like all URLs used by  example.com/foodstore  should start with  example.com/foodstore

    and URLs used by  example.com/shoesstore  should start with  example.com/shoesstore

    json conf file is used to configure this.

    Is calling weboptimizer and static file handler middleware multiple times prohibited or useless. Or is it good practice to configure multiple sites for same application.

    WebOptimizer use method has overload which allows to specify array of path bases in single call. So maybe it can called once for multiple sites

    UseStaticFiles() accepts only single path base. So it should called multiple times.

    Thursday, April 22, 2021 7:19 PM
  • User-474980206 posted

    Still not clear what problem you are trying to solve. 

    assume wwwroot contains:

      /css/site.css
      /js/site.js

    you install the site a domain.com/site1, then the default paths are

         domain.com/site1/css/site.css
         domain.com/site1/js/site.js

    then you install your app to domain.com/site2, then the defaults are:

         domain.coms/site2/css/site.css
         domain.coms/site2/js/site.js

    if you do multi-tenant for domain.com via route variables then just src="~/js/site.js" 

         domain.com/tenant1/home/index
         domain.com/css/site.css
         domain.com/js/site.js

    if you do host headers

         tentant1.domain.com/home/index
         tentant1.domain.com/css/site.css
         tentant1.domain.com/js/site.js

    Thursday, April 22, 2021 7:38 PM
  • User-1262787652 posted

    I tried the following:

    apache.conf:

    <Location "/store1"> 
    RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME}
    ProxyPreserveHost On
    ProxyPass http://127.0.0.1:5000/store1
    ProxyPassReverse http://127.0.0.1:5000/store1
    </Location>
    ...
    <Location "/store5"> RequestHeader set "X-Forwarded-Proto" expr=%{REQUEST_SCHEME} ProxyPreserveHost On ProxyPass http://127.0.0.1:5000/store5 ProxyPassReverse http://127.0.0.1:5000/store5 </Location>

    StartUp.cs:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
    ...
    app.UseWebOptimizer();
    app.UseStaticFiles();
    ...
    }

    _Layout.cshtml:

    <link rel="stylesheet" href="~/css/site.css" />
    

    In browser it appears as

     <link rel="stylesheet" href="/store5/css/site.css" />

    Trying to load it using  example.com/store5/css/site.css  throws error 500 in WebOptimizer

     System.IO.FileNotFoundException: No files found matching "/store5/css/site.css" exist in "/var/www/store5/wwwroot/"
    at WebOptimizer.Asset.ExpandGlobs(IAsset asset, IWebHostEnvironment env)
    ...

    How to fix this so that hosting tenants in subdirectories

    example.com/store1/css/site.css 
    ..
    example.com/store5/css/site.css 

    all use same files in wwwroot ?

    Root directory of example.com may contain other application.

    Thursday, April 22, 2021 8:32 PM
  • User-474980206 posted

    Now that you explained what issue you have, it’s simple. When configuring a subsite, you need to set the base path

    https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.usepathbaseextensions.usepathbase?view=aspnetcore-5.0

    You can use a value from app settings or environment variable.

    Friday, April 23, 2021 2:41 PM
  • User-1262787652 posted

    Hi!

    Same services serves many base paths:

    example.com/store1, example.com/store2,   othersite.com/someotherstore,  storeinrootdirectory.com etc.

    Should UsePathBase called multiple times for every base path ?

    Andrus.

    Friday, April 23, 2021 6:33 PM
  • User-474980206 posted

    no, UseBasePath should only be called once.

    it looks like you are trying to do multi-tenant site via a proxy instead of the more common host headers.

    you should define proxy /common for static file and map in the service to /common to the static file handler. then you just use ~/common to access static files.

    then for sites, you should use the routing to extract the site as a variable from the route that is passed to the actions. 

    the other common solution is a url rewrite middleware that extracts the site from the url.  

    google asp.net core multi-tenant for more info.

     

    Sunday, April 25, 2021 12:42 AM