Skip to main content

Negotiation

Content Negotiation

MIME types

Commonly used data formats:

const (
MIMEApplicationJSON = "application/json"
MIMEApplicationJSONCharsetUTF8 = "application/json; charset=UTF-8"
MIMEApplicationJavaScript = "application/javascript"
MIMEApplicationJavaScriptCharsetUTF8 = "application/javascript; charset=UTF-8"
MIMEApplicationXML = "application/xml"
MIMEApplicationXMLCharsetUTF8 = "application/xml; charset=UTF-8"
MIMETextXML = "text/xml"
MIMETextXMLCharsetUTF8 = "text/xml; charset=UTF-8"
MIMEApplicationForm = "application/x-www-form-urlencoded"
MIMEApplicationProtobuf = "application/protobuf"
MIMEApplicationMsgpack = "application/msgpack"
MIMETextHTML = "text/html"
MIMETextHTMLCharsetUTF8 = "text/html; charset=UTF-8"
MIMETextPlain = "text/plain"
MIMETextPlainCharsetUTF8 = "text/plain; charset=UTF-8"
MIMEMultipartForm = "multipart/form-data"
MIMEOctetStream = "application/octet-stream"
)

Headers

Some commonly used header names:

const (
HeaderAccept = "Accept"
HeaderAcceptCharset = "Accept-Charset"
HeaderAcceptEncoding = "Accept-Encoding"
HeaderAcceptLanguage = "Accept-Language"
// HeaderAllow is the name of the "Allow" header field used to list the set of methods
// advertised as supported by the target resource. Returning an Allow header is mandatory
// for status 405 (method not found) and useful for the OPTIONS method in responses.
// See RFC 7231: https://datatracker.ietf.org/doc/html/rfc7231#section-7.4.1
HeaderAllow = "Allow"
HeaderAuthorization = "Authorization"
HeaderContentDisposition = "Content-Disposition"
HeaderContentEncoding = "Content-Encoding"
HeaderContentLength = "Content-Length"
HeaderContentType = "Content-Type"
HeaderCookie = "Cookie"
HeaderSetCookie = "Set-Cookie"
HeaderIfModifiedSince = "If-Modified-Since"
HeaderLastModified = "Last-Modified"
HeaderLocation = "Location"
HeaderUpgrade = "Upgrade"
HeaderVary = "Vary"
HeaderWWWAuthenticate = "WWW-Authenticate"
HeaderXForwardedFor = "X-Forwarded-For"
HeaderXForwardedProto = "X-Forwarded-Proto"
HeaderXForwardedProtocol = "X-Forwarded-Protocol"
HeaderXForwardedSsl = "X-Forwarded-Ssl"
HeaderXUrlScheme = "X-Url-Scheme"
HeaderXHTTPMethodOverride = "X-HTTP-Method-Override"
HeaderXRealIP = "X-Real-IP"
HeaderXRequestID = "X-Request-ID"
HeaderXRequestedWith = "X-Requested-With"
HeaderServer = "Server"
HeaderOrigin = "Origin"
HeaderCacheControl = "Cache-Control"
HeaderConnection = "Connection"

// Access control
HeaderAccessControlRequestMethod = "Access-Control-Request-Method"
HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers"
HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin"
HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods"
HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers"
HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials"
HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers"
HeaderAccessControlMaxAge = "Access-Control-Max-Age"

// Security
HeaderStrictTransportSecurity = "Strict-Transport-Security"
HeaderXContentTypeOptions = "X-Content-Type-Config"
HeaderXXSSProtection = "X-XSS-Protection"
HeaderXFrameOptions = "X-Frame-Config"
HeaderContentSecurityPolicy = "Content-Security-Policy"
HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"
HeaderXCSRFToken = "X-CSRF-Token"
HeaderReferrerPolicy = "Referrer-Policy"
)

type Negotiator

Content negotiation struct

type Negotiator struct {
// Cache capacity, we reuse parsing results when negotiation headers are the same.
capacity int
// Sometimes the parsed Accept values are not the standard
// values defined by W3C. We rewrite them into standard format through this function.
onParse func(*Accept)
}

An HTTP request content negotiation tool that handles negotiation based on the following headers:

  • Accept, content types the client can handle, expressed as MIME types;
  • Accept-Charset, charset types the client can handle, such as utf-8 or iso-8859-1;
  • Accept-Encoding, content encoding methods the client can understand, typically compression algorithms;
  • Accept-Language, natural languages the client can understand, such as zh.

func NewNegotiator

func NewNegotiator(capacity int, onParse func(*Accept)) *Negotiator

Creates a content negotiation tool.

func (*Negotiator) Slice

func (n *Negotiator) Slice(header string) AcceptSlice

Parses HTTP Accept(-Charset|-Encoding|-Language) headers and returns an AcceptSlice instance. The result is sorted in descending order according to the type and weight factor. If the type and weight are the same, they are sorted by order of appearance.

We can refer to the standard: https://www.rfc-editor.org/rfc/rfc9110.html#name-accept

func (*Negotiator) Charset

func (n *Negotiator) Charset(r *http.Request, charsets ...string) string

Parses the Accept-Charset header and returns the charset with the highest weight from the provided charset list charsets. Returns an empty string if there's no match.

func (*Negotiator) Encoding

func (n *Negotiator) Encoding(r *http.Request, encodings ...string) string

Parses the Accept-Encoding header and returns the encoding with the highest weight from the provided encoding list encodings. Returns an empty string if there's no match.

func (*Negotiator) Language

func (n *Negotiator) Language(r *http.Request, languages ...string) string

Parses the Accept-Language header and returns the language with the highest weight from the provided language list languages. Returns an empty string if there's no match.

func (*Negotiator) Type

func (n *Negotiator) Type(r *http.Request, types ...string) string

Parses the Accept header and returns the content type with the highest weight from the provided content type list types. Returns an empty string if there's no match. The types parameter values can be standard media types (like application/json) or extensions (like json, xml, etc.).

func (*Negotiator) Accepts

func (n *Negotiator) Accepts(header string, ctypes ...string) string

Parses header header and returns the content type with the highest weight from the provided parameter list ctypes. Returns an empty string if there's no match. This method is a combined version of the above 4 methods, but differs slightly from Negotiator#Type in that it doesn't support judging content types by extension.

type Accept

type Accept struct {
Type string
Subtype string
Quality float64
Extensions map[string]any
}

Represents a parsed unit value of Accept(-Charset|-Encoding|-Language) headers.

type AcceptSlice

type AcceptSlice []Accept

Represents parsed values of Accept(-Charset|-Encoding|-Language) headers, which is a slice of Accept structs.

func (AcceptSlice) Len()

func (as AcceptSlice) Len() int

Implements the Len() method of the sort.Interface interface.

func (AcceptSlice) Less

func (as AcceptSlice) Less(i, j int) bool

Implements the Less() method of the sort.Interface interface, sorting elements in descending order of priority.

func (AcceptSlice) Swap

func (as AcceptSlice) Swap(i, j int)

Implements the Swap() method of the sort.Interface interface.

func (AcceptSlice) Negotiate

func (as AcceptSlice) Negotiate(ctypes ...string) (string, int, error)

Returns a type from the slice that can be accepted by the client. Returns an empty string if no type is found.

func (AcceptSlice) Accepts

func (as AcceptSlice) Accepts(ctype string) bool

Returns true if the provided type ctype can be accepted by the client, otherwise returns false.