How to handle multiple [FromBody] parameters in ASP.NET Core Web API?

I’m migrating an existing ASP.NET MVC application to ASP.NET Core and running into issues with action methods that accept multiple parameters from the request body.

In the original MVC version, this worked fine:

[HttpPost]
public ActionResult FilterData(List<string> categories, FilterOptions options)
{
    // Original ASP.NET MVC implementation
    return View();
}

However, when I try to implement the same logic in ASP.NET Core using multiple [FromBody] attributes, the first parameter comes back as null:

[HttpPost]
public IActionResult FilterData([FromBody]List<string> categories, [FromBody]FilterOptions options)
{
    // ASP.NET Core implementation - categories is always null
    return Ok();
}

I know I can work around this by adding the categories list as a property inside the FilterOptions class, but I have many similar methods throughout my codebase. Is there a better approach to handle multiple body parameters without major refactoring?

Been through this migration pain myself. The multiple FromBody limitation hit me on about 30 endpoints.

Here’s what actually worked without huge refactoring:

Create one base wrapper for the common pattern:

public class DualBodyRequest<TFirst, TSecond>
{
    public TFirst First { get; set; }
    public TSecond Second { get; set; }
}

Then update your methods:

[HttpPost]
public IActionResult FilterData([FromBody]DualBodyRequest<List<string>, FilterOptions> body)
{
    return ProcessFilter(body.First, body.Second);
}

private IActionResult ProcessFilter(List<string> categories, FilterOptions options)
{
    // Your original logic stays the same
    return Ok();
}

Keep your business logic in separate methods so you only change controller signatures.

Your frontend sends:

{
    "first": ["category1", "category2"],
    "second": { /* FilterOptions properties */ }
}

I knocked out 30+ endpoints in 2 hours this way. Business logic never changed, just parameter unwrapping.

Not elegant, but it works and keeps changes minimal.

The wrapper approach works but gets messy at scale. I’ve dealt with this exact problem across dozens of endpoints.

Automate the wrapper generation instead of creating classes manually everywhere. Build a code generator that scans your methods and creates wrapper classes automatically.

Even better - set up automated request transformation at the API gateway level. Your controllers stay clean and you don’t need to refactor existing code.

I use Latenode for this transformation. It sits between your frontend and API, combines multiple JSON objects into the wrapper format your ASP.NET Core expects, then unwraps the response back to the original structure.

Your frontend keeps sending separate objects like before, but your backend gets properly formatted requests. No controller changes needed.

Latenode handles the JSON manipulation and routing automatically. Takes about 10 minutes to set up vs weeks of manual refactoring.

ASP.NET Core only allows for one [FromBody] parameter per action method due to how model binding functions. When the request body is read, the second parameter is null as a result. I encountered this issue during my migration, and I found that creating a wrapper class is the most efficient solution, even if it seems cumbersome. Here’s a flexible wrapper that can be reused across multiple endpoints: csharp public class MultipleBodyRequest<T1, T2> { public T1 FirstParameter { get; set; } public T2 SecondParameter { get; set; } } [HttpPost] public IActionResult FilterData([FromBody]MultipleBodyRequest<List<string>, FilterOptions> request) { var categories = request.FirstParameter; var options = request.SecondParameter; return Ok(); } Alternatively, you could source parameters from different locations—one from the body and the other from the query string or route. However, this approach alters your API contract significantly.

yeah, asp.net core’s model binding is frustrating here. another option is creating a custom model binder that manually deserializes json to handle multiple body params. more setup work, but you won’t need wrapper classes cluttering your method signatures.