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.37k stars 9.99k forks source link

NavigationException when attempting to navigate after submitting a form #49143

Closed danroth27 closed 1 year ago

danroth27 commented 1 year ago

Repro steps:

@page "/"
@inject NavigationManager NavigationManager

<PageTitle>Index</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

<EditForm method="post" Model="new()" OnValidSubmit="Navigate">
    <button type="submit">Submit and navigate</button>
</EditForm>

@code {
    void Navigate()
    {
        Console.WriteLine("Navigate");
        NavigationManager.NavigateTo("/counter");
    }
}

Expected result: Form is submitted and app navigates to the counter page Actual result:

Microsoft.AspNetCore.Components.NavigationException: Exception of type 'Microsoft.AspNetCore.Components.NavigationException' was thrown.
   at Microsoft.AspNetCore.Components.Server.Circuits.RemoteNavigationManager.NavigateToCore(String uri, NavigationOptions options)
   at Microsoft.AspNetCore.Components.NavigationManager.NavigateToCore(String uri, Boolean forceLoad)
   at Microsoft.AspNetCore.Components.NavigationManager.NavigateTo(String uri, Boolean forceLoad, Boolean replace)
   at BlazorApp3.Pages.Index.Navigate() in C:\Users\daroth\source\repos\BlazorApp3\BlazorApp3\Pages\Index.razor:line 18
   at Microsoft.AspNetCore.Components.EventCallbackWorkItem.InvokeAsync[T](MulticastDelegate delegate, T arg)
   at Microsoft.AspNetCore.Components.ComponentBase.Microsoft.AspNetCore.Components.IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, Object arg)
   at Microsoft.AspNetCore.Components.EventCallback`1.InvokeAsync(TValue arg)
   at Microsoft.AspNetCore.Components.Forms.EditForm.HandleSubmitAsync()
   at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
   at Microsoft.AspNetCore.Components.Endpoints.RazorComponentEndpointInvoker.RenderComponentCore()
   at Microsoft.AspNetCore.Components.Endpoints.RazorComponentEndpointInvoker.RenderComponentCore()
   at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext.<>c.<<InvokeAsync>b__9_0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

HEADERS
=======
Accept: */*
Host: localhost:7108
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.51
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryc1xzNe1hpeP893LF
Cookie: .AspNetCore.Antiforgery.IeH0rP6rx9A=CfDJ8F4wj4CPUDJJpKiv_dArKQq7-zlTyLTppJPATs2r_sPqwv8mRiR6HIH34f0IqKK0Ah0Xrt_EqvXLOj0sR0epDhOMweYTt-TA4ZXGW_plxoQ85aIu_hmmOp-ypV-fltkIUoDYZ6TBl74EW4yHbVUfF54
Origin: https://localhost:7108
Referer: https://localhost:7108/
Content-Length: 44
sec-ch-ua: "Not.A/Brand";v="8", "Chromium";v="114", "Microsoft Edge";v="114"
sec-ch-ua-mobile: ?0
blazor-enhanced-nav: on
sec-ch-ua-platform: "Windows"
sec-fetch-site: same-origin
sec-fetch-mode: cors
sec-fetch-dest: empty
javiercn commented 1 year ago

This is happening because the Blazor Server navigation manager is overriding the one for endpoints RemoteNavigationManager

SteveSandersonMS commented 1 year ago

Are you sure? It doesn't look like there even is a Blazor Server navigation manager in this case.

I think what's happening is that DispatchCapturedEvent runs at the wrong time, for two reasons:

  1. It's not wrapped in any try/catch(NavigationException) hence the immediate problem here
  2. It runs after we've reached quiescence, but if it causes any async work, then it will sets quiesceTask to an incomplete task. This means the renderer goes into the streaming SSR code paths even if streaming SSR is not enabled, which would be a bug.

Instead I think DispatchCapturedEvent should be triggered inside EndpointHtmlRenderer's RenderEndpointComponent, right after await WaitForResultReady. At this point, the outer code doesn't yet consider the renderer quiescent, so DispatchCapturedEvent is free to add other async work which will be considered part of the initial page render (without causing streaming SSR to start), plus it will automatically be inside the existing catch for NavigationException so that would just work.

danroth27 commented 1 year ago

@javiercn @SteveSandersonMS Are there any workarounds for this issue with the current bits?

SteveSandersonMS commented 1 year ago

Done in https://github.com/dotnet/aspnetcore/pull/49472