JavaScript, CSS, HTML & Other Static Files in ASP .NET Core

By Shahed C on March 11, 2019

This is the tenth of a series of posts on ASP .NET Core in 2019. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2019, titled A-Z of ASP .NET Core!

ASPNETCoreLogo-300x267 A – Z of ASP .NET Core!

In this Article:

J is for JavaScript, CSS, HTML & Other Static Files

NOTE: this article will teach you how to include and customize the use of static files in ASP .NET Core web applications. It is not a tutorial on front-end web development.

If your ASP .NET Core web app has a front end – whether it’s a collection of MVC Views or a Single-Page Application (SPA) – you will need to include static files in your application. This includes (but is not limited to): JavaScript, CSS, HTML and various image files.

When you create a new web app using one of the built-in templates (MVC or Razor Pages), you should see a “wwwroot” folder in the Solution Explorer. This points to a physical folder in your file system that contains the same files seen from Visual Studio. However, this location can be configured, you can have multiple locations with static files, and you can enable/disable static files in your application if desired. In fact, you have to “opt in” to static files in your middleware pipeline.

Blog-Diagram-AspNetCore-mw

You can browse the (template-generated) sample app (with static files) on GitHub at the following location:

Web sample app with static files: https://github.com/shahedc/AspNetCoreStaticSample

Configuring Static Files via Middleware

Let’s start by observing the Startup.cs configuration file. We’ve seen this file several times throughout this blog series. In the Configure() method, you’ll find the familiar method call to enable the use of static files.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   ...
   app.UseStaticFiles();
   ...
   app.UseMvc(...);
}

This call to app.UseStaticFiles() ensures that static files can be served from the designated location, e.g. wwwroot. In fact, this line of code looks identical whether you look at the Startup.cs file in an MVC or Razor Pages project.

It’s useful to note the placement of this line of code. It appears before app.UseMvc(), which is very important. This ensures that static file requests can be processed and sent back to the web browser without having to touch the MVC middleware. This becomes even more important when authentication is used.

Take a look at either the MVC or Razor Pages projects that have authentication added to them. In the code below, you can see the familiar call to app.UseStaticFiles() once again. However, there is also a call to app.UseAuthentication(). It’s important for the authentication call to appear after the call to use static files. This ensure that the authentication process isn’t triggered when it isn’t needed.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   ...
   app.UseStaticFiles();
   ...
   app.UseAuthentication();
   ...
   app.UseMvc(...);
}

By using the middleware pipeline in this way, you can “short-circuit” the pipeline when a request has been fulfilled by a specific middleware layer. If a static file has been successfully served using the Static Files middleware, it prevents the next layers of middleware (i.e. authentication, MVC) from processing the request.

NOTE: if you need to secure any static files, e.g. private images, you can consider a cloud solution such as Azure Blob Storage to store the files. The files can then be served from within the application, instead of actual static files. You could also serve secure files (from outside the wwwroot location) as a FileResult() object returned from an action method that has an [Authorize] attribute.

Customizing Locations for Static Files

It may be convenient to have the default web templates create a location for your static files and also enable the use of those static files. As you’ve already seen, enabling static files isn’t magic. Removing the call to app.useStaticFiles() will disable static files from being served. In fact, the location for static files isn’t magic either.

In a previous post, we had discussed how the Program.cs file includes a call to CreateDefaultBuilder() to set up your application:

public class Program
{
   ...
   public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
      WebHost.CreateDefaultBuilder(args)
         .UseStartup<Startup>();
}

Behind the scenes, this method call sets the “content root” to the current directory, which contains the “wwwroot” folder, your project’s “web root”. These can both be customized.

WebHost.CreateDefaultBuilder(args).UseContentRoot("c:\\<content-root>")
WebHost.CreateDefaultBuilder(args).UseWebRoot("public")

You may also use the call to app.UseStaticFiles() to customize an alternate location to serve static files. This allows you to serve additional static files from a location outside of the designated web root.

...
using Microsoft.Extensions.FileProviders;
using System.IO;
...
public void Configure(IApplicationBuilder app)
{
   ...
   app.UseStaticFiles(new StaticFileOptions
   {
      FileProvider = new PhysicalFileProvider(
         Path.Combine(env.ContentRootPath, "AltStaticRoot")),
         RequestPath = "/AltStaticFiles"
   });
}

Wait a minute… why does it look like there are two alternate locations for static files? There is a simple explanation:

  • In the call to Path.Combine(), the “AltStaticRoot” is an actual folder in your current directory. This Path class and its Combine() method are available in the System.IO namespace.
  • The “AltStaticFiles” value for RequestPath is used as a root-level “virtual folder” from which images can be served. The PhysicalFileProvider class is available in the Microsoft.Extensions.FileProviders namespace.

