dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.19k stars 9.93k forks source link

Blazor WebApp Wasm Hot Reload does not work with UseRouting #52339

Open Jejuni opened 9 months ago

Jejuni commented 9 months ago

Is there an existing issue for this?

Describe the bug

When using the UseRouting middleware as described in the migration documentation (the part about adding UseAntiforgery right after UseRouting: https://learn.microsoft.com/en-us/aspnet/core/migration/70-80?view=aspnetcore-8.0&tabs=visual-studio#convert-a-hosted-blazor-webassembly-app-into-a-blazor-web-app) hot reload no longer works and fails with the error mentioned in the Exception section

Expected Behavior

UseRouting should continue to function when used with Blazor WebApp

Steps To Reproduce

Repro Github: https://github.com/Jejuni/BlazorWebAppRoutingProblem

The repro is simply the default Blazor WebApp Wasm Global Template with app.UseRouting(); added directly before UseAntiforgery

To produce the error start the app via F5, then make a simple change in Home.razor and try to hot reload

Exceptions (if any)

image

The browser displays:

at System.Reflection.Metadata.MetadataUpdater.ApplyUpdate(Assembly assembly, ReadOnlySpan`1 metadataDelta, ReadOnlySpan`1 ilDelta, ReadOnlySpan`1 pdbDelta)
   at Microsoft.Extensions.HotReload.HotReloadAgent.ApplyDeltas(IReadOnlyList`1 deltas)
   at Microsoft.AspNetCore.Components.WebAssembly.HotReload.WebAssemblyHotReload.ApplyHotReloadDelta(String moduleIdString, Byte[] metadataDelta, Byte[] ilDelta, Byte[] pdbBytes)
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
--- End of stack trace from previous location ---
   at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.InvokeSynchronously(JSRuntime jsRuntime, DotNetInvocationInfo& callInfo, IDotNetObjectReference objectReference, String argsJson)
   at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.Invoke(JSRuntime jsRuntime, DotNetInvocationInfo& invocationInfo, String argsJson)
   at Microsoft.AspNetCore.Components.WebAssembly.Services.DefaultWebAssemblyJSRuntime.InvokeDotNet(String assemblyName, String methodIdentifier, Int64 dotNetObjectId, String argsJson)
   at Microsoft.AspNetCore.Components.WebAssembly.Services.DefaultWebAssemblyJSRuntime.__Wrapper_InvokeDotNet_478490305(JSMarshalerArgument* __arguments_buffer)
Error: The assembly 'BlazorWebAppRoutingProblem.Client.dll' cannot be edited or changed, because environment variable DOTNET_MODIFIABLE_ASSEMBLIES is set to '(null)', not 'Debug'
    at Jn (http://localhost:5255/_framework/dotnet.runtime.8.0.0.0qodnbme20.js:3:31614)
    at kr (http://localhost:5255/_framework/dotnet.runtime.8.0.0.0qodnbme20.js:3:35529)
    at Object.<anonymous> (http://localhost:5255/_framework/dotnet.runtime.8.0.0.0qodnbme20.js:3:180449)
    at Object.invokeDotNetFromJS (http://localhost:5255/_framework/blazor.web.js:1:156087)
    at y.invokeDotNetMethod (http://localhost:5255/_framework/blazor.web.js:1:3851)
    at y.invokeDotNetStaticMethod (http://localhost:5255/_framework/blazor.web.js:1:3607)
    at vt._internal.applyHotReload (http://localhost:5255/_framework/blazor.web.js:1:161564)
    at http://localhost:5255/_framework/aspnetcore-browser-refresh.js:150:35
    at Array.forEach (<anonymous>)
    at applyBlazorDeltas (http://localhost:5255/_framework/aspnetcore-browser-refresh.js:148:14)

.NET Version

8.0.100

Anything else?

Asp.Net Core Version: 8.0.0 IDE: VS 17.8.1

Solbstr commented 9 months ago

I came across this issue and wanted to share that I am experiencing a very similar problem in my Blazor wasm hosted application after migrating to .NET 8. Just like in the case described, I encounter an error stating that the assembly cannot be edited or changed due to the DOTNET_MODIFIABLE_ASSEMBLIES environment variable being set to '(null)', not 'Debug'.

I have tried setting DOTNET_MODIFIABLE_ASSEMBLIES to 'Debug' in various ways (including in Program.cs, through system environment variables, and via the command line), but the issue persists. My application's middleware configuration closely follows the recommended pattern, with app.UseAntiforgery() placed correctly between app.UseRouting() and app.UseEndpoints().

Any additional insights or solutions would be greatly appreciated, as this issue is impacting the efficiency of my development process.

Jejuni commented 9 months ago

My application's middleware configuration closely follows the recommended pattern, with app.UseAntiforgery() placed correctly between app.UseRouting() and app.UseEndpoints().

Any additional insights or solutions would be greatly appreciated, as this issue is impacting the efficiency of my development process.

@Solbstr, I would say if you are using the "minimal API approach" (the one with no Startup.cs, whether you have controllers or not does not matter) simply remove app.useRouting() and app.UseEndpoints(). Both are only required in very special cases where you need to change the order of middleware. As per the official routing documentation:

Apps typically don't need to call UseRouting or UseEndpoints. WebApplicationBuilder configures a middleware pipeline that wraps middleware added in Program.cs with UseRouting and UseEndpoints

If nothing else just try to remove UseRouting. That fixed it for me

Solbstr commented 9 months ago

Thanks @Jejuni , seems that hot reload started working again after removal of app.useRouting().

ghost commented 9 months ago

To learn more about what this message means, what to expect next, and how this issue will be handled you can read our Triage Process document. We're moving this issue to the .NET 9 Planning milestone for future evaluation / consideration. Because it's not immediately obvious what is causing this behavior, we would like to keep this around to collect more feedback, which can later help us determine how to handle this. We will re-evaluate this issue, during our next planning meeting(s). If we later determine, that the issue has no community involvement, or it's very rare and low-impact issue, we will close it - so that the team can focus on more important and high impact work.

NielsPilgaard commented 9 months ago

I have the same issue, but removing app.UseRouting() causes my application to no longer render, giving this error in the browser console:

Error: One or more errors occurred. ('<' is an invalid start of a value. LineNumber: 0 | BytePositionInLine: 0.)
    at Jn (marshal-to-js.ts:349:18)
    at Ul (marshal-to-js.ts:306:28)
    at 00b1e8b6:0x1faca
    at 00b1e8b6:0x1bf8b
    at 00b1e8b6:0xf172
    at 00b1e8b6:0x1e7e4
    at 00b1e8b6:0x1efda
    at 00b1e8b6:0xcfec
    at 00b1e8b6:0x440ad
    at e.<computed> (cwraps.ts:338:24)

Do you guys have any suggestions other than removing app.UseRouting()? 😄

MJohnsson commented 6 months ago

We have the same issue as @NielsPilgaard. When we remove UseRouting it seems that all css/js-files gets the context of App.razor.

Working with frontend without hot-reload and close to no inspection support (children could not be evaluated on every semi-complex model) is getting very costly.

Surprised this fix is scheduled for .net9 :(

NielsPilgaard commented 6 months ago

I managed to find a workaround, this very specific order of middleware allowed hot reload to work again, with UseRouting

app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseAntiforgery();
MJohnsson commented 6 months ago

So, it seems that app.UseBlazorFrameworkFiles();, placed like mentioned above, makes Hot-reload work again. But this middleware is not supposed to be used in .net 8 as far as I understand (atleast the migration documentation states to remove that command).

Not sure if it adds any sort of regression or unwanted behaviour. Might be a quick-fix for local development, so thanks for the tip.

TheDusty01 commented 4 months ago

From my testing it's apparently enough to just place app.UseBlazorFrameworkFiles() above app.UseStaticFiles().

// Other middleware like auth, swagger etc.

app.UseBlazorFrameworkFiles();
app.UseStaticFiles();

app.MapFallbackToFile("index.html");

app.Run();
eldo-xy commented 4 weeks ago

Also ran in to this today, UseBlazorFrameworkFiles() fixes it but as said I thought this was not meant to be used anymore