Documentation
¶
Overview ¶
Package mediatype provides a typed value for media types defined by RFC 7231 and RFC 2045.
The matching/selection primitives used by both server-side validation and Accept-header negotiation.
The package is stdlib-only.
The matching rule ¶
MediaType.Matches is asymmetric. The receiver acts as the "bound" (an allowed entry on the server side, or a candidate offer when matching against an Accept entry); the argument is the constraint (the actual incoming request, or the Accept entry being satisfied).
- bare type/subtype must agree, with wildcard handling on either side ("*/*" matches anything; "type/*" matches any subtype);
- if the receiver carries no parameters, any constraint is accepted regardless of its parameters;
- otherwise every (key,value) pair on the constraint must be present on the receiver, with case-insensitive value comparison. The receiver may carry additional parameters the constraint does not list.
q-values are NOT considered by MediaType.Matches — they are the negotiator's concern, handled inside Set.BestMatch.
Index ¶
Constants ¶
const ( SpecificityAny = iota // "*/*" SpecificityType // "type/*" SpecificityExact // "type/subtype" (no params) SpecificityExactWithParams // "type/subtype;k=v" )
Specificity scores returned by MediaType.Specificity, ordered from least to most specific.
const ErrMalformed mediaTypeError = "mediatype: malformed"
ErrMalformed is the sentinel returned (wrapped) by Parse when its input cannot be parsed as an RFC 7231 media type.
Callers can test for it with errors.Is to distinguish a client-side malformed Content-Type header (an HTTP 400 outcome) from a well-formed value that simply matches no allowed entry (an HTTP 415 outcome).
Variables ¶
This section is empty.
Functions ¶
func Lookup ¶
func Lookup[T any](m map[string]T, mediaType string, opts ...MatchOption) (T, bool)
Lookup finds the entry in m matching mediaType, with alias-aware fallback. It is the canonical seam for codec-map lookups in both the client and server runtimes — placing the fallback policy here keeps alias definitions (and any future lookup tolerances) in one place.
Lookup tries the following, in order, returning the first hit:
- mediaType verbatim (fast path for callers that already pass a canonical, parameter-free string and store map keys in the same form).
- An alias-aware walk against the parsed "type/subtype" form: a direct map hit on the parsed key, on its alias canonical if any, and finally an O(len(m)) scan returning any map entry whose key alias-canonicalizes to the same target. Catches both "map keyed by canonical, query uses alias" and "map keyed by one alias, query uses another alias of the same canonical".
- When AllowSuffix is passed in opts: the same alias-aware walk against the RFC 6839 structured-syntax suffix base. Catches the "spec/traffic divergence" case (request for application/vnd.api+json finds a JSON consumer registered under application/json). Query-side suffix fold only — no map-side suffix folding.
Lookup does NOT fall back to "*/*". Callers that want wildcard behavior (the historical resolveConsumer pattern in the client runtime) chain that themselves after a Lookup miss — keeping wildcard semantics explicit at each call site.
Map keys are expected in canonical "type/subtype" form (no parameters). The runtime's default Consumers / Producers maps follow this convention.
Returns (zero, false) when:
- m is empty;
- mediaType fails to parse and is not present verbatim;
- none of the active steps hits.
The malformed-vs-not-found distinction is intentionally elided: codec-lookup callers treat both as the same "no codec" error path.
Types ¶
type MatchKind ¶
type MatchKind int
MatchKind classifies the strength of a match between two media types. Larger values represent stronger matches and win in negotiation tie-breaks.
MatchExact covers direct subtype or wildcard agreement under RFC 7231 rules; MatchAlias is returned when the strict comparison fails but the two values agree after canonicalization through the internal alias table (see MediaType.Canonical); MatchSuffix is returned only when both alias and exact comparisons fail but the two values agree after folding the RFC 6839 structured-syntax suffix (see MediaType.Base).
MatchSuffix matches are off by default at the negotiation / lookup callers — they count only when AllowSuffix is passed to Set.BestMatch, MatchFirst, or Lookup. The opt-in is the single user-visible knob; MediaType.Match itself always returns the strongest tier that succeeds.
const ( MatchNone MatchKind = iota // no match MatchSuffix // matched via the RFC 6839 suffix base MatchAlias // matched via the alias table MatchExact // matched directly (RFC 7231 semantics) )
MatchKind values. Returned by MediaType.Match.
type MatchOption ¶
type MatchOption func(*matchOptions)
MatchOption configures the matching tolerances used by Set.BestMatch, MatchFirst, and Lookup. The zero behaviour is strict: only MatchAlias and MatchExact count.
func AllowSuffix ¶
func AllowSuffix() MatchOption
AllowSuffix returns a MatchOption that lets the caller count MatchSuffix results as valid matches. Use this to opt into RFC 6839 structured-syntax suffix tolerance for situations where the client/server traffic does not strictly abide by the spec (typical example: server returning application/problem+json against operations that only declare application/json in produces).
type MediaType ¶
type MediaType struct {
Type string
Subtype string
Suffix string
Params map[string]string
Q float64
}
MediaType is a parsed RFC 7231 media type with optional parameters and an optional q-value (used by Accept negotiation).
Type, Subtype and the keys of Params are lowercased. Parameter values are preserved verbatim; comparisons are case-insensitive (matching the pre-v0.30 behaviour and the common convention for charset, version, etc.).
Suffix exposes the RFC 6839 structured syntax suffix (the token after the final '+' in Subtype) as a parallel hint. Subtype itself retains the full wire value, so existing callers comparing Subtype against a string see no change.
func MatchFirst ¶
MatchFirst reports whether actual matches any entry in allowed, using MediaType.Match — the param-aware RFC 7231 rule plus the alias bridge from the package-internal alias table.
The scan is multi-pass and tier-ordered: the first pass returns the first allowed entry that matches under MatchExact (RFC 7231 semantics); the second pass looks for a MatchAlias match; when AllowSuffix is in opts a third pass looks for a MatchSuffix match. This preserves the "stronger tier wins" ordering from Set.BestMatch while keeping the "first match wins" semantics within each tier.
Return values:
- (matched, true, nil) — the first allowed entry that matches, with exact matches preferred over alias matches.
- (zero, false, nil) — actual is well-formed but no allowed entry accepts it. Maps to an HTTP 415 outcome.
- (zero, false, err) — actual fails to parse. err wraps ErrMalformed, so callers can use errors.Is to distinguish this case. Maps to an HTTP 400 outcome.
Allowed entries that themselves fail to parse are skipped (they cannot match any well-formed actual), and no error is surfaced for them.
An empty allowed list returns (zero, false, nil). MatchFirst is the primitive; callers decide what no-constraints means in their context.
func Parse ¶
Parse parses a single media type. The input may carry parameters and a q-value; the q-value is extracted into [MediaType.Q] and removed from [MediaType.Params].
An empty input returns an error.
func (MediaType) Base ¶
Base returns the base media type implied by the RFC 6839 structured syntax suffix, or m unchanged when:
- Suffix is empty;
- Suffix is not present in the package-internal suffix→base table.
The returned value represents the structural base only: it carries no parameters and no q-value. Use it to find a codec for the underlying wire format — for example, "application/vnd.api+json" resolves to "application/json".
Base does not mutate the receiver.
func (MediaType) Canonical ¶
Canonical returns m rewritten to its canonical media type via the package-internal alias table, or m unchanged when (Type, Subtype) is not a known alias. Params and Q are preserved on the returned value; Suffix is recomputed from the canonical Subtype (none of the current entries carry a suffix, but the contract is forward-safe).
Canonical does not mutate the receiver.
func (MediaType) Match ¶
Match reports how m matches other, classifying the result by MatchKind. Used by negotiation to rank candidate offers: stronger tiers win when both apply.
Returns, strongest first:
- MatchExact when m.Matches(other) is true under the strict RFC 7231 rules (including wildcards and the param subset rule).
- MatchAlias when m.Canonical().Matches(other.Canonical()) is true but the strict comparison failed.
- MatchSuffix when m.Base().Canonical().Matches( other.Base().Canonical()) is true but the alias comparison failed (RFC 6839 structured-syntax suffix fold).
- MatchNone otherwise.
The asymmetric "bound vs constraint" rule of MediaType.Matches is preserved at every tier. Match itself is always lenient — the opt-in to count MatchSuffix lives one level up at Set.BestMatch, MatchFirst, and Lookup via AllowSuffix.
func (MediaType) Matches ¶
Matches reports whether the receiver accepts other, per the package documentation: the receiver is the bound, other is the constraint.
func (MediaType) Specificity ¶
Specificity returns a numeric score for ordering matches. Higher is more specific. The returned value is one of SpecificityAny, SpecificityType, SpecificityExact or SpecificityExactWithParams.
func (MediaType) String ¶
String renders the canonical "type/subtype;k=v;k=v" form. Parameters are emitted in lexicographic key order (the standard library guarantees this) so the result is stable. The q-value is NOT emitted — it is meta, not part of the media type identity.
func (MediaType) StripParams ¶
StripParams returns a copy of m with no parameters. Q is preserved because it drives negotiation ordering, not media-type identity.
Useful for the legacy "ignore parameters" negotiation mode.
type Set ¶
type Set []MediaType
Set is a list of media types — typically the parsed value of an Accept header, or a list of server-side offers.
func ParseAccept ¶
ParseAccept parses a comma-separated list of media types, as found in the Accept, Accept-Charset (etc.) HTTP headers. Malformed entries are skipped silently — be liberal in what you accept.
An empty input returns nil.
func (Set) BestMatch ¶
func (s Set) BestMatch(offered Set, opts ...MatchOption) (best MediaType, ok bool)
BestMatch picks the offer most acceptable to the receiver's Accept entries. Selection follows RFC 7231 §5.3.2 plus tier-aware ranking:
- highest q-value wins;
- ties on q broken by the highest MediaType.Specificity of the matching Accept entry;
- ties on specificity broken by MatchKind (MatchExact beats MatchAlias beats MatchSuffix);
- ties on match kind broken by earliest position in offered.
Accept entries with q=0 are treated as exclusions and never match. MatchSuffix results are only counted when AllowSuffix is passed. Returns ok=false if no offer matched any non-zero-q entry.