Skip to main content
Version: 5.0

Response Handlers

FluentHttpClient includes a set of response handler extensions that allow you to take action based on the result of an HTTP request - without breaking the fluent flow. These methods operate after the response has been received, offering a clean way to apply side effects, logging, metrics, or conditional logic.

Response handlers are useful when you want to:

  • Run logic only for successful responses (OnSuccess).
  • Run logic only for failed responses (OnFailure).
  • Execute custom logic based on arbitrary conditions (When).
  • Attach observability hooks (logging, metrics, tracing) at the point where the response becomes available.
  • Avoid wrapping everything in if statements after each awaited request.

These handlers do not interfere with normal response processing. Each method returns the original HttpResponseMessage, allowing additional chained operations.

danger

If the handler reads the response content, subsequent chained operations may not be able to read it again.

Conditional Response Handling

The When extension is the foundation of all response handlers. It evaluates a predicate against the completed HttpResponseMessage and runs a handler only when the predicate returns true.

Two overloads are provided: one for synchronous handlers, one for asynchronous handlers.

Synchronous handler

var response = await builder.GetAsync()
.When(r => r.StatusCode == HttpStatusCode.NotModified,
r => logger.LogInformation("Not modified"));

Asynchronous handler

var response = await builder.PostAsync()
.When(r => r.StatusCode == HttpStatusCode.BadRequest,
async r => await LogValidationErrorsAsync(r));

Handling Successful Responses

OnSuccess is a convenience wrapper around When that triggers if response.IsSuccessStatusCode is true.

Synchronous handler

var response = await builder.GetAsync()
.OnSuccess(r => logger.LogInformation($"Success: {r.StatusCode}"));

Asynchronous handler

var response = await builder.GetAsync()
.OnSuccess(async r => await metrics.RecordSuccessAsync(r));

Handling Failed Responses

OnFailure is a convenience wrapper around When that triggers if response.IsSuccessStatusCode is false.

Synchronous handler

var response = await builder.PostAsync()
.OnFailure(r => logger.LogError($"Request failed: {r.StatusCode}"));

Asynchronous handler

aawait builder.PostAsync()
.OnFailure(async r => await metrics.RecordFailureAsync(r));

Usage Examples

Example: log success, capture failures, continue pipeline

var response = await builder
.WithJsonContent(model)
.PostAsync()
.OnSuccess(r => logger.LogInformation("Created"))
.OnFailure(async r => await SaveFailedResponseAsync(r));

Example: fail fast on error after logging

var response = await builder.GetAsync()
.OnFailure(r => logger.LogWarning($"API returned {r.StatusCode}"));

Example: use a custom predicate

var response = await builder.GetAsync()
.When(r => r.StatusCode == HttpStatusCode.TooManyRequests,
async r => await HandleRateLimitAsync(r));

Behavior Notes

  • The predicate is evaluated after the HTTP call completes.
  • The response is not disposed automatically - you control its lifetime.
  • If the handler reads the response content, subsequent chained operations may not be able to read it again.
  • The returned Task resolves to the original response, whether or not the handler ran.

Quick Reference

MethodTrigger conditionHandler type
When(predicate, Action)predicate(response) is trueSynchronous
When(predicate, Func<Task>)predicate(response) is trueAsynchronous
OnSuccess(Action)response.IsSuccessStatusCodeSynchronous
OnSuccess(Func<Task>)response.IsSuccessStatusCodeAsynchronous
OnFailure(Action)!response.IsSuccessStatusCodeSynchronous
OnFailure(Func<Task>)!response.IsSuccessStatusCodeAsynchronous

These methods make post-response processing concise, expressive, and fully compatible with fluent request construction.