File Uploads
Submit ordinary HTML file inputs through Heimdall and bind them with ASP.NET Core's native IFormFile pipeline.
Heimdall adds the interaction layer; form encoding, binding, request limits, validation, and storage remain familiar ASP.NET Core concepts.
1. Build the Form
Use MultipartFormData and the typed file input. Accept, Capture, and Multiple emit the corresponding native HTML attributes.
return FluentHtml.Form(form => form
.Id("profile-upload")
.MultipartFormData()
.Heimdall(h => h
.Submit("profile.save")
.PayloadFromClosestForm()
.Target("#upload-result")
.SwapInner()
.Disable())
.Input(Html.InputType.text, input => input
.Name("DisplayName")
.Required())
.Input(Html.InputType.file, input => input
.Name("avatar")
.Accept("image/png", "image/jpeg")
.Required())
.Button(button => button
.Type("submit")
.Text("Upload")));2. Runtime Encoding
A form containing a selected file is sent as multipart/form-data. Forms without files continue to use JSON. The browser owns the multipart boundary, so application code should not set Content-Type manually.
const data = new FormData(
document.querySelector("#profile-upload"));
await Heimdall.invoke("profile.save", data, {
target: "#upload-result",
swap: "inner"
});3. Bind Fields and Files
The normal payload and file parameters can appear together without attributes when their names match the form fields. Heimdall recognizes the multipart request and binds the payload plus IFormFile by convention. Use FromForm only when you want a form-only contract or need Name to map a different input name.
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
[ContentInvocation("profile.save")]
public static IHtmlContent Save(
ProfileRequest request,
IFormFile avatar)
{
return UploadResult.Render(
request.DisplayName,
avatar.Length);
}4. Supported File Shapes
Bind one file, all files, an array, or a common generic collection. File parameters resolve by parameter name. Add FromForm(Name = ...) only when the HTML input uses a different name.
IFormFile
IFormFileCollection
IFormFile[]
IEnumerable<IFormFile>
IReadOnlyList<IFormFile>
[FromForm(Name = "attachments")]
IReadOnlyList<IFormFile> files5. Native ASP.NET Core Limits
Heimdall honors request and multipart limit metadata on the action method or declaring type. Method metadata wins over type metadata, while configured FormOptions remain the baseline. Limit violations return 413.
[RequestSizeLimit(10_000_000)]
[RequestFormLimits(
MultipartBodyLengthLimit = 8_000_000)]
[ContentInvocation("profile.save")]
public static IHtmlContent Save(...)
// Available when intentionally required:
[DisableRequestSizeLimit]6. Form-Only Payloads
FromForm on the payload is optional for an ordinary multipart submission, but it prevents a registered payload type from being mistaken for a service and rejects JSON with 415 Unsupported Media Type.
public static IHtmlContent Save(
[FromForm] ProfileRequest request,
IFormFile avatar)
multipart request -> binds
JSON request -> 415 Unsupported Media Type7. Synchronization and Snapshots
With queue-latest, form fields and selected file objects are snapshotted when submit occurs. Later edits do not change the queued request. Disable is still useful when duplicate submission should be impossible rather than queued.
form.Heimdall(h => h
.Submit("profile.save")
.PayloadFromClosestForm()
.SyncQueueLatest("profile-upload")
.Disable());8. Security Checklist
File names and Content-Type are untrusted metadata. Validate actual content, generate storage names, keep finite limits, store outside executable/static roots by default, authorize the action, and apply malware scanning appropriate to the application.
Validate:
- declared type and file signature
- allowed size and count
- authorization and ownership
Store:
- generated server-side name
- non-executable location
- outside wwwroot unless serving is intentional9. Buffered, Not Streaming
Uploads use ASP.NET Core's buffered IFormFile model. Use a dedicated streaming endpoint for very large files instead of forcing that workflow through a Heimdall content action.
Typical profile image or attachment -> Heimdall + IFormFile
Multi-gigabyte streaming upload -> dedicated endpoint