// This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { AuthenticationConfig.Configure(services, Configuration); BlazorConfig.Configure(services, Configuration); DependencyInjectionConfig.Configure(services, Configuration); }
private static string FindIndexHtmlFile(BlazorConfig config) { // Before publishing, the client project may have a wwwroot directory. // If so, and if it contains index.html, use that. if (!string.IsNullOrEmpty(config.WebRootPath)) { var wwwrootIndexHtmlPath = Path.Combine(config.WebRootPath, "index.html"); if (File.Exists(wwwrootIndexHtmlPath)) { return(wwwrootIndexHtmlPath); } } // After publishing, the client project won't have a wwwroot directory. // The contents from that dir will have been copied to "dist" during publish. // So if "dist/index.html" now exists, use that. var distIndexHtmlPath = Path.Combine(config.DistPath, "index.html"); if (File.Exists(distIndexHtmlPath)) { return(distIndexHtmlPath); } // Since there's no index.html, we'll use the default DefaultPageStaticFileOptions, // hence we'll look for index.html in the host server app's wwwroot. return(null); }
/// <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) { var config = BlazorConfig.Read(options.ClientAssemblyPath); var clientAppBinDir = Path.GetDirectoryName(config.SourceOutputAssemblyPath); var clientAppDistDir = Path.Combine(clientAppBinDir, "dist"); var distFileProvider = new PhysicalFileProvider(clientAppDistDir); applicationBuilder.UseDefaultFiles(new DefaultFilesOptions { FileProvider = distFileProvider }); applicationBuilder.UseStaticFiles(new StaticFileOptions { FileProvider = distFileProvider, ContentTypeProvider = CreateContentTypeProvider(), }); 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) }); } }
public static void UseAutoRebuild(this IApplicationBuilder appBuilder, BlazorConfig config) { // Currently this only supports VS for Windows. Later on we can add // an IRebuildService implementation for VS for Mac, etc. if (!VSForWindowsRebuildService.TryCreate(out var rebuildService)) { return; // You're not on Windows, or you didn't launch this process from VS } // Assume we're up to date when the app starts. var buildToken = new RebuildToken(new DateTime(1970, 1, 1)) { BuildTask = Task.CompletedTask, }; WatchFileSystem(config, () => { // Don't start the recompilation immediately. We only start it when the next // HTTP request arrives, because it's annoying if the IDE is constantly rebuilding // when you're making changes to multiple files and aren't ready to reload // in the browser yet. // // Replacing the token means that new requests that come in will trigger a rebuild, // and will all 'join' that build until a new file change occurs. buildToken = new RebuildToken(DateTime.Now); }); appBuilder.Use(async(context, next) => { try { var token = buildToken; if (token.BuildTask == null) { // The build is out of date, but a new build is not yet started. // // We can count on VS to only allow one build at a time, this is a safe race // because if we request a second concurrent build, it will 'join' the current one. var task = rebuildService.PerformRebuildAsync( config.SourceMSBuildPath, token.LastChange); token.BuildTask = task; } // In the general case it's safe to await this task, it will be a completed task // if everything is up to date. await token.BuildTask; } catch (Exception) { // If there's no listener on the other end of the pipe, or if anything // else goes wrong, we just let the incoming request continue. // There's nowhere useful to log this information so if people report // problems we'll just have to get a repro and debug it. // If it was an error on the VS side, it logs to the output window. } await next(); }); }
/// <summary> /// Configures the middleware pipeline to work with Blazor. /// </summary> /// <param name="app">The <see cref="IApplicationBuilder"/>.</param> /// <param name="options">Options to configure the middleware.</param> /// <returns>The <see cref="IApplicationBuilder"/> with configured Blazor.</returns> public static IApplicationBuilder UseLocalBlazor( this IApplicationBuilder app, 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)app.ApplicationServices.GetService(typeof(IHostingEnvironment)); var config = BlazorConfig.Read(options.ClientAssemblyPath); // First, match the request against files in the client app dist directory app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(config.DistPath), ContentTypeProvider = CreateContentTypeProvider(config.EnableDebugging), OnPrepareResponse = SetCacheHeaders }); // * Before publishing, we serve the wwwroot files directly from source // (and don't require them to be copied into dist). // In this case, WebRootPath will be nonempty if that directory exists. // * After publishing, the wwwroot files are already copied to 'dist' and // will be served by the above middleware, so we do nothing here. // In this case, WebRootPath will be empty (the publish process sets this). if (!string.IsNullOrEmpty(config.WebRootPath)) { app.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(config.WebRootPath), OnPrepareResponse = SetCacheHeaders }); } // Accept debugger connections if (config.EnableDebugging) { // Disable MonoDebugProxy, since it require me to maintain copy of Blazor code // app.UseMonoDebugProxy(); } // Finally, use SPA fallback routing (serve default page for anything else, // excluding /_framework/*) app.MapWhen(IsNotFrameworkDir, childAppBuilder => { var indexHtmlPath = FindIndexHtmlFile(config); var indexHtmlStaticFileOptions = string.IsNullOrEmpty(indexHtmlPath) ? null : new StaticFileOptions { FileProvider = new PhysicalFileProvider(Path.GetDirectoryName(indexHtmlPath)), OnPrepareResponse = SetCacheHeaders }; childAppBuilder.UseSpa(spa => { spa.Options.DefaultPageStaticFileOptions = indexHtmlStaticFileOptions; }); }); return(app); }
/// <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) { if (env.ApplicationName.Equals(DevServerApplicationName, StringComparison.OrdinalIgnoreCase)) { applicationBuilder.UseDevServerAutoRebuild(config); } else { applicationBuilder.UseHostedAutoRebuild(config, env.ContentRootPath); } } // 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; }); }); }
/// <summary> /// Configures the middleware pipeline to work with Blazor. /// </summary> /// <param name="aApplicationBuilder">The <see cref="IApplicationBuilder"/>.</param> /// <param name="aBlazorOptions">Options to configure the middleware.</param> /// <returns>The <see cref="IApplicationBuilder"/>.</returns> public static IApplicationBuilder UseBlazorDualMode( this IApplicationBuilder aApplicationBuilder, BlazorOptions aBlazorOptions) { aApplicationBuilder.UseStaticFiles(); // 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)aApplicationBuilder.ApplicationServices.GetService(typeof(IHostingEnvironment)); var config = BlazorConfig.Read(aBlazorOptions.ClientAssemblyPath); // The rebuild stuff is private and I dont' want to pull in all the source. // I frankly never get the auto stuff to work anyway //if (env.IsDevelopment() && config.EnableAutoRebuilding) //{ // if (env.ApplicationName.Equals(DevServerApplicationName, StringComparison.OrdinalIgnoreCase)) // { // app.UseDevServerAutoRebuild(config); // } // else // { // //app.UseHostedAutoRebuild(config, env.ContentRootPath); // } //} // First, match the request against files in the client app dist directory aApplicationBuilder.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(config.DistPath), ContentTypeProvider = CreateContentTypeProvider(config.EnableDebugging), OnPrepareResponse = SetCacheHeaders }); // * Before publishing, we serve the wwwroot files directly from source // (and don't require them to be copied into dist). // In this case, WebRootPath will be nonempty if that directory exists. // * After publishing, the wwwroot files are already copied to 'dist' and // will be served by the above middleware, so we do nothing here. // In this case, WebRootPath will be empty (the publish process sets this). if (!string.IsNullOrEmpty(config.WebRootPath)) { aApplicationBuilder.UseStaticFiles(new StaticFileOptions { FileProvider = new PhysicalFileProvider(config.WebRootPath), OnPrepareResponse = SetCacheHeaders }); } return(aApplicationBuilder); }
/// <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(), }; // 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) }); } // Definitely don't open a listener for live reloading in production, even if the // client app was compiled with live reloading enabled if (env.IsDevelopment()) { // Whether or not live reloading is actually enabled depends on the client config // For release builds, it won't be (by default) applicationBuilder.UseBlazorLiveReloading(config); } // Finally, use SPA fallback routing (serve default page for anything else, // excluding /_framework/*) applicationBuilder.MapWhen(IsNotFrameworkDir, childAppBuilder => { childAppBuilder.UseSpa(spa => { spa.Options.DefaultPageStaticFileOptions = distDirStaticFiles; }); }); }
/// <summary> /// Adds a <see cref="StaticFileMiddleware"/> that will serve static files from the client-side Blazor application /// specified by <paramref name="clientAssemblyFilePath"/>. /// </summary> /// <param name="clientAssemblyFilePath">The file path of the client-side Blazor application assembly.</param> /// <param name="app">The <see cref="IApplicationBuilder"/>.</param> /// <returns>The <see cref="IApplicationBuilder"/>.</returns> public static IApplicationBuilder UseClientSideBlazorFiles(this IApplicationBuilder app, string clientAssemblyFilePath) { if (clientAssemblyFilePath == null) { throw new ArgumentNullException(nameof(clientAssemblyFilePath)); } var fileProviders = new List <IFileProvider>(); // 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 config = BlazorConfig.Read(clientAssemblyFilePath); // First, match the request against files in the client app dist directory fileProviders.Add(new PhysicalFileProvider(config.DistPath)); // * Before publishing, we serve the wwwroot files directly from source // (and don't require them to be copied into dist). // In this case, WebRootPath will be nonempty if that directory exists. // * After publishing, the wwwroot files are already copied to 'dist' and // will be served by the above middleware, so we do nothing here. // In this case, WebRootPath will be empty (the publish process sets this). if (!string.IsNullOrEmpty(config.WebRootPath)) { fileProviders.Add(new PhysicalFileProvider(config.WebRootPath)); } // We can't modify an IFileContentTypeProvider, so we have to decorate. var contentTypeProvider = new FileExtensionContentTypeProvider(); AddMapping(contentTypeProvider, ".dll", MediaTypeNames.Application.Octet); if (config.EnableDebugging) { AddMapping(contentTypeProvider, ".pdb", MediaTypeNames.Application.Octet); } var options = new StaticFileOptions() { ContentTypeProvider = contentTypeProvider, FileProvider = new CompositeFileProvider(fileProviders), OnPrepareResponse = CacheHeaderSettings.SetCacheHeaders, }; app.UseStaticFiles(options); return(app);
/// <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 clientAppBinDir = Path.GetDirectoryName(config.SourceOutputAssemblyPath); var clientAppDistDir = Path.Combine( env.ContentRootPath, Path.Combine(clientAppBinDir, "dist")); var distDirStaticFiles = new StaticFileOptions { FileProvider = new PhysicalFileProvider(clientAppDistDir), ContentTypeProvider = CreateContentTypeProvider(), }; // 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) }); } // Finally, use SPA fallback routing (serve default page for anything else, // excluding /_framework/*) applicationBuilder.MapWhen(IsNotFrameworkDir, childAppBuilder => { childAppBuilder.UseSpa(spa => { spa.Options.DefaultPageStaticFileOptions = distDirStaticFiles; }); }); }
/// <summary> /// Adds a low-priority endpoint that will serve the the file specified by <paramref name="filePath"/> from the client-side /// Blazor application specified by <paramref name="clientAssemblyFilePath"/>. /// </summary> /// <param name="clientAssemblyFilePath">The file path of the client-side Blazor application assembly.</param> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param> /// <param name="pattern">The route pattern to match.</param> /// <param name="filePath"> /// The relative path to the entry point of the client-side application. The path is relative to the /// <see cref="IWebHostEnvironment.WebRootPath"/>, commonly <c>wwwroot</c>. /// </param> /// <returns>The <see cref="IApplicationBuilder"/>.</returns> /// <remarks> /// <para> /// This method is intended to handle cases where URL path of the request does not contain a filename, and no other /// endpoint has matched. This is convenient for routing requests for dynamic content to the client-side blazor /// application, while also allowing requests for non-existent files to result in an HTTP 404. /// </para> /// </remarks> public static IEndpointConventionBuilder MapFallbackToClientSideBlazor(this IEndpointRouteBuilder endpoints, string clientAssemblyFilePath, string pattern, string filePath) { if (endpoints == null) { throw new ArgumentNullException(nameof(endpoints)); } if (clientAssemblyFilePath == null) { throw new ArgumentNullException(nameof(clientAssemblyFilePath)); } if (pattern == null) { throw new ArgumentNullException(nameof(pattern)); } if (filePath == null) { throw new ArgumentNullException(nameof(filePath)); } var config = BlazorConfig.Read(clientAssemblyFilePath); // We want to serve "index.html" from whichever directory contains it in this priority order: // 1. Client app "dist" directory // 2. Client app "wwwroot" directory // 3. Server app "wwwroot" directory var directory = endpoints.ServiceProvider.GetRequiredService <IWebHostEnvironment>().WebRootPath; var indexHtml = config.FindIndexHtmlFile(); if (indexHtml != null) { directory = Path.GetDirectoryName(indexHtml); } var options = new StaticFileOptions() { FileProvider = new PhysicalFileProvider(directory), OnPrepareResponse = CacheHeaderSettings.SetCacheHeaders, }; return(endpoints.MapFallbackToFile(pattern, filePath, options)); }
private static void WatchFileSystem(BlazorConfig config, Action onWrite) { var clientAppRootDir = Path.GetDirectoryName(config.SourceMSBuildPath); var excludePathPrefixes = _excludedDirectories.Select(subdir => Path.Combine(clientAppRootDir, subdir) + Path.DirectorySeparatorChar); var fsw = new FileSystemWatcher(clientAppRootDir); fsw.Created += OnEvent; fsw.Changed += OnEvent; fsw.Deleted += OnEvent; fsw.Renamed += OnEvent; fsw.IncludeSubdirectories = true; fsw.EnableRaisingEvents = true; // Ensure the watcher is not GCed for as long as the app lives lock (_uncollectableWatchers) { _uncollectableWatchers.Add(fsw); } void OnEvent(object sender, FileSystemEventArgs eventArgs) { if (!File.Exists(eventArgs.FullPath)) { // It's probably a directory rather than a file return; } if (!_includedSuffixes.Any(ext => eventArgs.Name.EndsWith(ext, StringComparison.OrdinalIgnoreCase))) { // Not a candiate file type return; } if (excludePathPrefixes.Any(prefix => eventArgs.FullPath.StartsWith(prefix, StringComparison.Ordinal))) { // In an excluded subdirectory return; } onWrite(); } }
// TODO: Change this combination of APIs to make it possible to supply either // an assembly name (resolved to current bin dir) or full assembly path internal static void UseBlazorInternal( this IApplicationBuilder applicationBuilder, string clientAssemblyPath) { var config = BlazorConfig.Read(clientAssemblyPath); var clientAppBinDir = Path.GetDirectoryName(config.SourceOutputAssemblyPath); var clientAppDistDir = Path.Combine(clientAppBinDir, "dist"); var distFileProvider = new PhysicalFileProvider(clientAppDistDir); applicationBuilder.UseDefaultFiles(new DefaultFilesOptions { FileProvider = distFileProvider }); applicationBuilder.UseStaticFiles(new StaticFileOptions { FileProvider = distFileProvider, ContentTypeProvider = CreateContentTypeProvider(), }); }
/// <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) { var config = BlazorConfig.Read(options.ClientAssemblyPath); var clientAppBinDir = Path.GetDirectoryName(config.SourceOutputAssemblyPath); var clientAppDistDir = Path.Combine(clientAppBinDir, "dist"); var distDirStaticFiles = new StaticFileOptions { FileProvider = new PhysicalFileProvider(clientAppDistDir), ContentTypeProvider = CreateContentTypeProvider(), }; // 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) }); } // Finally, use SPA fallback routing (serve default page for anything else, // excluding /_framework/*) applicationBuilder.MapWhen(IsNotFrameworkDir, childAppBuilder => { childAppBuilder.UseSpa(spa => { spa.Options.DefaultPageStaticFileOptions = distDirStaticFiles; }); }); }
public static void UseHostedAutoRebuild(this IApplicationBuilder appBuilder, BlazorConfig config, string hostAppContentRootPath) { var isFirstFileWrite = true; WatchFileSystem(config, () => { if (isFirstFileWrite) { try { // Touch any .cs file to force the host project to rebuild // (which in turn rebuilds the client, since it's referenced) var fileToTouch = Directory.EnumerateFiles( hostAppContentRootPath, "*.cs", SearchOption.AllDirectories).FirstOrDefault(); if (!string.IsNullOrEmpty(fileToTouch)) { File.SetLastWriteTime(fileToTouch, DateTime.Now); } } catch (Exception ex) { // If we don't have permission to write these files, autorebuild will not be enabled var loggerFactory = appBuilder.ApplicationServices.GetRequiredService <ILoggerFactory>(); var logger = loggerFactory.CreateLogger(typeof(AutoRebuildExtensions)); logger?.LogWarning(ex, "Cannot autorebuild because there was an error when writing to a file in '{0}'.", hostAppContentRootPath); } isFirstFileWrite = false; } }); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { MvcConfig.Configure(app, env); AuthenticationConfig.Configure(app, env); BlazorConfig.Configure(app, env); }