Fix static file handling so that it special cases modules.json (#18953)

## Issue: Microsoft.AspNetCore.Components.WebView.props is not imported by Maui applications

This issue pertains to the non-importation of Microsoft.AspNetCore.Components.WebView.props by Maui applications. The file has been relocated within the package to ensure its correct pickup and importation during the restore/build process.

Fixes dotnet/aspnetcore#42348.

## Description

The inability of Maui applications to rely on JavaScript (JS) initializers, particularly in the case of Fluent UI, is impacted by this issue. The runtime searches for the initializer's definition in a specific location, which is configured in the props file of the package.

## Impact on Customers

The malfunction of JS initializers in Blazor Hybrid has significant implications for both library authors and end users.

Library authors may find their libraries' functionality restricted due to this bug. JS initializers, a feature in Blazor, enable library authors to inject scripts onto the page at the start of the app. These scripts can augment functionality, enhance user interfaces, or facilitate third-party service integration. For instance, a library author might employ a JS initializer to inject a script that integrates with a mapping service, thereby providing real-time location updates within a Blazor app. This functionality would be unavailable in Blazor Hybrid apps due to this bug.

End users may be unable to use certain libraries, or those libraries may not function as anticipated in Blazor Hybrid apps. If a user were to use a Blazor Hybrid app that relies on the aforementioned mapping library, they would not receive the real-time location updates that they would in a regular Blazor app. This could result in an inferior user experience, and in some cases, render the app unusable.

Users and library authors are compelled to manually inject the script onto the page, and some functionality (like configuring Blazor before it starts) is not available in this mode.

## Regression?

- [ ] Yes
- [X] No

## Risk

- [ ] High
- [ ] Medium
- [X] Low

The failure to load a file from a NuGet package impacts the build. The change causes the file to load at build time, enabling the rest of the pipeline to function as expected.

## Verification

- [X] Manual (required)
- [ ] Automated

The changes were made locally on the package cache and ensured the file got imported.

## Packaging changes reviewed?

- [ ] Yes
- [ ] No
- [x] N/A

## When servicing release/2.1

- [ ] Make necessary changes in eng/PatchConfig.props
This commit is contained in:
Javier Calvarro Nelson 2023-11-27 23:52:00 +01:00 коммит произвёл GitHub
Родитель 34d73c8ee5
Коммит 4a31ee1f34
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 103 добавлений и 69 удалений

Просмотреть файл

@ -0,0 +1,3 @@
const element = document.createElement('p');
element.innerHTML = 'Hello from Razor Class Library';
document.body.appendChild(element);

Просмотреть файл

@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.InteropServices.WindowsRuntime; using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -84,88 +85,115 @@ namespace Microsoft.AspNetCore.Components.WebView.Maui
_logger.HandlingWebRequest(requestUri); _logger.HandlingWebRequest(requestUri);
// First, call into WebViewManager to see if it has a framework file for this request. It will var uri = new Uri(requestUri);
// fall back to an IFileProvider, but on WinUI it's always a NullFileProvider, so that will never var relativePath = AppOriginUri.IsBaseOf(uri) ? AppOriginUri.MakeRelativeUri(uri).ToString() : null;
// return a file.
if (TryGetResponseContent(requestUri, allowFallbackOnHostPage, out var statusCode, out var statusMessage, out var content, out var headers) // Check if the uri is _framework/blazor.modules.json is a special case as the built-in file provider
// brings in a default implementation.
if (relativePath != null &&
string.Equals(relativePath, "_framework/blazor.modules.json", StringComparison.Ordinal) &&
await TryServeFromFolderAsync(eventArgs, allowFallbackOnHostPage: false, requestUri, relativePath))
{
_logger.ResponseContentBeingSent(requestUri, 200);
}
else if (TryGetResponseContent(requestUri, allowFallbackOnHostPage, out var statusCode, out var statusMessage, out var content, out var headers)
&& statusCode != 404) && statusCode != 404)
{ {
// First, call into WebViewManager to see if it has a framework file for this request. It will
// fall back to an IFileProvider, but on WinUI it's always a NullFileProvider, so that will never
// return a file.
var headerString = GetHeaderString(headers); var headerString = GetHeaderString(headers);
_logger.ResponseContentBeingSent(requestUri, statusCode); _logger.ResponseContentBeingSent(requestUri, statusCode);
eventArgs.Response = _coreWebView2Environment!.CreateWebResourceResponse(content.AsRandomAccessStream(), statusCode, statusMessage, headerString); eventArgs.Response = _coreWebView2Environment!.CreateWebResourceResponse(content.AsRandomAccessStream(), statusCode, statusMessage, headerString);
} }
else if (new Uri(requestUri) is Uri uri && AppOriginUri.IsBaseOf(uri)) else if (relativePath != null)
{ {
var relativePath = AppOriginUri.MakeRelativeUri(uri).ToString(); await TryServeFromFolderAsync(
eventArgs,
// If the path does not end in a file extension (or is empty), it's most likely referring to a page, allowFallbackOnHostPage,
// in which case we should allow falling back on the host page. requestUri,
if (allowFallbackOnHostPage && !Path.HasExtension(relativePath)) relativePath);
{
relativePath = _hostPageRelativePath;
}
relativePath = Path.Combine(_contentRootRelativeToAppRoot, relativePath.Replace('/', '\\'));
statusCode = 200;
statusMessage = "OK";
var contentType = StaticContentProvider.GetResponseContentTypeOrDefault(relativePath);
headers = StaticContentProvider.GetResponseHeaders(contentType);
IRandomAccessStream? stream = null;
if (_isPackagedApp)
{
var winUIItem = await Package.Current.InstalledLocation.TryGetItemAsync(relativePath);
if (winUIItem != null)
{
using var contentStream = await Package.Current.InstalledLocation.OpenStreamForReadAsync(relativePath);
stream = await CopyContentToRandomAccessStreamAsync(contentStream);
}
}
else
{
var path = Path.Combine(AppContext.BaseDirectory, relativePath);
if (File.Exists(path))
{
using var contentStream = File.OpenRead(path);
stream = await CopyContentToRandomAccessStreamAsync(contentStream);
}
}
var hotReloadedContent = Stream.Null;
if (StaticContentHotReloadManager.TryReplaceResponseContent(_contentRootRelativeToAppRoot, requestUri, ref statusCode, ref hotReloadedContent, headers))
{
stream = await CopyContentToRandomAccessStreamAsync(hotReloadedContent);
}
if (stream != null)
{
var headerString = GetHeaderString(headers);
_logger.ResponseContentBeingSent(requestUri, statusCode);
eventArgs.Response = _coreWebView2Environment!.CreateWebResourceResponse(
stream,
statusCode,
statusMessage,
headerString);
}
else
{
_logger.ReponseContentNotFound(requestUri);
}
async Task<IRandomAccessStream> CopyContentToRandomAccessStreamAsync(Stream content)
{
using var memStream = new MemoryStream();
await content.CopyToAsync(memStream);
var randomAccessStream = new InMemoryRandomAccessStream();
await randomAccessStream.WriteAsync(memStream.GetWindowsRuntimeBuffer());
return randomAccessStream;
}
} }
// Notify WebView2 that the deferred (async) operation is complete and we set a response. // Notify WebView2 that the deferred (async) operation is complete and we set a response.
deferral.Complete(); deferral.Complete();
} }
private async Task<bool> TryServeFromFolderAsync(
CoreWebView2WebResourceRequestedEventArgs eventArgs,
bool allowFallbackOnHostPage,
string requestUri,
string relativePath)
{
// If the path does not end in a file extension (or is empty), it's most likely referring to a page,
// in which case we should allow falling back on the host page.
if (allowFallbackOnHostPage && !Path.HasExtension(relativePath))
{
relativePath = _hostPageRelativePath;
}
relativePath = Path.Combine(_contentRootRelativeToAppRoot, relativePath.Replace('/', '\\'));
var statusCode = 200;
var statusMessage = "OK";
var contentType = StaticContentProvider.GetResponseContentTypeOrDefault(relativePath);
var headers = StaticContentProvider.GetResponseHeaders(contentType);
IRandomAccessStream? stream = null;
if (_isPackagedApp)
{
var winUIItem = await Package.Current.InstalledLocation.TryGetItemAsync(relativePath);
var location = Package.Current.InstalledLocation.Path;
if (winUIItem != null)
{
using var contentStream = await Package.Current.InstalledLocation.OpenStreamForReadAsync(relativePath);
stream = await CopyContentToRandomAccessStreamAsync(contentStream);
}
}
else
{
var path = Path.Combine(AppContext.BaseDirectory, relativePath);
if (File.Exists(path))
{
using var contentStream = File.OpenRead(path);
stream = await CopyContentToRandomAccessStreamAsync(contentStream);
}
}
var hotReloadedContent = Stream.Null;
if (StaticContentHotReloadManager.TryReplaceResponseContent(_contentRootRelativeToAppRoot, requestUri, ref statusCode, ref hotReloadedContent, headers))
{
stream = await CopyContentToRandomAccessStreamAsync(hotReloadedContent);
}
if (stream != null)
{
var headerString = GetHeaderString(headers);
_logger.ResponseContentBeingSent(requestUri, statusCode);
eventArgs.Response = _coreWebView2Environment!.CreateWebResourceResponse(
stream,
statusCode,
statusMessage,
headerString);
return true;
}
else
{
_logger.ReponseContentNotFound(requestUri);
}
return false;
async Task<IRandomAccessStream> CopyContentToRandomAccessStreamAsync(Stream content)
{
using var memStream = new MemoryStream();
await content.CopyToAsync(memStream);
var randomAccessStream = new InMemoryRandomAccessStream();
await randomAccessStream.WriteAsync(memStream.GetWindowsRuntimeBuffer());
return randomAccessStream;
}
}
/// <inheritdoc /> /// <inheritdoc />
protected override void QueueBlazorStart() protected override void QueueBlazorStart()
{ {

Просмотреть файл

@ -0,0 +1,3 @@
const element = document.createElement('p');
element.innerHTML = 'Hello from App';
document.body.appendChild(element);