119 lines
5.1 KiB
C#
119 lines
5.1 KiB
C#
// 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 Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Blazor.Server;
|
|
using Microsoft.AspNetCore.StaticFiles;
|
|
using Microsoft.Extensions.FileProviders;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.Net.Http.Headers;
|
|
using System.Net.Mime;
|
|
|
|
namespace Microsoft.AspNetCore.Builder
|
|
{
|
|
/// <summary>
|
|
/// Provides extension methods that add Blazor-related middleware to the ASP.NET pipeline.
|
|
/// </summary>
|
|
public static class BlazorAppBuilderExtensions
|
|
{
|
|
/// <summary>
|
|
/// Configures the middleware pipeline to work with Blazor.
|
|
/// </summary>
|
|
/// <typeparam name="TProgram">Any type from the client app project. This is used to identify the client app assembly.</typeparam>
|
|
/// <param name="applicationBuilder"></param>
|
|
public static void UseBlazor<TProgram>(
|
|
this IApplicationBuilder applicationBuilder)
|
|
{
|
|
var clientAssemblyInServerBinDir = typeof(TProgram).Assembly;
|
|
applicationBuilder.UseBlazor(new BlazorOptions
|
|
{
|
|
ClientAssemblyPath = clientAssemblyInServerBinDir.Location,
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Configures the middleware pipeline to work with Blazor.
|
|
/// </summary>
|
|
/// <param name="applicationBuilder"></param>
|
|
/// <param name="options"></param>
|
|
public static void UseBlazor(
|
|
this IApplicationBuilder applicationBuilder,
|
|
BlazorOptions options)
|
|
{
|
|
// TODO: Make the .blazor.config file contents sane
|
|
// Currently the items in it are bizarre and don't relate to their purpose,
|
|
// hence all the path manipulation here. We shouldn't be hardcoding 'dist' here either.
|
|
var env = (IHostingEnvironment)applicationBuilder.ApplicationServices.GetService(typeof(IHostingEnvironment));
|
|
var config = BlazorConfig.Read(options.ClientAssemblyPath);
|
|
var distDirStaticFiles = new StaticFileOptions
|
|
{
|
|
FileProvider = new PhysicalFileProvider(config.DistPath),
|
|
ContentTypeProvider = CreateContentTypeProvider(),
|
|
OnPrepareResponse = SetCacheHeaders
|
|
};
|
|
|
|
if (env.IsDevelopment() && config.EnableAutoRebuilding)
|
|
{
|
|
applicationBuilder.UseAutoRebuild(config);
|
|
}
|
|
|
|
// First, match the request against files in the client app dist directory
|
|
applicationBuilder.UseStaticFiles(distDirStaticFiles);
|
|
|
|
// Next, match the request against static files in wwwroot
|
|
if (!string.IsNullOrEmpty(config.WebRootPath))
|
|
{
|
|
// In development, we serve the wwwroot files directly from source
|
|
// (and don't require them to be copied into dist).
|
|
// TODO: When publishing is implemented, have config.WebRootPath set
|
|
// to null so that it only serves files that were copied to dist
|
|
applicationBuilder.UseStaticFiles(new StaticFileOptions
|
|
{
|
|
FileProvider = new PhysicalFileProvider(config.WebRootPath),
|
|
OnPrepareResponse = SetCacheHeaders
|
|
});
|
|
}
|
|
|
|
// Finally, use SPA fallback routing (serve default page for anything else,
|
|
// excluding /_framework/*)
|
|
applicationBuilder.MapWhen(IsNotFrameworkDir, childAppBuilder =>
|
|
{
|
|
childAppBuilder.UseSpa(spa =>
|
|
{
|
|
spa.Options.DefaultPageStaticFileOptions = distDirStaticFiles;
|
|
});
|
|
});
|
|
}
|
|
|
|
private static void SetCacheHeaders(StaticFileResponseContext ctx)
|
|
{
|
|
// By setting "Cache-Control: no-cache", we're allowing the browser to store
|
|
// a cached copy of the response, but telling it that it must check with the
|
|
// server for modifications (based on Etag) before using that cached copy.
|
|
// Longer term, we should generate URLs based on content hashes (at least
|
|
// for published apps) so that the browser doesn't need to make any requests
|
|
// for unchanged files.
|
|
var headers = ctx.Context.Response.GetTypedHeaders();
|
|
if (headers.CacheControl == null)
|
|
{
|
|
headers.CacheControl = new CacheControlHeaderValue
|
|
{
|
|
NoCache = true
|
|
};
|
|
}
|
|
}
|
|
|
|
private static bool IsNotFrameworkDir(HttpContext context)
|
|
=> !context.Request.Path.StartsWithSegments("/_framework");
|
|
|
|
private static IContentTypeProvider CreateContentTypeProvider()
|
|
{
|
|
var result = new FileExtensionContentTypeProvider();
|
|
result.Mappings.Add(".dll", MediaTypeNames.Application.Octet);
|
|
result.Mappings.Add(".mem", MediaTypeNames.Application.Octet);
|
|
result.Mappings.Add(".wasm", WasmMediaTypeNames.Application.Wasm);
|
|
return result;
|
|
}
|
|
}
|
|
}
|