Skip to main content

How to Read a Request Header in a Minimal API with FromHeader

· 4 min read
Jagdish Kumawat
Founder @ Dewiride

To read a request header in a minimal API, add a parameter with [FromHeader]. One detail decides how it behaves: string? makes the header optional, and string makes it required, with a 400 if it's missing.

Prerequisites​

  • An ASP.NET Core minimal API. I used ASP.NET Core 10.

Step 1: Add the Parameter​

An endpoint
g.MapGet("/header/optional", ([FromHeader(Name = "X-Request-Source")] string? source) =>
Results.Ok(new { source }));

The endpoint in a macOS-style code window, with a FromHeader attribute naming X-Request-Source on a nullable string parameter.

Name = gives the header's real name, because a C# parameter can't have a dash in it. [FromHeader] needs using Microsoft.AspNetCore.Mvc;.

Step 2: Call It With and Without​

Response (no header)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
"source": null
}
Response (X-Request-Source: mobile-app)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
"source": "mobile-app"
}

Two responses of 200 OK: source null with no header, and source mobile-app with the header.

Step 3: Make It Required​

Take away the ?:

An endpoint
g.MapGet("/header/required", ([FromHeader(Name = "X-Request-Source")] string source) =>
Results.Ok(new { source }));
Response (no header)
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json

{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Bad Request",
"status": 400,
"traceId": "00-7eccd113fd241306bbae1c41eeee997d-00182afd897625c4-00"
}

The endpoint doesn't run. The log says why: Required parameter "string source" was not provided from header. With the header, it answers normally:

Response (X-Request-Source: mobile-app)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
"source": "mobile-app"
}

Step 4: Read It from HttpContext​

If the name is only known at runtime, or you want all the values, use HttpContext. Names aren't case-sensitive:

An endpoint
g.MapGet("/header/noattr", (HttpContext context) =>
Results.Ok(new { source = context.Request.Headers["X-Request-Source"].ToString() }));
Response (x-request-source: lower-case)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{
"source": "lower-case"
}

Step 5: Validate It​

With AddValidation() on, attributes work on the parameter:

src/Dewiride.Inventory.Api/Features/Orders/OrderEndpoints.cs (excerpt)
[FromHeader(Name = "Idempotency-Key"), StringLength(64)] string? idempotencyKey,

Common Mistakes​

MistakeWhat happensDo this instead
Leaving out Name for a header with dashesIt looks for a header called sourceGive the name
Using string for an optional header400 when it's missingUse string?
Putting secrets in a header without TLSThey travel in the clearUse HTTPS

Troubleshooting​

SymptomLikely causeFix
Required parameter ... was not provided from headerIt's required, and missingThe fix
It's always nullA different nameCheck Name =
The validation 400 names idempotencyKeyIt uses the parameter's nameThat's expected

Conclusion​

[FromHeader(Name = "...")] reads a header into a parameter. string? for optional, string for required, and HttpContext.Request.Headers for the rest.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.