Using a wwwroot folder (ASP.NET Core style) in ASP.NET 4.5 project
Asked Answered
D

3

34

I quite like the approach of the new asp.net (asp.net 5\core 1.0) web apps with the wwwroot folder being the root and only static files in there being served up.

Is possible through routing or other configuration to have a wwwroot folder in an asp.net 4.5 project behave in a similar way, such that static files are only served out of it, and it's the "root" of the webapp for static files?

(Part of my motivation in asking this is that I have an angular app hosted in an asp.net 5 project in VS2015, but I need to move this into an asp.net 4.5 project, but would like to keep the existing structure on disk)

Attempt using OWIN

I've attempted this using an empty ASP.NET 4.5 web app project and OWIN. So my folder structure has my angular app, with a main index.html file in a wwwroot folder in the project folder. There are no HTML files in the root of the project.

I've added OWIN through nuget and the following startup file:

[assembly: OwinStartup(typeof(MyApp.UI.Startup))]
namespace MyApp.UI
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            string root = AppDomain.CurrentDomain.BaseDirectory;
            var physicalFileSystem = new PhysicalFileSystem(Path.Combine(root, "wwwroot"));
            var options = new FileServerOptions
            {
                EnableDefaultFiles = true,
                FileSystem = physicalFileSystem
            };
            options.StaticFileOptions.FileSystem = physicalFileSystem;
            options.StaticFileOptions.ServeUnknownFileTypes = false;
            options.DefaultFilesOptions.DefaultFileNames = new[] {"index.html"};
            app.UseFileServer(options);
        }
    }
}

This fails though - the source of my index.html file does load when I run this, but all the css, js, etc files that it references fail with a 404. Even worse, if I append gulpfile.js onto the root url it loads my gulp file from the root of the project folder. This is precisely the sort of thing I'm trying to avoid.

Any ideas?

Digiacomo answered 29/3, 2016 at 20:9 Comment(0)
D
41

I believe I have a working method for doing this now. Took a bit of googling and experimentation, but in the end I came up with the following process:

  1. Create a new ASP.NET 4.5 project in VS2015, selecting the Empty Template

  2. Add OWIN references through nuget (Install-Package Microsoft.Owin.Host.SystemWeb and Microsoft.Owin.StaticFiles)

  3. Add a startup file similar to this:

    [assembly: OwinStartup(typeof(MyApp.Startup))]
    namespace MyApp.UI
    {
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                string root = AppDomain.CurrentDomain.BaseDirectory;
                var physicalFileSystem = new PhysicalFileSystem(Path.Combine(root, "wwwroot"));
                var options = new FileServerOptions
                {
                    RequestPath = PathString.Empty,
                    EnableDefaultFiles = true,
                    FileSystem = physicalFileSystem
                };
                options.StaticFileOptions.FileSystem = physicalFileSystem;
                options.StaticFileOptions.ServeUnknownFileTypes = false;
                app.UseFileServer(options);
            }
        }
    }
    
  4. Add the following to your web.config file, to prevent IIS from serving static files you don't want it to, and force everything through the OWIN pipeline:

    <system.webServer>
        <handlers>
          <remove name="StaticFile"/>
          <add name="Owin" verb="" path="*" type="Microsoft.Owin.Host.SystemWeb.OwinHttpHandler, Microsoft.Owin.Host.SystemWeb"/>
        </handlers>
      </system.webServer>
    

I'm always open to better suggestions on how to do this, but this at least seems to work for me.

Digiacomo answered 30/3, 2016 at 1:14 Comment(3)
3 months later... are you still using this? How well did it turn out?Boling
Yes, still using this and it works pretty much as intended. Keeps things relatively nice and clean for a site built on angular 2 where everything is built through gulp into the wwwroot folder. It's currently in use in production for said site\web app, but it's a fairly small site so it's not exactly getting hammered or anything.Digiacomo
It is exactly what I was looking for. Thanks for that.Whenever
T
13

Although OWIN worked for me in my dev environment running in VS 2017, it didn't work once deployed to azure. We run a SPA as well, and are storing the webpack output in ~/wwwroot/ but wanted it to load as if it was in the project root ~/ just like .net core webapi does it. I accomplished it by using just the URL rewrite shown below:

<system.webServer>
<rewrite>
  <rules>
    <rule name="wwwRootFix" stopProcessing="true">
      <match url="(.*)" />
      <conditions logicalGrouping="MatchAll">
        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
        <add input="{APPL_PHYSICAL_PATH}wwwroot\{R:1}" matchType="IsFile" />
        <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
        <add input="{REQUEST_URI}" pattern="^/(wwwroot)" negate="true" />
      </conditions>
      <action type="Redirect" url="/wwwroot/{R:1}" />
    </rule>
    <rule name="React Routes" stopProcessing="true">
      <match url=".*" />
      <conditions logicalGrouping="MatchAll">
        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
        <add input="{REQUEST_URI}" pattern="^/(api)" negate="true" />
        <add input="{REQUEST_URI}" pattern="^/(wwwroot)" negate="true" />
      </conditions>
      <action type="Rewrite" url="/wwwroot/index.html" />
    </rule>
  </rules>
</rewrite>
</system.webServer>

The first rule ensures that the file you are looking for doesn't exist in the root, isn't a folder, is found in the new path, isn't the wwwroot folder, nor is it part of the api. If ALL of those conditions are met, it tries to load the data from wwwroot.

The second rule checks to make sure you aren't trying to load the api, or a file that actually exists at the root. If both conditions are met, it loads the default SPA html document (in our case we use react).

Effectively this allows react-router 4 to handle all other routes after all the other conditions above have failed to find a match, and make it so it doesn't throw a 404 error when trying to load one of the react-router routes.

Tensile answered 20/10, 2017 at 21:33 Comment(0)
F
0

If you would like to use IIS for this you can create a rewrite rule:

<system.webServer>
    <rewrite>
      <rules>
        <rule name="RewriteUnknownToIndex" patternSyntax="ECMAScript" stopProcessing="true">
          <match url="^wwwroot/.*" negate="true" />
          <action type="Rewrite" url="/wwwroot/index.html" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>

You can also add a prefix to the url regex, such as ^(wwwroot|api)/.*, if this is a web-api that you would like to host at /api.

Fogbound answered 11/11, 2016 at 19:41 Comment(2)
I wasn't the one that down voted it but this doesn't work for other files or subfolders found in the wwwroot. It redirects to index.html no matter what.Tensile
@Tensile Any request that starts with "/wwroot/" will return the corresponding subfolder/file from disk, all other requests will return index.html.Toucan

© 2022 - 2024 — McMap. All rights reserved.