The following markup may be used in a .cshtml file to refer to an image, e.g. MyImage01.png:

<img src="~/AltStaticFiles/MyImages/MyImage01.png" />

The screenshot below shows an example of an image loaded from an alternate location.

code sample for alt images

The screenshot below shows a web browser displaying such an image.

VS2017-AltStaticFile-Browser

You can find the above code in the RazorWebAltStatic sample web app on GitHub:

Preserving CDN Integrity

When you use a CDN (Content Delivery Network) to serve common CSS and JS files, you need to ensure that the integrity of the source code is reliable. You can rest assured that ASP .NET Core has already solved this problem for you in its built-in templates. Let’s take a look at the shared Layout page, e.g. _Layout.cshtml in a Razor Pages web application.

<environment include="Development">
 <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
</environment>

<environment exclude="Development">
 <link 
   rel="stylesheet" 
   href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css"
   asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
   asp-fallback-test-class="sr-only" 
   asp-fallback-test-property="position" 
   asp-fallback-test-value="absolute"
   crossorigin="anonymous"
   integrity="sha256-eSi1q2PG6J7g7ib17yAaWMcrr5GrtohYChqibrV7PBE="/>
</environment>

Right away, you’ll notice that there are two conditional <environment> blocks in the above markup. The first block is used only during development, in which the bootstrap CSS file is obtained from your local copy. When not in development (e.g. staging, production, etc), the bootstrap CSS file is obtained from a CDN, e.g. CloudFlare.

You could use an automated hash-generation tool to generate the SRI (Subresource Integrity) hash values, but you would have to manually copy the value into your code. You can try out the relatively-new LibMan (aka Library Manager) for easily adding and updating your client-side libraries.

LibMan (aka Library Manager)

The easiest way to use LibMan is to use the built-in features available in Visual Studio. Using LibMan using the IDE is as easy as launching it from Solution Explorer. Specify the provider from the library you want, and any specific files you want from that library.

If you’ve already read my SignalR article from my 2018 blog series, you may recall the steps to add a client library via LibMan (aka Library Manager):

  • Right-click project in Solution Explorer
  • Select Add | Client-Side Library

In the popup that appears, select/enter the following:

  • Provider: choose from cdnjs, filesystem, unpkg
  • Library search term, e.g. @aspnet/signalr@1… pick latest stable if desired
  • Files: At a minimum, choose specific files, e.g. signalr.js and/or its minified equivalent

VS2017-SignalR-ClientLib

For more on LibMan (using VS or CLI), check out the official docs:

In any case, using LibMan will auto-populate a “libman.json” manifest file, which you can also inspect and edit manually. The aforementioned SignalR article also includes a real-time polling web app sample. You can view its libman.json file to observe its syntax for using a SignalR client library.

{
  "version": "1.0",
  "defaultProvider": "unpkg",
  "libraries": [
    {
      "library": "@aspnet/signalr@1.1.0",
      "destination": "wwwroot/lib/signalr/",
      "files": [
        "dist/browser/signalr.js",
        "dist/browser/signalr.min.js"
      ]
    }
  ]
}

Every time you save the libman.json file, it will trigger LibMan’s restore process. This pulls down the necessary libraries from their specified source, and adds them to your local file system. If you want to trigger this restore process manually, you can always choose the “Restore Client-Side Libraries” option by right-clicking the libman.json file in Solution Explorer.

What About NPM or WebPack?

If you’ve gotten this far, you may be wondering: “hey, what about NPM or WebPack?”

It’s good to be aware that LibMan is a not a replacement for your existing package management systems. In fact, the Single-Page Application templates in Visual Studio (for Angular and React) currently use npm and WebPack. LibMan simply provides a lightweight mechanism to include client-side libraries from external locations.

For more information on WebPack in ASP .NET Core, I would recommend these 3rd-party articles:

References

Additional reference for pre-compressing static files, found on Twitter via:

@DotNetAppDevhttps://twitter.com/DotNetAppDev/status/1106299093922533376

 

 

3 thoughts on “JavaScript, CSS, HTML & Other Static Files in ASP .NET Core

  1. Pingback: Dew Drop – March 12, 2019 (#2917) | Morning Dew

  2. Pingback: The Morning Brew - Chris Alcock » The Morning Brew #2703

  3. Pingback: Middleware in ASP .NET Core | Wake Up And Code!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.