unoplatform / Uno.Wasm.Bootstrap

A simple nuget package to run C# code in a WASM-compatible browser
Other
367 stars 57 forks source link

[Wasm] Debugging and Hot Reload support in ASP.NET Core hosting scenario #483

Open mmarinchenko opened 2 years ago

mmarinchenko commented 2 years ago

Relates to:


It would be great if Uno had the facility to use Uno.Wasm.Bootstrap.DevServer and Uno.UI.RemoteControl in ASP.NET Core hosting scenario.

For example, Microsoft.AspNetCore.Components.WebAssembly.DevServer package has Microsoft.AspNetCore.Components.WebAssembly.Server counterpart for this.

jeromelaban commented 2 years ago

Thanks for the report. Are you able to do this with a Blazor app? If so, what's the setup that you need to do this?

mmarinchenko commented 2 years ago

Thanks for quick response, @jeromelaban! I'll provide the details a bit later.

mmarinchenko commented 2 years ago

Blazor WebAssembly Build Process

The net6.0 target framework provides Microsoft.NET.Sdk.BlazorWebAssembly SDK for client-side Wasm apps.

When using this SDK the _framework/* files are deployed to content root (typically the bin\{Configuration}\net6.0\wwwroot folder) as part of the build process. See Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets for more details.

ASP.NET Core Build Process

ASP.NET Core uses Microsoft.NET.Sdk.Web SDK, which internally references Microsoft.NET.Sdk.Razor SDK.

The Microsoft.NET.Sdk.Razor SDK provides the facility to resolve static web assets of referenced projects as part of the build process. See Microsoft.NET.Sdk.Razor.StaticWebAssets.targets for more details.

ASP.NET Core Hosting

ASP.NET Core provides Microsoft.AspNetCore.Components.WebAssembly.Server NuGet package which basically contains 2 extension methods as public API:

  1. UseWebAssemblyDebugging(), which is intended only for Development environment. It serves the /_framework/debug request path, opens port 9222 on localhost for browser dev tools, and so on.
  2. UseBlazorFrameworkFiles([PathString pathPrefix = default]), which configures content type provider for static _framework/* files from the Blazor WebAssembly project and serves them.

Required Project Setup

So the following Blazor WebAssembly project:

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.*" />
  </ItemGroup>

</Project>

Can be referenced in ASP.NET Core Hosting project as follows:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="$(BlazorWebAssemblyProjectPath)" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="6.0.*" />
  </ItemGroup>

</Project>

Middlewares from the Microsoft.AspNetCore.Components.WebAssembly.Server NuGet package:

var app = WebApplication.CreateBuilder(args).Build();

if (app.Environment.IsDevelopment())
{
    app.UseWebAssemblyDebugging(); // 1
}

app.UseBlazorFrameworkFiles(); // 2, path prefix can be passed as an argument
app.UseStaticFiles(); // serve application content root, should be after UseBlazorFrameworkFiles()

app.UseRouting();
app.MapFallbackToFile("index.html");

app.Run();

As a result it is sufficient to build, publish, and run only ASP.NET Core Hosting project. Because in this case:

  1. The Blazor WebAssembly project will always be built before ASP.NET Core Hosting project. Thus the Blazor WebAssembly content root (including _framework/* files) will always be up to date.
  2. The ASP.NET Core Hosting project will always resolve Blazor WebAssembly content root (including _framework/* files) as referenced static web assets and include them in its own output.
  3. Middlewares, added to the ASP.NET Core Hosting project, will act as embedded DevServer for Blazor WebAssembly.

What about Uno?

Uno.Wasm.Bootstrap cannot be used with this approach. Blazor has specifics unrelated to Uno and vice versa.

For example, Uno Wasm head cannot be referenced in ASP.NET Core Hosting project directly, because Uno generates intermediate stuff in content root under bin/ and ASP.NET Core does not aware about WasmShellDistPath. Though custom targets may be used in ASP.NET Core Hosting project to invoke the build of Uno Wasm head using the MSBuild task and then copy static web assets from WasmShellDistPath similarly to https://github.com/unoplatform/Uno.Wasm.Bootstrap/issues/191#issuecomment-955041893.

But the main concern is middlewares. Blazor middlewares does not aware about WasmShellDistPath structure, *.clr file extension, /_framework/unohotreload request path, etc. Therefore it would be nice if the code from Uno.Wasm.Bootstrap.DevServer will be adapted to provide similar middlewares as public API.

Let's imagine a hypothetical Uno.Wasm.Bootstrap.Server (or maybe Uno.Wasm.Bootstrap.Hosting) package with UseUnoWasmHeadDebugging() and UseUnoWasmShellDistFiles([PathString pathPrefix = default]) extension methods.

P.S. Uno.UI.RemoteControl package is not specific to Wasm head but if I understand correctly it still requires the /_framework/unohotreload and /_framework/blazor-hotreload.js request paths to be served.

jeromelaban commented 2 years ago

Thanks for the details, this clear quite a bit! I'd assume that creating such a package would be possible, considering a large portion of the hosting is similar to what blazor does, even if it's only integrated in the "dev server".

wzxinchen commented 2 years ago

Any solution for hot reload without blazor?