Interfaces & layering
All interfaces live in the root package
github.com/go-openapi/runtime.
Each one comes with a companion *Func adapter so plain functions can be
used wherever an implementation is required.
The five interfaces
Consumer — bind a request body to a Go value
See the authoritative definition in
godoc: runtime.Consumer.
Used on both sides:
- server: deserialize the inbound request body into the parameter struct matched to the operation
- client: deserialize the response body into the operation’s typed result
Producer — write a Go value to an HTTP response
See the authoritative definition in
godoc: runtime.Producer.
Used on both sides:
- server: serialize the operation handler’s return value into the response body
- client: serialize a request body before sending
The split between Consumer and Producer is deliberate — request
deserialization and response serialization are independent concerns and a
given content type may want different behaviour on each side (think of
streaming uploads vs. buffered downloads).
Authenticator — turn raw auth data into a principal
See the authoritative definition in
godoc: runtime.Authenticator.
The three return values mean:
| Value | Meaning |
|---|---|
bool | did this scheme apply to the request? (false ⇒ try the next one) |
any | the authenticated principal (whatever your app uses) |
error | non-nil ⇒ scheme applied but failed |
Server-only. Built-in implementations for Basic, API key, Bearer and OAuth2
live in the security
package, each with a context-aware *Ctx variant.
Authorizer — gate the principal for this specific request
See the authoritative definition in
godoc: runtime.Authorizer.
Authentication answers who; authorization answers may they do this?. Authorizer runs after a principal has been resolved. A non-nil error blocks the request.
Server-only. There is no built-in authorizer — you wire your own.
OperationHandler — your business logic
See the authoritative definition in
godoc: runtime.OperationHandler.
Server-only. The argument is the bound (and validated) parameter struct;
the return value is whatever Producer will then turn into the response
body. error propagates to the configured error handler.
Server lifecycle — where each interface fires
For a request that reaches a matched route, the conventional pipeline
runs the following stages. Each stage is a separate http.Handler in
the chain, composable via middleware.Builder.
flowchart TD
req(((HTTP request)))
router["Router<br/>match path/method against spec"]
sec["Security · Context.Authorize<br/>Authenticator → principal<br/>Authorizer → may proceed?"]
neg["ContentType / Accept negotiation<br/>pick Consumer + target Producer<br/>(part of BindValidRequest)"]
bind["Binder<br/>path/query/header/body params<br/>— uses Consumer —"]
val["Validator<br/>param validation + Validatable"]
op["OperationExecutor<br/>call OperationHandler.Handle"]
resp["Responder<br/>— uses Producer —"]
out(((HTTP response)))
req --> router --> sec --> neg --> bind --> val --> op --> resp --> out
A few things worth knowing:
- The order above is a convention, not a runtime invariant. It is what
the runtime’s untyped path
(
middleware.newRoutableUntypedAPI) does — it wraps the bind+validate closure withnewSecureAPIso that security runs first — and what go-swagger’s generated typed handlers do (each operation’sServeHTTPcallsContext.AuthorizethenContext.BindValidRequestthen the handler). You can compose a different chain viamiddleware.Builderif you have a reason to. - Security comes before binding and validation. That way an unauthenticated request short-circuits with 401 without paying for parameter binding or body deserialization.
- Auth is a single call site, not two.
Context.Authorizeruns the configured authenticators in order and, on success, calls the optionalAuthorizer. AnAuthenticatorreturning(false, nil, nil)means “this scheme does not apply” and the next one is tried; a non-nil error short-circuits with 401. - The pipeline’s stages are documented in detail under server / pipeline.
Client lifecycle — where each interface fires
flowchart TD
gen["generated client method<br/>(GetPet, ListUsers, …)"]
cop["ClientOperation<br/>{Params, Reader, …}<br/>(request descriptor)"]
submit["Runtime.Submit"]
enc["Producer<br/>encode body → *http.Request"]
auth["AuthInfoWriter<br/>attach auth headers"]
rt["Transport.RoundTrip<br/>(net/http)"]
dec["Consumer<br/>decode response body"]
res(((typed result)))
gen --> cop --> submit
submit --> enc --> rt
submit --> auth --> rt
rt --> dec --> res
Authenticator and Authorizer are not used on the client. Client-side
auth is attached through AuthInfoWriter, covered under
client / authentication.
Which interface goes where?
| Interface | Server | Client | Notes |
|---|---|---|---|
Consumer | ✓ | ✓ | request body in (server) / response body in (client) |
Producer | ✓ | ✓ | response body out (server) / request body out (client) |
Authenticator | ✓ | scheme picks a principal from the request | |
Authorizer | ✓ | gates the principal for this request | |
OperationHandler | ✓ | your business logic | |
Validatable /ContextValidatable | ✓ | ✓ | model self-validation; details in validation |