---
url: https://jasperfx.github.io/alba/scenarios/formdata.md
description: >-
  Post HTML form data and multipart file uploads to ASP.NET Core endpoints in
  Alba scenarios.
---

# Sending Form Data

## Standard Form Data

Posting HTTP form data in the request can be done with the extension method shown below:

```cs
public async Task write_form_data(IAlbaHost system)
{
    var form1 = new Dictionary<string, string>
    {
        ["a"] = "what?",
        ["b"] = "now?",
        ["c"] = "really?"
    };

    await system.Scenario(_ =>
    {
        // This writes the dictionary values to the HTTP
        // request as form data, and sets the content-length
        // header as well as setting the content-type
        // header to application/x-www-form-urlencoded
        _.WriteFormData(form1);
    });
}
```

snippet source | anchor

There's a second overload that attempts to use an object and its properties to populate the form data:

```cs
[Fact]
public async Task can_bind_to_form_data()
{
    await using var system = await AlbaHost.For<Startup>();

    var input = new InputModel {
        One = "one",
        Two = "two",
        Three = "three"
    };

    await system.Scenario(_ =>
    {
        _.Post.FormData(input)
            .ToUrl("/gateway/insert");
    });

    GatewayController.LastInput.ShouldNotBeNull();

    GatewayController.LastInput.One.ShouldBe("one");
    GatewayController.LastInput.Two.ShouldBe("two");
    GatewayController.LastInput.Three.ShouldBe("three");
}
```

snippet source | anchor

Do note that this only adds first level properties, so if you need to deeper accessors like add "Prop1.Prop2.Prop3,"
you'll have to resort to the dictionary approach.

## Multipart Form Data & File Uploads

`MultipartFormData(...)` builds a multipart body of form fields and file uploads:

```cs
var result = await host.Scenario(x =>
{
    x.Post.MultipartFormData(form => form
            // Files with the same name bind to a collection; the content type
            // comes from the file name extension unless one is passed
            .File("files", Encoding.UTF8.GetBytes("Hello there!"), "greeting.txt")
            .File("files", image, "TestImage.jpg")
            .Field("additionalContent", "My additional string of fun times"))
        .ToUrl("/api/files/upload");

    x.StatusCodeShouldBeOk();
});
```

snippet source | anchor

* `File(name, bytes, fileName)` and `File(name, stream, fileName)` add a file. The application binds it by `name`, to an
  `IFormFile` parameter or property, or to a collection when several files share the name.
* The file's content type comes from the extension of `fileName`, such as `text/csv` for `report.csv`, and falls back
  to `application/octet-stream`. Pass a content type as the last argument to override it.
* A stream is read when the request is sent, so keep it open until the scenario completes.
* `Field(name, value)` adds a form value.

Minimal API endpoints that bind `IFormFile` validate an antiforgery token when the application uses antiforgery. Call
`.DisableAntiforgery()` on such an endpoint in the test host, or send a token, because Alba does not supply one.

For full control over the body, pass a `MultipartFormDataContent` instead:

```cs
public async Task write_multipart_form_data(IAlbaHost system)
{
    // Read our file into a stream
    await using var imageFile = File.OpenRead("TestImage.jpg");
    // Extract the name from the path
    var imageFileName = Path.GetFileName(imageFile.Name);
    // Create the stream content
    using var content = new StreamContent(imageFile);
    // Remember to manually set the media type as it won't be done automatically
    content.Headers.ContentType = new MediaTypeHeaderValue(MediaTypeNames.Image.Jpeg);

    // Create the MultiPartForm content & add the file
    using var formData = new MultipartFormDataContent();
    formData.Add(content, "files", imageFileName);

    // If you have other content in the form object, you can add it as well!
    formData.Add(new StringContent("My additional metadata"), "metadata");

    var result = await system.Scenario(_ =>
    {
        // This extension will write the content to the request
        // body and set the required headers
        _.Post.MultipartFormData(formData).ToUrl("/api/files/upload");
        _.StatusCodeShouldBeOk();
    });
}
```

snippet source | anchor
