Skip to content

Forms Postbacks and Antiforgery

Mike Christensen edited this page Aug 28, 2026 · 1 revision

Forms, postbacks, and antiforgery

When a resolved page receives POST, Imp calls IAsyncPostable.PostbackAsync when implemented; otherwise it calls IPostable.Postback. Rendering continues afterward.

Prefer IAsyncPostable for modern ASP.NET Core applications.

Register antiforgery

builder.Services.AddAntiforgery();

Inject IAntiforgery into the page, render a token, and validate every state-changing request. Imp does not add antiforgery automatically.

[PageTemplate("MySite.PageTemplates.Tasks.htm")]
public sealed class Tasks(TaskStore store, IAntiforgery antiforgery)
   : BasePage, IAsyncPostable
{
   private string message;

   public Task Antiforgery(TextWriter output, DynamicContentArgs args)
   {
      var tokens = antiforgery.GetAndStoreTokens(Request.HttpContext);
      var name = HtmlEncoder.Default.Encode(tokens.FormFieldName);
      var value = HtmlEncoder.Default.Encode(tokens.RequestToken);
      return output.WriteAsync(
         $"<input type=\"hidden\" name=\"{name}\" value=\"{value}\" />");
   }

   public async Task PostbackAsync(HttpResponse response)
   {
      try
      {
         await antiforgery.ValidateRequestAsync(Request.HttpContext);
         var form = await Request.ReadFormAsync();
         store.Add(form["title"].ToString());
         message = "Task added.";
      }
      catch (AntiforgeryValidationException)
      {
         response.StatusCode = StatusCodes.Status400BadRequest;
         message = "The form expired. Reload and try again.";
      }
   }
}

Template:

<form method="post">
  <Dynamic.Antiforgery />
  <label for="title">Task</label>
  <input id="title" name="title" maxlength="120" required="required" />
  <button type="submit">Add</button>
</form>

Binding and validation

Query properties are bound before PreRender and postback handling. Form fields are not bound to properties; read them through Request.ReadFormAsync().

Validate on the server even when HTML attributes provide client-side validation:

  • required values and maximum lengths;
  • number/date ranges;
  • IDs and record existence;
  • user ownership and authorization;
  • allowed action names;
  • upload type and size, if enabled.

Do not display exception text that can contain secrets or internal details. Return a safe validation message and log the diagnostic separately.

Multiple actions

A hidden action field is a simple way to share one page postback:

<input type="hidden" name="action" value="delete" />

Use an allow-list switch and verify the target belongs to the current user. Treat every form field as untrusted.

Post/redirect/get

Imp normally renders the page after PostbackAsync. This is convenient for inline validation but browser refresh may resubmit the form. If implementing post/redirect/get, test the response carefully because the Imp lifecycle proceeds to rendering after the postback. A dedicated application middleware or a future framework short-circuit feature may be preferable for complex workflows.

Clone this wiki locally