Signing with HttpClient
AddHmacHttpMessageHandler attaches an HmacDelegatingHandler to a named
HttpClient. Every request that client sends is signed on the way out.
builder.Services
.AddHttpClient("api", client => client.BaseAddress = new Uri("https://api.example.com"))
.AddHmacHttpMessageHandler("MyPolicy");With a scheme:
builder.Services
.AddHttpClient("api", client => client.BaseAddress = new Uri("https://api.example.com"))
.AddHmacHttpMessageHandler("MyPolicy", "UserContext");Then use the client normally — there is nothing to remember at the call site:
var client = httpClientFactory.CreateClient("api");
var response = await client.GetAsync("/orders");Why this rather than signing by hand
The handler runs inside the HttpClient pipeline, which means it signs after
BaseAddress has been applied and the request URI is absolute.
Signing manually before calling SendAsync is the common way to get a
signature mismatch on every request: you sign /orders, the server verifies
against https://api.example.com/orders, and the authority segment of the
signing content does not match. Signing a
relative URI throws AbsoluteUriException rather than producing that quietly.
Failures
If a request cannot be signed, the handler throws HmacSigningException rather
than sending an unsigned request. The reason is logged first — event 1001 for a
signing that produced no HMAC, event 1003 for the abandoned request.
The most common cause is a scheme header that is not on the request yet:
var request = new HttpRequestMessage(HttpMethod.Get, "/orders");
request.Headers.Add("X-UserId", "42"); // before Send, not after
var response = await client.SendAsync(request);Scheme headers must be present before Send or SendAsync. The handler signs
what it is given; a header added later is not covered by the signature and the
verifier will reject the request.