liveBook · Manning

Aspect

As explained in Go by Example

Interceptor is a book-specific wrapper type that intercepts response-writing behavior while retaining access to the wrapped response writer.

An Interceptor wraps an http.ResponseWriter so middleware can observe or alter selected response-writing calls without changing handlers. It remains usable as a ResponseWriter, captures WriteHeader status codes, then forwards the call to the original writer so the response proceeds normally. section 9.3 section 9.3.1

Figure 9.4 Interceptor captures HTTP status codes by intercepting WriteHeader. Then Interceptor forwards the call to the underlying ResponseWriter.
A blue square with black text

AI-generated content may be incorrect.

The wrapper sits between a handler and its original response writer: it records the status code at WriteHeader, then delegates the write. section 9.3

Embed http.ResponseWriter anonymously and define only the method that needs interception. Promoted methods continue to reach the embedded writer, while Interceptor.WriteHeader takes precedence, calls an optional OnWriteHeader hook, and then invokes the embedded writer’s WriteHeader method. section 9.3.1

Listing 9.6 Intercepting method calls (link/kit/hlog/hlog.go)
// Interceptor provides hooks to intercept response writes.
type Interceptor struct {
    http.ResponseWriter  #1
    OnWriteHeader func(code int)  #2
}

// WriteHeader calls [Interceptor.OnWriteHeader] if provided and
// then calls the embedded [http.ResponseWriter.WriteHeader].
func (ic *Interceptor) WriteHeader(code int) {
    if ic.OnWriteHeader != nil {  #3
        ic.OnWriteHeader(code)   #3
    }   #3
    ic.ResponseWriter.WriteHeader(code)  #4
}

This implementation keeps the wrapper focused: a hook receives the code before delegation to the embedded ResponseWriter. section 9.3.1

StatusCode middleware creates an Interceptor for one request and supplies a hook that saves the observed code. Initialize the destination to http.StatusOK before calling the next handler, because the server uses OK when a handler never explicitly calls WriteHeader. The supplied integer is not safe for concurrent use. section 9.3.2

Preserving streaming behavior

Embedding the ResponseWriter interface does not expose optional methods implemented only by its runtime value, such as http.Flusher.Flush. Add Unwrap to return the original writer so ResponseController and wrapper-aware code can reach optional functionality; direct type assertions against the wrapper can still fail in older code. section 9.4.1 section 9.4.3

Listing 9.9 Unwrap (link/kit/hlog/hlog.go)
// Unwrap returns the embedded [http.ResponseWriter] to allow
// handlers to access the original when needed to preserve
// [http] optional interfaces like [http.Flusher], etc.
func (ic *Interceptor) Unwrap() http.ResponseWriter {
    return ic.ResponseWriter  #1
}

Unwrap exposes the embedded response writer, allowing wrapper-aware HTTP facilities to find optional behavior beneath the Interceptor. section 9.4.3

How Go by Example covers it

How deep it goes
Explains embedding, method precedence, callbacks, status defaults, and optional-interface hazards; defers modern wrapper use beyond Unwrap.
Examples
Uses StatusCode middleware, WriteHeader hooks, a flushing handler, and an Unwrap implementation.
What it assumes
Assumes familiarity with HTTP handlers and ResponseWriter, then teaches embedding and type assertions needed for the wrapper.
What you can do after
Build it from scratch: wrap a ResponseWriter to record status codes while forwarding normal response behavior.
How it connects
Connects http.ResponseWriter, middleware, field embedding, type assertions, http.Flusher, ResponseController, and Unwrap.

Also explained in

Compare how 2 books explain aspect →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Go by Example. The section links point to the parts of the book it draws on.