Skip to content

Interceptors

Interceptors are the primary extension point for generated clients. Add them to {Service}ClientConfig.Interceptors:

var client = new WeatherClient(
new Uri("https://api.example.com"),
new()
{
Interceptors = { new CorrelationIdInterceptor() },
});

Implement IClientInterceptor and override only the hooks you need:

public sealed class CorrelationIdInterceptor : IClientInterceptor
{
public ValueTask<SmithyHttpRequest> OnBeforeTransmitAsync(
SmithyContext context,
SmithyHttpRequest request,
CancellationToken cancellationToken = default)
{
request.Headers["X-Correlation-Id"] = Guid.NewGuid().ToString("N");
return ValueTask.FromResult(request);
}
}

Available hooks:

HookUse
OnBeforeExecutionRead or initialize per-call context before runtime work starts.
OnBeforeSerializationObserve the typed input before protocol serialization.
OnBeforeSigningAsyncModify the serialized request before auth signing.
OnBeforeTransmitAsyncModify the signed request before transport sends it.
OnAfterTransmitObserve the raw response before deserialization.
OnAfterDeserializationObserve the typed output after protocol deserialization.
OnAfterExecutionRun cleanup or final observation after the call completes; receives the exception on failure (null on success).

Before hooks run in configured order. After hooks run in reverse order.

The per-call SmithyContext is a typed value bag read through SmithyContextKeys — for example context.Get(SmithyContextKeys.ServiceName). The runtime populates ServiceName and OperationName (string), Attempt (int), and — for clients constructed with an endpoint or HttpClientEndpoint (Uri).

DebugInterceptor is a built-in interceptor that logs every execution stage: the typed input and output, each transport attempt’s request and response, and a hex dump of the body bytes. It shows exactly what a protocol puts on the wire, which is especially handy for binary protocols like rpcv2Cbor and gRPC, and makes retries visible as separate attempts.

var client = new WeatherClient(
new Uri("http://localhost:5001"),
new() { Interceptors = { new DebugInterceptor() } });

Output goes to standard output by default; set Output to any TextWriter to redirect it. MaxBodyBytes caps each hex dump (1024 bytes by default), and RedactedHeaders controls which header values are masked (Authorization, Proxy-Authorization, and X-Api-Key by default). It is a debugging aid, not a production logging solution; for production telemetry see Observability.