← Back

Passing Parameters to ASP.NET Core Controllers

2026-01-27 14:54 · 👁 22011

#asp.net#c#

Passing Parameters to ASP.NET Core Controllers (Every Way You Can)

In ASP.NET Core, parameters can come from URL segments, query string, headers, body, forms, and even services.

If you get the binding wrong, you’ll see:

  • null values
  • 415 Unsupported Media Type
  • model validation errors
  • “why is my param always default?” 🤯

Let’s break down every common source.


1) FromRoute (URL segments)

Use it when the value is part of the route template.

[HttpGet("clients/{id:int}")]
public IActionResult GetClient([FromRoute] int id)
    => Ok(new { id });

✅ Best for resource identifiers: /clients/123
Tip: route constraints (:int, :guid) reduce ambiguous matches.


2) FromQuery (query string)

For filtering, pagination, searching, sorting.

[HttpGet("clients")]
public IActionResult GetClients(
    [FromQuery] int page = 1,
    [FromQuery] int pageSize = 20,
    [FromQuery] string? search = null)
    => Ok(new { page, pageSize, search });

Example: /clients?page=2&pageSize=50&search=diego


3) FromHeader (HTTP headers)

Great for correlation IDs, tenant IDs, API versioning, custom auth tokens, etc.

[HttpGet("ping")]
public IActionResult Ping([FromHeader(Name = "X-Correlation-Id")] string? correlationId)
    => Ok(new { correlationId });

4) FromBody (JSON/XML body)

Use it for complex request payloads (POST/PUT/PATCH).
Typically only one parameter can come from body.

public sealed record CreateClientRequest(string Name, int GroupId);

[HttpPost("clients")]
public IActionResult Create([FromBody] CreateClientRequest request)
    => Ok(request);

Make sure the request Content-Type is correct (usually application/json).


5) FromForm (multipart/form-data or x-www-form-urlencoded)

Use for file uploads or classic form posts.

public sealed class UploadAvatarRequest
{
    public IFormFile File { get; set; } = default!;
    public string? Caption { get; set; }
}

[HttpPost("clients/{id:int}/avatar")]
public async Task<IActionResult> UploadAvatar(
    [FromRoute] int id,
    [FromForm] UploadAvatarRequest form)
{
    using var stream = form.File.OpenReadStream();
    // save stream somewhere...
    return Ok(new { id, form.Caption, form.File.FileName, form.File.Length });
}

Client side must send multipart/form-data.


6) FromServices (Dependency Injection)

Yes—controllers can accept parameters from DI right in the action.

[HttpGet("time")]
public IActionResult ServerTime([FromServices] TimeProvider timeProvider)
    => Ok(timeProvider.GetUtcNow());

This is perfect for “one-off” dependencies that you don’t want as fields.


7) FromRoute + FromQuery + FromHeader together

You can mix them freely—just be explicit.

[HttpGet("clients/{id:int}")]
public IActionResult Get(
    [FromRoute] int id,
    [FromQuery] bool includeOrders = false,
    [FromHeader(Name = "X-Tenant-Id")] string? tenantId = null)
    => Ok(new { id, includeOrders, tenantId });

8) FromBody vs “default binding” (important rule)

In [ApiController], ASP.NET Core applies binding source inference:

  • Simple types → usually query/route
  • Complex types → usually body

Still, I recommend using attributes in public APIs for clarity and future-proofing.


When your action has “too many parameters” → use a parameter class

If you’re passing 6–10 query params (filters), don’t create a monster signature.
Create a request model and bind it from query (or route/body depending on the case).

Query parameters grouped into a class
public sealed class ClientSearchParams
{
    public string? Search { get; init; }
    public int Page { get; init; } = 1;
    public int PageSize { get; init; } = 20;
    public string? SortBy { get; init; }
    public bool Desc { get; init; }
}

[HttpGet("clients")]
public IActionResult Search([FromQuery] ClientSearchParams p)
    => Ok(p);

Example:
/clients?search=ana&page=2&pageSize=50&sortBy=name&desc=true

✅ Cleaner controller
✅ Easier validation/versioning
✅ Easier to reuse across endpoints


“Other attributes available” (quick reference)

These are the most used binding-related attributes in controllers:

  • [FromRoute]: URL route values
  • [FromQuery]: query string
  • [FromHeader]: headers (supports Name = "X-...")
  • [FromBody]: request body (JSON/XML)
  • [FromForm]: form fields + files (multipart / urlencoded)
  • [FromServices]: DI container (per-action injection)
  • [BindRequired] / [BindNever]: require/deny binding for a property/param
  • [ModelBinder]: plug a custom binder when default rules aren’t enough

(And of course: validation attributes like [Required], [Range], etc. apply on models.)


Practical rules of thumb
  • Route = identity (/clients/123)
  • Query = filtering/paging/sorting (?page=2&search=x)
  • Header = metadata (X-Correlation-Id, tenant, version)
  • Body = the command (create/update payload)
  • Form = files + form fields
  • Services = dependencies, not client input

Rejoining the server...

Rejoin failed... trying again in seconds.

Failed to rejoin.
Please retry or reload the page.

The session has been paused by the server.

Failed to resume the session.
Please reload the page.