WebAPI
HTTP
Put
Post
API parameters

WebAPI Multiple Put/Post parameters

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

A common ASP.NET Web API problem is trying to send multiple parameters in a POST or PUT method and finding that only one value binds correctly. This usually happens because model binding rules allow only one body-bound parameter by default. If a method has several complex parameters, the framework does not know how to map the single request body to all of them.

The reliable fix is to define one request contract object and place all fields inside it. You can still mix route and query parameters where needed, but body data should usually be represented as one DTO.

Core Sections

1. Why multiple complex parameters fail

In classic Web API and ASP.NET Core controllers, the body stream can only be read once by default model binding. This means patterns like the following often fail:

csharp
1[HttpPost]
2public IActionResult Create(OrderDto order, CustomerDto customer)
3{
4    // binding ambiguity
5    return Ok();
6}

Both order and customer are complex types, and both would need body binding.

2. Use a single request DTO

csharp
1public class CreateOrderRequest
2{
3    public OrderDto Order { get; set; } = default!;
4    public CustomerDto Customer { get; set; } = default!;
5    public string Source { get; set; } = "web";
6}
7
8[HttpPost("orders")]
9public IActionResult Create([FromBody] CreateOrderRequest request)
10{
11    // use request.Order and request.Customer
12    return Ok(new { id = 123, request.Source });
13}

JSON payload:

json
1{
2  "order": { "itemId": 42, "quantity": 2 },
3  "customer": { "id": "C-100" },
4  "source": "mobile"
5}

3. Mix route/query + body correctly

Use route or query for identifiers and flags, body for structured data.

csharp
1[HttpPut("orders/{orderId}")]
2public IActionResult Update(
3    [FromRoute] int orderId,
4    [FromQuery] bool dryRun,
5    [FromBody] UpdateOrderRequest request)
6{
7    return Ok(new { orderId, dryRun, request.Status });
8}

This keeps endpoint contracts explicit and avoids hidden binding behavior.

4. Validate request contracts

Use data annotations and model-state checks:

csharp
1public class UpdateOrderRequest
2{
3    [Required]
4    public string Status { get; set; } = default!;
5}
6
7if (!ModelState.IsValid)
8{
9    return ValidationProblem(ModelState);
10}

Validation catches malformed multi-field payloads early.

5. Minimal API equivalent

In minimal APIs, the same principle applies. Prefer one record parameter:

csharp
app.MapPost("/orders", (CreateOrderRequest req) => Results.Ok(req));

Common Pitfalls

  • Declaring multiple complex parameters and expecting both to bind from one JSON body.
  • Mixing body and query semantics without explicit attributes.
  • Using primitive parameters for structured input and creating brittle endpoint contracts.
  • Skipping request validation and accepting partially bound payloads silently.
  • Changing request JSON shape without versioning or compatibility checks.

Summary

For Web API POST and PUT, treat the request body as one contract object. Use route/query parameters for lightweight contextual values and body DTOs for structured input. This eliminates binding ambiguity, improves validation, and makes endpoint behavior predictable for clients. If you consistently design endpoints around explicit request models, multi-parameter update/create APIs remain clean and maintainable.

A practical way to make this guidance durable is to convert it into a small runbook that includes prerequisites, expected environment versions, and a short verification sequence. Even strong teams lose time when troubleshooting steps live only in memory or chat history. A runbook should explicitly answer three questions: what to check first, what output confirms healthy behavior, and what output indicates a known failure mode. This level of clarity helps both experienced maintainers and newer contributors, and it reduces repeated investigation during incidents.

It is also valuable to create a tiny reproducible fixture for this topic. The fixture can be a minimal script, test case, sample request, or small dataset that demonstrates the correct behavior in isolation. When regressions appear after dependency upgrades, infrastructure changes, or framework migrations, that fixture becomes the fastest way to isolate whether the issue is environmental or logic-related. Keeping a focused fixture in source control gives you a stable benchmark across branches and release cycles.

For long-term reliability, pair documentation with one automated guardrail in CI. The guardrail should be narrow and fast: an import check, schema validation, endpoint contract test, deterministic unit test, or lightweight performance threshold. Avoid broad flaky checks that hide real signals. The goal is early, actionable feedback before code reaches production. If the same category of issue appears repeatedly, promote the manual troubleshooting step into automation so the system catches it first. Over time, this shifts effort from reactive debugging to preventive quality control and keeps the knowledge article relevant in real engineering workflows.


Course illustration
Course illustration

All Rights Reserved.