question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Blazor Server Apps packed into a Tool can't find static assets

See original GitHub issue

Describe the bug

When packing an ASP.NET app into a dotnet tool package the static assets from wwwroot result in 404.

To Reproduce

  1. Check out repo
  2. cd DotnetToolStaticFilesNotFound
  3. dotnet pack
  4. dotnet tool install -g --add-source .\bin\Debug\ DotnetToolStaticFilesNotFound
  5. cd .. - change directory so the wwwroot isn’t in your current path
  6. Load localhost:5000 in a browser. No css can be found.

Alternately:

  1. Create a new Blazor server app.
  2. add <PackAsTool>true</PackAsTool> and <IsPackable>true</IsPackable> to the csproj.
  3. Pack, install and run tool as above. image

https://github.com/gfs/BlazorPackFailure

Exceptions (if any)

Further technical details

  • Include the output of dotnet --info
  • 7.0.100-rc.2.22477.23
  • The IDE (VS / VS Code/ VS4Mac) you’re running on, and its version
  • VS 2022 Preview

Also reproduces on .NET 6.0.

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:1
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
MiguelAlhocommented, Dec 15, 2022

I’ve found a workaround for my case mentioned above that is quite horrible and will bite me in the future, but I can at least move forward. Posting case anyone comes to the same situation:

  • Because I need MudBlazor’s assets, I’ve run dotnet publish to expose them in the published wwwroot folder
  • With access to them, I’ve included the files directly into the project’s wwwroot folder (manually, using file explorer and IDE) since I know that what is in it will be included in the package. Unfortunately I’ll need to update them any time I update the package, which will be a PITA to remember. I’ve also moved them into a wwwroot\MudBlazor instead of the expected wwwroot\_content\Mudblazor folder. If this ever get’s fixed, the _content will eventually appear in the package, and the awkwaredness may remind me of the situation.
  • Because of the path change, I’ve had to update my _Host.cshtml/ Layout.cshtml with the new paths to the asset files
<link href="MudBlazor/MudBlazor.min.css" rel="stylesheet" />
....
<script src="MudBlazor/MudBlazor.min.js"></script>
  • I adopted a slight variation on @gfs 's code since I’m using minimal format on Program.cs:
    • added GetAssetLocations as a static method returning a tuple of the calculated paths
    • opted to throw instead of return
static (string assemblyLocation, string wwwrootLocation, string webroot) GetAssetLocations()
{
    var assemblyLocation = Directory.GetParent(Assembly.GetExecutingAssembly().Location)?.FullName;
    if (assemblyLocation is null)
    {
        throw new FileNotFoundException("Couldn't get directory containing assembly, unable to set content root.");
    }

    // If we are running from dotnet build or dotnet publish the wwwroot will be adjacent to the assemblies
    var wwwrootLocation = Path.Combine(assemblyLocation, "wwwroot");
    var webRoot = wwwrootLocation;

    // If the expected wwwroot doesn't exist, we can also check if we are installed as a dotnet tool
    if (!Directory.Exists(wwwrootLocation))
    {
        // If we are installed as a tool the assembly will be located in a folder structure that looks like this
        //      <toolname>\<version>\tools\<framework>\<platform>\
        // The wwwroot is packed a folder called "staticwebassets" in the <version> folder, so we want to go 3 levels up.
        var toolRootInstallDirectory = new DirectoryInfo(assemblyLocation)?.Parent?.Parent?.Parent;
        if (toolRootInstallDirectory is null)
        {
            throw new DirectoryNotFoundException("expected tool directory does not exist. probably not a tool install");
        }
        
        var staticWebAssetsLocation = Path.Combine(toolRootInstallDirectory.FullName, "staticwebassets");
        if (Directory.Exists(staticWebAssetsLocation))
        {
            webRoot = staticWebAssetsLocation;
        }
        else
        {
            throw new DirectoryNotFoundException(
                "can't find the static web assets, so rendering likely won't work properly");
        }
    }

    return (assemblyLocation, wwwrootLocation, webRoot);
}
  • then set the builder.Environment’s WebRootPath and ContentRootPath with the calculated paths:
var (assemblyLocation, wwwrootLocation, webroot) = GetAssetLocations();
var builder = WebApplication.CreateBuilder(new WebApplicationOptions()
{
   Args = args,
   ContentRootPath = webroot,
   WebRootPath = webroot
});
...
  • I’ve commented out the call to StaticWebAssetsLoader.UseStaticWebAssets(builder.Environment, builder.Configuration);?from the default template, too
1reaction
gfscommented, Nov 16, 2022

If anyone else has the same issue, I have figured out a workaround based on the folder structure of the tool when unpacked.

private static int RunServer()
{
    IHostBuilder server = Host.CreateDefaultBuilder(Array.Empty<string>());
    var assemblyLocation = Directory.GetParent(Assembly.GetExecutingAssembly().Location)?.FullName;
    if (assemblyLocation is null)
    {
        Log.Error("Couldn't get directory containing assembly, unable to set content root.");
        return -1;
    }

    // We have to set the content root to the folder with the assemblies to function properly
    server.UseContentRoot(assemblyLocation);

    // If we are running from dotnet build or dotnet publish the wwwroot will be adjacent to the assemblies
    var wwwrootLocation = Path.Combine(assemblyLocation, "wwwroot");
    var webRoot = wwwrootLocation;

    // If the expected wwwroot doesn't exist, we can also check if we are installed as a dotnet tool
    if (!Directory.Exists(wwwrootLocation))
    {
        // If we are installed as a tool the assembly will be located in a folder structure that looks like this
        //      <toolname>\<version>\tools\<framework>\<platform>\
        // The wwwroot is packed a folder called "staticwebassets" in the <version> folder, so we want to go 3 levels up.
        var toolRootInstallDirectory = new DirectoryInfo(assemblyLocation)?.Parent?.Parent?.Parent;
        if (toolRootInstallDirectory is null)
        {
            // Probably not a tool install
            return -2;
        }
        var staticWebAssetsLocation = Path.Combine(toolRootInstallDirectory.FullName, "staticwebassets");
        if (Directory.Exists(staticWebAssetsLocation))
        {
            webRoot = staticWebAssetsLocation;
        }
        else
        {
            // We can't find the static web assets, so rendering likely won't work property
            return -3;
        }
    }

    var host = server.ConfigureWebHostDefaults(webBuilder =>
    {
        // Now we set the webroot
        webBuilder.UseWebRoot(webRoot);
        webBuilder.UseStartup<Startup>();
    })
    .Build();

    host.Run();
        
    return 0;
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

ASP.NET Core Blazor static files
Learn how to configure and manage static files for Blazor apps.
Read more >
Why ASP.NET Core web application does not serve static ...
The reason why you found it is not working is because there is no static assets in bin/Debug folder. It works in development...
Read more >
How to Build and Secure Web Applications with Blazor
Learn how to build client-side Web apps using Blazor and how to secure them with Auth0 authentication and authorization features.
Read more >
ASP.NET hosted blazor WASM not serving static files as ...
I have a Blazor WASM that's configured to be asp.net core (.net 5) hosted. The Blazor client app works just fine, but I...
Read more >
Getting Started with Blazor TextBox Component
This section briefly explains about how to include Blazor TextBox component in your Blazor Server App and Blazor WebAssembly App using Visual Studio....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found