The POST request does not specify which form is being submitted. Blazor
answers a form POST with this when the page is statically rendered and the form has no
name, so it cannot tell which handler should receive the data. Give the
EditForm a FormName, or the plain <form> a
@formname, and the same POST reaches your handler.
The whole fix is usually one attribute: FormName="new-patient" on the
EditForm. Any value works as long as it is unique among the forms on the
page. A plain HTML form also needs <AntiforgeryToken /> inside it;
EditForm renders that for you.
The error
Status: 400 Bad Request
Content-Type: text/plain
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>.
That is the entire response: a plain-text page in the browser, and nothing in the server log at the template's default log levels. No exception, no stack trace.
Why it happens
A page without a @rendermode is rendered with static server-side rendering.
There is no live connection, so pressing Save sends an ordinary HTTP POST to the page's
URL, and one page can hold several forms. Blazor has to know which form's handler to run,
and it learns that from a hidden field named _handler, which is rendered only
when the form has a name. No name, no field, and Blazor rejects the request with a 400
before any of your code runs.
Antiforgery validation happens even earlier. A form posted without its token gets a
different 400, A valid antiforgery token was not provided with the request.
If that is the one you see, add <AntiforgeryToken /> to the form first;
the form-name message is the next gate.
The fix
@page "/form-noname"
<h1>New patient</h1>
<EditForm Model="Patient" OnValidSubmit="Save" FormName="new-patient">
<label>Name <InputText @bind-Value="Patient!.Name" /></label>
<button type="submit">Save</button>
</EditForm>
<p id="message">@message</p>
@code {
[SupplyParameterFromForm]
private PatientInput? Patient { get; set; }
private string message = "";
protected override void OnInitialized() => Patient ??= new();
private void Save() => message = $"Saved {Patient!.Name}";
public class PatientInput
{
public string? Name { get; set; }
}
}
With the name in place the form renders
<input type="hidden" name="_handler" value="new-patient" />, the same POST
returned 200, and the page showed "Saved Maria Garcia". For a plain form the pieces are the
same, spelled differently:
<form method="post" @onsubmit="Save" @formname="new-patient">
<AntiforgeryToken />
<label>Name <input name="Name" /></label>
<button type="submit">Save</button>
</form>
<p id="message">@message</p>
@code {
[SupplyParameterFromForm]
private string? Name { get; set; }
private string message = "";
private void Save() => message = $"Saved {Name}";
}
The second way out is to make the page interactive: an EditForm on an
InteractiveServer page submits over the live connection and saved fine without
any name. Making a page interactive only to avoid one attribute is a poor trade, though; the
name costs nothing, and a circuit costs server memory for every visitor.
How it was reproduced
A fresh dotnet new blazor -o FixLab --interactivity Server project on .NET SDK
10.0.401 (ASP.NET Core runtime 10.0.12, no extra packages). The form page had no render
mode, so it was static even though the app supports interactivity. PowerShell's
Invoke-WebRequest fetched the page, kept the antiforgery cookie and posted the
token with Patient.Name=Maria Garcia; the answer was the 400 above. A plain
<form method="post" @onsubmit="Save"> without a name produced the same
message. After adding the names, both POSTs returned 200.
Frequently asked
- Do I need FormName on every Blazor EditForm?
- Only on forms that are rendered statically, meaning the page or component has no interactive render mode. There the name tells Blazor which handler receives the POST. An EditForm on an InteractiveServer page submits over the live connection and works without one, though adding a name does no harm.
- What value should FormName or @formname have?
- Any string that is unique among the forms on the page, such as new-patient. Blazor writes it into a hidden _handler field and matches it when the POST comes back. The user never sees it.
- Why does my Blazor form POST return 400 with an antiforgery message instead?
- The form was posted without an antiforgery token. EditForm adds the token automatically; a plain form element needs an AntiforgeryToken component inside it, and Program.cs needs app.UseAntiforgery(), which the template already has.
More decoded errors in the Fixes category. For forms from the ground up, with validation, see Blazor Forms and Validation, the Friendly Way.