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.49k stars 10.03k forks source link

.NET 8 Blazor form processing with action to Razor Components Page with static SSR #56445

Closed likquietlyy closed 2 days ago

likquietlyy commented 4 months ago

Is there an existing issue for this?

Describe the bug

I have Login.razor page(@page "/login") with InteractiveServer Render Mode. There is a <form> on this page with method="post", @formname="uniqueFormName" and action="login/submit"(@page "login/submit" with Static SSR mode). On submitting this form I get an error on page login/submit.

Expected Behavior

Login.razor page with InteractiveServer Render Mode(cause I need some interactivity on this page), and LoginSubmit.razor page with static SSR to process a post request from form on login page.

Steps To Reproduce

Login.razor

@page "/login"

<form method="post" action="login/submit" @formname="uniqueLoginForm">
  <input id="name" class="form-control" name="name" type="text"
         placeholder="User Name" required autofocus>
  <input id="password" class="form-control" name="password" type="password"
         placeholder="Password" required>
  <label class="form-check-label" for="rememberMe">
    <input id="rememberMe" class="form-check-input" name="rememberMe" type="checkbox" value="true">
    <span>Remember Me</span>
  </label>
  <input type="submit" value="Login"/>
  <AntiforgeryToken></AntiforgeryToken>
</form>

LoginSubmit.razor

@page "/login/submit"
@inject IHttpContextAccessor HttpContextAccessor
@inject NavigationManager NavigationManager

@code {
  protected override async Task OnParametersSetAsync()
  {
    ArgumentNullException.ThrowIfNull(HttpContextAccessor.HttpContext);

    var name = HttpContextAccessor.HttpContext.Request.Form["name"];
    var claims = new List<Claim> { new(ClaimTypes.Name, name) };

    var claimsIdentity = new ClaimsIdentity(claims, "UI");
    var userPrincipal = new ClaimsPrincipal([claimsIdentity]);

    await HttpContextAccessor.HttpContext.SignInAsync(userPrincipal, new AuthenticationProperties
    {
      IsPersistent = true
    });
  }
}

App.razor

  <HeadOutlet @rendermode="@RenderModeForPage" />
  <Routes @rendermode="@RenderModeForPage" />
@code {
  [CascadingParameter]
  private HttpContext HttpContext { get; set; } = default!;

  private IComponentRenderMode? RenderModeForPage => HttpContext.Request.Path.StartsWithSegments("/login/submit")
      ? null
      : new InteractiveServerRenderMode();
}

Exceptions (if any)

The POST request does not specify which form is being submitted. To fix this, ensure <form> elements have a @formname attribute with any unique value, or pass a FormName parameter if using <EditForm>.

.NET Version

8.0.302

Anything else?

No response

MackinnonBuck commented 4 months ago

Thanks for reaching out.

When submitting a form across pages, the page being submitted to needs to specify a form with the specified @formname. So, you could put an empty form on the LoginSubmit page containing a hidden form with an @onsubmit handler. Does that work for you?

MackinnonBuck commented 4 months ago

Long-term, we could address this in https://github.com/dotnet/aspnetcore/issues/53129

likquietlyy commented 4 months ago

@MackinnonBuck, It works for me only for SSR form component that submit as HTTP request to SSR endpoint. But if I have form at Interactive Server render mode page that submit as HTTP request to SSR endpoint it still ends with an error message (The POST request does not specify which form is being submitted. To fix this, ensure <form> elements have a @formname attribute with any unique value, or pass a FormName parameter if using <EditForm>.)

mkArtakMSFT commented 2 days ago

Closing as a dupe of https://github.com/dotnet/aspnetcore/issues/53129