DEV Community

loading...
Cover image for How to deal with the "HTTP 404 '_content/Foo/Bar.css' not found" when using Razor component package on ASP.NET Core Blazor app

How to deal with the "HTTP 404 '_content/Foo/Bar.css' not found" when using Razor component package on ASP.NET Core Blazor app

jsakamoto
Microsoft MVP for Visual Studio and Development Tech. (prefer C#, .NET Core, ASP.NET Core, Azure Web Apps, TypeScript, and Blazor WebAssembly App!)
・5 min read

― Prologue ―

Quick tour of "Razor Class library" package

You can easily create a class library project that includes Blazor components to reuse them from other projects.

Those libraries are called "Razor Class Library", and you can also package it as a NuGet package.

The "Razor Class Library" NuGet package can also include static web assets such as JavaScript, CSS, graphics, fonts, etc.

How to create "Razor Class Library" and package it?

If you use Visual Studio IDE on Windows OS, you can start from the "Razor Class Library" project template for creating it.

fig.1

If you use dotnet CLI, dotnet new razorclasslib command does it.

fig.2

After the new "Razor Class Library" project was generated from the template, you can see the project includes Razor component, JavaScript, CSS, and PNG image file.

fig.3

After implements what you want, you can package it as a NuGet package file(.nupkg).

To package the project on Visual Studio IDE, right-click the project node on solution explorer, and click the "Pack" menu item of the context menu.

fig.4

If you use dotnet CLI, the "dotnet pack" command does it.

fig.5

After the process was completed, you can see the NuGet package (.nupkg) file is generated.

Static web asset files that are included in the source project are also bundled inside of the NuGet package file.

fig.6

How to redistribute and consume the package?

Once you created the "Razor Class Library" as a NuGet package, you can redistribute it in numerous ways.

One of the easiest ways is, save the package file into the file system.

After configuring the package source settings to adding the folder location where your NuGet packages will be saved (see below),

fig.7

you can add the package reference to that NuGet package into any Blazor app projects.

fig.8

And, you can also reference the static web assets that are included the NuGet package from root document such as index.html or _Host.cshtml.

The URL of those static web assets is:

  • starts with "_content/",
  • next, the name of the package,
  • at last, the file name of the asset file.

As you see, you can create, redistribute, and use a reusable component library package that includes static web assets, very easily.

fig.9

Wait, where are static web asset files?

By the way, if you inspect the output files of the application project, you will find that you cannot find any static web asset files provided by the NuGet package.

fig.10

Where do those files come from?

The key point is the ".StaticWebAssets.xml" file.

The answer is, "Those files come from the NuGet package cache folder".

You will find "{OutputName}.StaticWebAssets.xml" XML formatted file in output folder.

The .StaticWebAssets.xml file is generated at the build process, and it describes the mapping information from the URL path to the physical file system location.

fig.11

The .StaticWebAssets.xml file is read by ASP.NET Core StaticWebAssetsFileProvider file provider (in Blazor Server, or Blazor WebAssembly hosted ASP.NET Core scenario) or Blazor DevServer (in Blazor WebAssembly scenario).

StaticWebAssetsFileProvider file provider or Blazor DevServer be able to respond static web asset contents that are located NuGet package cache folder to web browsers with the URL mapping information that is provided by .StaticWebAssets.xml file.

This "Static Web Assets" feature of ASP.NET Core is helpful for save the build time and reduce file usage size on your disk space.

Of course, this feature is designed for application development time.

After publishing the project, the published folder includes all static web asset files statically that are included NuGet packages.

fig.12

― Main Subject ―

One day, the styling of the component was lost!

One day, my co-worker run into the problem.

"Wow, what's happening? the styling of "My.Greeting" component on my Blazor Server App is lost! 😱"

fig.13

I started an investigation with developer tools on the web browser immediately to know what is happening.

The developer tools said the request to static web asset (in this case, the .css file that bundled the NuGet package) was failed by HTTP 404 "Resouce not found".

fig.14

I also inspected .StaticWebAssets.xml file was broken or not, but it existed and it was no problem.

Why the StaticWebAssetsFileProvider in the Blazor Server app could not find the static web asset files?

The reason is ...

Next, I cloned my coworker's Git repository and compared the two Blazor Server app projects, one is my co-worker's project, and another one is mine that is not broken.

After a few hours I was using diff tools, finally, I found the reason for the problem.

The reason for the problem was the "ASPNETCORE_ENVIRONMENT" environment variable.

In the Blazor Server app project broken version, the configuration of the "ASPNETCORE_ENVIRONMENT" environment variable was set with "Release", not "Development".

fig.15

My co-worker wanted to investigate the behavior of the Blazor Server app in the "Release" mode, so she changed the setting of the"'ASPNETCORE_ENVIRONMENT" environment variable.

Unfortunately, the StaticWebAssetsFileProvider is used only when the "ENVIRONMENT" configuration is "Development"...! (See ASP.NET Core source code at below.)

After I reverted this setting to "Development", the Blazor Server app was worked fine.

Another workaround

If you have to use the ASP.NET Core static web assets feature in a not "Development" environment mode, you can enable that feature manually.

Usually, the StaticWebAssetsFileProvider is inserted to the application by the inside of the ConfigureWebHostDefaults() extension method that will be called at your Program.Main() method, only when the hosting environment mode is "Development".

You can insert the StaticWebAssetsFileProvider manually even if the hosting environment mode is not "Development", like this code:

// Program.cs
...
public class Program
{
  ...
  public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
      .ConfigureWebHostDefaults(webBuilder =>
      {
        webBuilder.UseStartup<Startup>();

        // 👇 Add this "ConfigureAppConfiguration" method calling.
        webBuilder.ConfigureAppConfiguration((ctx, cb) =>
        {
          // 👇 Please specify the condition that is true only when
          //    the application is running on your development environment.
          //    Notice that excludes the case that the environment is "Development".
          if (!ctx.HostingEnvironment.IsDevelopment() &&
               /* your prefer condition*/)
          {
            // 👇 This call inserts "StaticWebAssetsFileProvider" into
            //    the static file middleware.
            StaticWebAssetsLoader.UseStaticWebAssets(
              ctx.HostingEnvironment, 
              ctx.Configuration);
          }
        });
        ...

― Conclusion ―

On Blazor Server App and Blazor Wasm ASP.NET Core host, the StaticWebAssetsFileProvider is used only when the hosting "ENVIRONMENT" configuration is "Development".

If you encounter like the "HTTP 404 '_content/Foo/Bar.css' not found" error when using Razor component library package on ASP.NET Core Blazor app development scene, I'll recommend you to check the "ASPNETCORE_ENVIRONMENT" environment variable is "Development" or not.

If you have to set non "Development" value to the "ASPNETCORE_ENVIRONMENT" environment variable and have to run it from Visual Studio or "dotnet run", please try to add StaticWebAssetsLoader.UseStaticWebAssets(...) method calling into the Program.cs.

Happy coding!

Discussion (4)

Collapse
alphons profile image
Alphons van der Heijden • Edited

This post made my day.
Thanks for your input, this code works! (.NET CORE 5, vs 2019 16.8.3)
I copied and paste it into program.cs including the finger-point chars ;-)

It should go into the template code when creating a new project!

Collapse
waziplay profile image
wazi

Hi
Great and detailed post.
I have one question - how do the entries appear in the StaticWebAssets.xml file in the first place.

<ContentRoot ...

I have a folder with a sub folder that contains my wwwroot and a .js file in it.

The entry is not appearing

Collapse
waziplay profile image
wazi

I fixed my own problem by understanding how the static files are "copied".
In my case I had to copy the static files into wwwroot (top level folder of the project). Right click (each file) > properties and set the Build Action to Content.
When the NuGet package is created these files should then be in the staticwebassets folder.
You can check this locally (win 10) C:\Users{username}.nuget\packages\unitedgroup.blazor\1.0.1\staticwebassets
When you reference the package in your consuming code it will serve up the content as usual - just make sure you have the path right (all explained in the original post above)

If I've added to the confusion - apologies.
If it helps in your situation - cool.

Collapse
jadavis42 profile image
jadavis42

I suffered with this issue for 2 hours before finding your enlightened post. Thanks so much for creating this article!