Handling Posted Form Data in an API Controller

By FoxLearn 3/12/2025 3:54:47 AM   18
To capture posted form data in an API Controller (using the [ApiController] attribute) in ASP.NET Core, use parameters with the [FromForm] attribute.
[Route("api/[controller]")]
[ApiController]
public class RegistrationController : ControllerBase
{
    [HttpPost]
    public IActionResult Post([FromForm] string username, [FromForm] string email)
    {
        // Process registration data
        
        return Ok(new { Username = username, Email = email });
    }
}

You can send a POST request with form data to this endpoint as shown below.

POST /api/registration HTTP/1.1  
Content-Type: application/x-www-form-urlencoded  

username=john_doe&[email protected]

The form data consists of key-value pairs (username=john_doe&[email protected]). The framework matches the form keys with the parameter names (or model property names) to bind the data.

You can also map multiple form fields as separate parameters, map them to a model, or even process the form data directly.

Map Multiple Form Fields as Individual Parameters

For example, if your form has the fields Username, Email, and Password, you can map them individually:

[Route("api/[controller]")]
[ApiController]
public class RegistrationController : ControllerBase
{
    [HttpPost]
    public IActionResult Post([FromForm] string username, [FromForm] string email, [FromForm] string password)
    {
        // Process registration form data
        
        return Ok($"Registration successful for: {username} with email {email}");
    }
}

Here’s what the request to this endpoint might look like:

POST /api/registration HTTP/1.1  
Content-Type: application/x-www-form-urlencoded  

username=john_doe&[email protected]&password=securePassword123

Note: When multiple form fields are included, the key-value pairs are separated by an ampersand (&).

Mapping to a Model

Instead of binding form fields to individual parameters, you can map them to a model class.

[Route("api/[controller]")]
[ApiController]
public class RegistrationController : ControllerBase
{
    [HttpPost]
    public IActionResult Post([FromForm] UserRegistration registration)
    {
        SaveToDatabase(registration);

        return Ok();
    }
    
    // Other methods
}

This maps the form data to the UserRegistration class, which might look like this:

using System.ComponentModel.DataAnnotations;

public class UserRegistration
{
    [Required]
    public string Username { get; set; }

    [Required]
    [EmailAddress]
    public string Email { get; set; }

    [Required]
    [MinLength(8)]
    public string Password { get; set; }
}

A request to this endpoint would look like:

POST /api/registration HTTP/1.1  
Content-Type: application/x-www-form-urlencoded  

username=john_doe&[email protected]&password=securePassword123

When mapping to a model, the framework automatically maps the form fields to the model properties, and the comparison is case-insensitive (e.g., username matches UserRegistration.Username).

Handling Validation Errors

When mapping form data to parameters or a model, the framework performs validation and returns a 400 – Bad Request if any validation fails.

For instance, if the Password is too short or missing:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "00-64ab197a56446c448095e9d3c3082c8b-a4255f612fce2e49-00",
    "errors": {
        "Password": [
            "The Password must be at least 8 characters long."
        ]
    }
}

Similarly, if a required field is missing (e.g., Email), you’ll get:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "00-d37111fc22168f42b5cbe4684eea9359-7a98c6fa7d623b4e-00",
    "errors": {
        "Email": [
            "The Email field is required."
        ]
    }
}

Error Response 415 – Unsupported Media Type

If you attempt to map a model without using the [FromForm] attribute:

[HttpPost]
public IActionResult Post(UserRegistration registration)

You’ll encounter a 415 – Unsupported Media Type error:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.13",
    "title": "Unsupported Media Type",
    "status": 415,
    "traceId": "00-cac8e6fd75525e40b3e2b61e0bc2008a-725c6cba45bde44d-00"
}

To fix this, simply add the [FromForm] attribute:

[HttpPost]
public IActionResult Post([FromForm] UserRegistration registration)

Reading Form Data Directly

If you need to access form data directly without mapping it to parameters or a model, you can use HttpContext.Request.Form. This gives you key-value pairs that can be processed as needed.

For example, How you might loop through the form data:

[HttpPost]
public IActionResult Post()
{
    foreach (var key in HttpContext.Request.Form.Keys)
    {
        var value = HttpContext.Request.Form[key];

        // Process the form data
    }

    return Ok();
}