Skip to content

[Blazor] Move to MapBlazorWebAssemblyApplication #19147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/Components/Blazor.sln
Original file line number Diff line number Diff line change
Expand Up @@ -552,9 +552,9 @@ Global
{C4D74173-702B-428A-B689-1A9AF51CE356} = {B29FB58D-FAE5-405E-9695-BCF93582BE9A}
{B3EF0C88-3466-40AE-9080-F694370F4192} = {C4D74173-702B-428A-B689-1A9AF51CE356}
{2916EC17-1D1F-4949-9EC7-50725157F1A6} = {C4D74173-702B-428A-B689-1A9AF51CE356}
{7EFB9CAF-6716-43BF-A6EF-C2878E95F8A6} = {B4ACD900-27B6-482B-B434-2C1E86E9D8BC}
{194EBC45-F98E-4919-B714-C1624EF17B31} = {B4ACD900-27B6-482B-B434-2C1E86E9D8BC}
{EAF50654-98ED-44BB-A120-0436EC0CD3E0} = {B4ACD900-27B6-482B-B434-2C1E86E9D8BC}
{7EFB9CAF-6716-43BF-A6EF-C2878E95F8A6} = {CBD2BB24-3EC3-4950-ABE4-8C521D258DCD}
{194EBC45-F98E-4919-B714-C1624EF17B31} = {CBD2BB24-3EC3-4950-ABE4-8C521D258DCD}
{EAF50654-98ED-44BB-A120-0436EC0CD3E0} = {CBD2BB24-3EC3-4950-ABE4-8C521D258DCD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {27A36094-AA50-4FFD-ADE6-C055E391F741}
Expand Down
3 changes: 1 addition & 2 deletions src/Components/WebAssembly/DevServer/src/Server/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ public void ConfigureServices(IServiceCollection services)
{
services.AddRouting();

services.AddWebAssemblyStaticFilesConfiguration();

services.AddResponseCompression(options =>
{
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]
Expand All @@ -54,6 +52,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment environment,

app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorWebAssemblyApplication();
endpoints.MapFallbackToFile("index.html");
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Net.Mime;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Net.Http.Headers;

namespace Microsoft.AspNetCore.Builder
{
/// <summary>
/// Extensions for mapping Blazor WebAssembly applications.
/// </summary>
public static class ComponentsWebAssemblyEndpointRouteBuilderExtensions
{
/// <summary>
/// Maps a Blazor webassembly application to the <paramref name="pathPrefix"/>.
/// </summary>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <param name="pathPrefix">The <see cref="PathString"/> that indicates the prefix for the Blazor application.</param>
/// <returns>The <see cref="IEndpointConventionBuilder"/></returns>
public static IEndpointConventionBuilder MapBlazorWebAssemblyApplication(this IEndpointRouteBuilder endpoints, PathString pathPrefix)
{
if (endpoints is null)
{
throw new ArgumentNullException(nameof(endpoints));
}

var webHostEnvironment = endpoints.ServiceProvider.GetRequiredService<IWebHostEnvironment>();

var options = CreateStaticFilesOptions(webHostEnvironment.WebRootFileProvider);
var appBuilder = endpoints.CreateApplicationBuilder();

appBuilder.Use(async (ctx, next) =>
{
var endpoint = ctx.GetEndpoint();
try
{
// Set the endpoint to null so that static files doesn't discard the path.
ctx.SetEndpoint(null);

if (ctx.Request.Path.StartsWithSegments(pathPrefix, out var rest) &&
rest.StartsWithSegments("/_framework"))
{
// At this point we mapped something from the /_framework
ctx.Response.Headers.Append(HeaderNames.CacheControl, "no-cache");
}

// This will invoke the static files middleware plugged-in below.
await next();

}
finally
{
ctx.SetEndpoint(endpoint);
}
});

appBuilder.UseStaticFiles(options);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to check, what are the possible scenarios where this might conflict unexpectedly with other things a developer might be doing? Are they safe to have their own separate UseStaticFiles calls with different options elsewhere?

Not saying it's the wrong design - just want to get my understanding right.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to check, what are the possible scenarios where this might conflict unexpectedly with other things a developer might be doing? Are they safe to have their own separate UseStaticFiles calls with different options elsewhere?

Not saying it's the wrong design - just want to get my understanding right.

Yes, you can have your own static files middleware and except for the case they are being mapped into the same subpath (or to "/") this should not interfere.

In the future, I want to flow the paths into the host so that it can map exactly the files the blazor app is meant to serve and nothing else. At that point there's no room for ever conflicting with the static files middleware.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

except for the case they are being mapped into the same subpath (or to "/") this should not interfere.

In the default "hosted" template case, if someone puts app.UseStaticFiles() into their server project because they also want to serve static files from there (independently of their Blazor application), is this likely to result in any conflicts or unexpected behavior?

As long as it's not hugely disruptive it's probably OK for preview2, but just checking we know what to expect.

In the future, I want to flow the paths into the host so that it can map exactly the files the blazor app is meant to serve and nothing else.

Totally makes sense as a future enhancement.


var conventionBuilder = endpoints.Map(
$"{pathPrefix}/{{*path:file}}",
appBuilder.Build());

conventionBuilder.Add(builder =>
{
// Map this route with low priority so that it doesn't interfere with any other potential request.
((RouteEndpointBuilder)builder).Order = int.MaxValue - 100;
});

return conventionBuilder;
}

/// <summary>
/// Maps a Blazor webassembly application to the root path of the application "/".
/// </summary>
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
/// <param name="pathPrefix">The <see cref="PathString"/> that indicates the prefix for the Blazor application.</param>
/// <returns>The <see cref="IEndpointConventionBuilder"/></returns>
public static IEndpointConventionBuilder MapBlazorWebAssemblyApplication(this IEndpointRouteBuilder endpoints) =>
MapBlazorWebAssemblyApplication(endpoints, default);

private static StaticFileOptions CreateStaticFilesOptions(IFileProvider webRootFileProvider)
{
var options = new StaticFileOptions();
options.FileProvider = webRootFileProvider;
var contentTypeProvider = new FileExtensionContentTypeProvider();
AddMapping(contentTypeProvider, ".dll", MediaTypeNames.Application.Octet);
// We unconditionally map pdbs as there will be no pdbs in the output folder for
// release builds unless BlazorEnableDebugging is explicitly set to true.
AddMapping(contentTypeProvider, ".pdb", MediaTypeNames.Application.Octet);

options.ContentTypeProvider = contentTypeProvider;

return options;
}

private static void AddMapping(FileExtensionContentTypeProvider provider, string name, string mimeType)
{
if (!provider.Mappings.ContainsKey(name))
{
provider.Mappings.Add(name, mimeType);
}
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ public class Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<RequestLog>();
services.AddWebAssemblyStaticFilesConfiguration();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand All @@ -34,12 +33,11 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, RequestL
app.UseBlazorDebugging();
}

app.UseStaticFiles();

app.UseRouting();

app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorWebAssemblyApplication();
endpoints.MapFallbackToFile("index.html");
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddWebAssemblyStaticFilesConfiguration();
}

public void Configure(IApplicationBuilder app)
Expand All @@ -21,6 +20,7 @@ public void Configure(IApplicationBuilder app)
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorWebAssemblyApplication();
endpoints.MapFallbackToFile("index.html");
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ public void ConfigureServices(IServiceCollection services)
services.AddAuthentication()
.AddIdentityServerJwt();

services.AddWebAssemblyStaticFilesConfiguration();

services.AddMvc();
services.AddResponseCompression(opts =>
{
Expand All @@ -58,8 +56,6 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseBlazorDebugging();
}

app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
Expand All @@ -70,6 +66,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
endpoints.MapControllers();
endpoints.MapRazorPages();

endpoints.MapBlazorWebAssemblyApplication();
endpoints.MapFallbackToFile("index.html");
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public void CachesResourcesAfterFirstLoad()
var subsequentResourcesRequested = GetAndClearRequestedPaths();
Assert.NotEmpty(initialResourcesRequested.Where(path => path.EndsWith("/blazor.boot.json")));
Assert.Empty(subsequentResourcesRequested.Where(path => path.EndsWith("/dotnet.wasm")));
Assert.Empty(subsequentResourcesRequested.Where(path => path.EndsWith(".js")));
Assert.NotEmpty(subsequentResourcesRequested.Where(path => path.EndsWith(".js")));
Assert.Empty(subsequentResourcesRequested.Where(path => path.EndsWith(".dll")));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ public void ConfigureServices(IServiceCollection services)

services.AddServerSideBlazor();

services.AddWebAssemblyStaticFilesConfiguration();

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
services.AddAuthorization(options =>
{
Expand All @@ -56,6 +54,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorWebAssemblyApplication();
endpoints.MapControllers();
endpoints.MapRazorPages();
endpoints.MapBlazorHub();
Expand Down
3 changes: 2 additions & 1 deletion src/Components/test/testassets/TestServer/ClientStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddServerSideBlazor();
services.AddWebAssemblyStaticFilesConfiguration();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand All @@ -43,6 +42,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorWebAssemblyApplication();

endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
Expand Down
3 changes: 2 additions & 1 deletion src/Components/test/testassets/TestServer/CorsStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ public CorsStartup(IConfiguration configuration)
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddWebAssemblyStaticFilesConfiguration();
services.AddCors(options =>
{
// It's not enough just to return "Access-Control-Allow-Origin: *", because
Expand Down Expand Up @@ -54,6 +53,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorWebAssemblyApplication();

endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddServerSideBlazor();
services.AddWebAssemblyStaticFilesConfiguration();
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand Down Expand Up @@ -55,6 +54,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorWebAssemblyApplication();

endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_ServerHost");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public StartupWithMapFallbackToClientSideBlazor(IConfiguration configuration)

public void ConfigureServices(IServiceCollection services)
{
services.AddWebAssemblyStaticFilesConfiguration();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
Expand All @@ -34,7 +33,12 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
// The client-side files middleware needs to be here because the base href in hardcoded to /subdir/
app.Map("/subdir", app =>
{
app.UseStaticFiles();
app.UseRouting();

app.UseEndpoints(endpoints =>
{
endpoints.MapBlazorWebAssemblyApplication();
});
});

// The calls to `Map` allow us to test each of these overloads, while keeping them isolated.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ public void ConfigureServices(IServiceCollection services)
services.AddRazorPages();
#endif

services.AddWebAssemblyStaticFilesConfiguration();

services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
Expand Down Expand Up @@ -127,6 +125,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
#endif
endpoints.MapControllers();

endpoints.MapBlazorWebAssemblyApplication();
endpoints.MapFallbackToFile("index.html");
});
}
Expand Down