📖 3 min read (~ 600 words).

Content types

Consumer and Producer (interfaces page) are the seam through which every wire format plugs in.

The runtime ships a handful of built-in factories for the formats most OpenAPI specs use; anything else is a function call away.

Built-in factories

All factories return ready-to-use Consumer / Producer values. They are side-effect free — call them as many times as you like.

FormatConsumer factoryProducer factoryCommon MIME
JSONruntime.JSONConsumer()runtime.JSONProducer()application/json
XMLruntime.XMLConsumer()runtime.XMLProducer()application/xml, text/xml
Plain textruntime.TextConsumer()runtime.TextProducer()text/plain
CSVruntime.CSVConsumer(opts…)runtime.CSVProducer(opts…)text/csv
Byte streamruntime.ByteStreamConsumer(opts…)runtime.ByteStreamProducer(opts…)application/octet-stream, any unparsed binary type
YAMLyamlpc.YAMLConsumer()yamlpc.YAMLProducer()application/yaml, application/x-yaml

YAML lives in a sub-package (github.com/go-openapi/runtime/yamlpc) to keep the YAML dependency optional.

The CSV and bytestream factories accept option functions (e.g. runtime.ClosesStream to make a stream consumer close the underlying reader). See the godoc for the full option list.

Registering codecs on a server

The server side keeps two map[mediaType]Consumer / …Producer lookups, populated at API construction time. For an untyped API:

api := untyped.NewAPI(spec)
api.RegisterConsumer(runtime.JSONMime, runtime.JSONConsumer())
api.RegisterProducer(runtime.JSONMime, runtime.JSONProducer())
api.RegisterConsumer("application/vnd.acme.v1+json", runtime.JSONConsumer())
api.RegisterProducer("application/vnd.acme.v1+json", runtime.JSONProducer())

Full source: docs/examples/core/contenttypes/main.go

Code generated by go-swagger calls these for you, one entry per consumes and produces value declared in the spec.

Registering codecs on a client

client.Runtime exposes per-content-type maps via Consumers and Producers on the runtime value. Generated clients populate them; for direct use:

rt := client.New("api.example.com", "/v1", []string{"https"})
rt.Consumers[runtime.JSONMime] = runtime.JSONConsumer()
rt.Producers[runtime.JSONMime] = runtime.JSONProducer()

Full source: docs/examples/core/contenttypes/main.go

Writing a custom Consumer / Producer

A consumer is a function. The runtime never inspects its concrete type — implementing Consumer (or supplying a ConsumerFunc) is enough.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// SPDX-License-Identifier: Apache-2.0

// Package customcodec illustrates how to implement a runtime.Consumer for a
// custom wire format. Uint32Consumer decodes a single big-endian 32-bit
// unsigned integer from the request body into a *uint32 target.
package customcodec

import (
	"encoding/binary"
	"fmt"
	"io"

	"github.com/go-openapi/runtime"
)

// Uint32Consumer returns a runtime.Consumer that reads a single big-endian
// uint32 from r and stores it at v (which must be a *uint32).
func Uint32Consumer() runtime.Consumer {
	return runtime.ConsumerFunc(func(r io.Reader, v any) error {
		p, ok := v.(*uint32)
		if !ok {
			return fmt.Errorf("uint32 consumer: target %T is not *uint32", v)
		}
		return binary.Read(r, binary.BigEndian, p)
	})
}

Full source: docs/examples/customcodec/uint32.go

Register the resulting Consumer under whatever MIME types should dispatch to it.

Selection rules

How the runtime chooses which consumer / producer to use for a given request — including wildcards, MIME parameters, and the asymmetric matching rule — is documented in tutorials / media-type selection and surfaced site-side under standalone / content negotiation.

Client-side override: ContentTyper

A request body value can declare its own Content-Type by implementing runtime.ContentTyper:

type ContentTyper interface {
    ContentType() string
}

When a body payload set via SetBodyParam is a stream and ContentType() returns a non-empty value, that value wins over the operation’s consumes default. Same goes for individual file values inside a multipart upload. The full algorithm is in tutorials / media-type selection.