Aspnetcore: Passing options to UseStaticFiles breaks Blazor Server's static file service

Created on 4 Mar 2020  路  9Comments  路  Source: dotnet/aspnetcore

Describe the bug

If you pass any kind of StaticFileOptions object to app.UseStaticFiles(), then Blazor Server will no longer be able to serve blazor.server.js, breaking Blazor.

To Reproduce

  • Create the template Blazor Server app
  • Run it and observe that it works correctly
  • Change the app.UseStaticFiles() call in Startup.Configure to read app.UseStaticFiles(new StaticFileOptions())
  • Run the app again and observe that blazor.server.js is 404'd, and hence the app doesn't work.

What appears to be happening is that if you don't pass any configuration to UseStaticFiles, then the framework correctly calls ConfigureStaticFilesOptions.PostConfigure after the pipeline is built. This configures the static file middleware to serve the Blazor JS from manifest resources.

As soon as there's any configuration passed to UseStaticFiles, then ConfigureStaticFilesOptions is no longer called. It doesn't matter what specific properties are in the StaticFileOptions object - it's just the presence of the object which causes the problem.

I will debug this further if it's helpful, but I suspect someone more familiar with the IPostConfigureOptions mechanism will know what's going on instantly.

Maybe there's some work-around, but if so then it would be helpful if it could be somehow more obvious, because "I added a static file extension and Blazor stopped working" is somewhat surprising.

I think this may be same issue as #18222, which was perhaps rather confusingly titled and ended up being closed, but I think there is a real issue here.

Further technical details

  • ASP.NET Core version 3.1.2
  • The IDE (VS / VS Code/ VS4Mac) you're running on, and it's version VS 16.4.5 on Win10
affected-few area-blazor enhancement severity-minor

Most helpful comment

Yeah, blazor should be adding their own static file handler like UseBlazorStaticResources rather than messing with the default.

All 9 comments

Does it work if you add a second instance of UseStaticFiles with options rather than modifying the existing one?

@Tratcher It does!

I am not sure where I currently am on the amazed <-> appalled spectrum...

:grin: UseStaticFiles is designed to support multiple instances if you pass in the options directly. UseStaticFiles() with no parameters pulls the options from DI which is likely where Blazor is tweaking the settings.

@Tratcher Thanks - that does make sense, though I didn't expect it at all, and it feels like a bit of a gotcha.

Would it perhaps be possible for Blazor to detect that it has never been given the chance to run its static file config code and explode somehow early in the run?
That would also catch the case where you didn't have a UseStaticFiles at all - I have seen other people on-line struggling with that.

Or, rather than co-opting (hijacking) the UseStaticFiles to serve-up the manifest resources, could there just be a separate bit of middleware: UseBlazorStaticResources or something, which just served the specific Blazor bits and didn't touch the normal StaticFile middleware?

Yeah, blazor should be adding their own static file handler like UseBlazorStaticResources rather than messing with the default.

We've moved this issue to the Backlog milestone. This means that it is not going to happen for the coming release. We will reassess the backlog following the current release and consider this item at that time. However, keep in mind that there are many other high priority features with which it will be competing for resources.

I just tried adding another call to app.UseStaticFiles() and it now works! That is blazor.server.js is served, as is my site.webmanifest file. To be clear this is what I now have in Startup.Configure(...):

app.UseStaticFiles();

FileExtensionContentTypeProvider provider = new FileExtensionContentTypeProvider();
provider.Mappings[".webmanifest"] = "application/manifest+json";

app.UseStaticFiles(new StaticFileOptions()
{
    ContentTypeProvider = provider
});

Not keen on this, but at least it works!

I've just ran in to this which is very confusing behaviour.

I can confirm that @benm-eras workaround is working as expected.

I've encountered the same problem.

The solution also comes in this other message which proposes two alternatives:

https://github.com/dotnet/aspnetcore/issues/9588#issuecomment-505465169

The first is the ideal solution. The second is a hack that I would not recommend.

Example:

// ConfigureServices method
services.Configure<StaticFileOptions>(options => {
    FileExtensionContentTypeProvider contentTypeProvider = new FileExtensionContentTypeProvider();
    contentTypeProvider.Mappings[".gpx"] = "application/gpx+xml";

    options.ContentTypeProvider = contentTypeProvider;
});

// Configure method
app.UseStaticFiles();
Was this page helpful?
0 / 5 - 0 ratings