Hcs.Plug.Negotiate
hcs · API reference
Content negotiation plug.
Parses Accept headers and selects response format based on client preferences and server capabilities. Follows RFC 7231 content negotiation semantics.
Usage
(* In router pipeline *)
let pipeline =
Plug.Negotiate.create ~formats:[ Json; Html ] () @> Plug.identity
(* In handler *)
let handler req =
match get_format req with
| Some Json -> Response.json {|{"message": "hello"}|}
| Some Html -> Response.html "<h1>Hello</h1>"
| _ -> Response.text "hello"
(* Or use respond helper *)
let handler req =
respond req
~json:(fun () -> {|{"message": "hello"}|})
~html:(fun () -> "<h1>Hello</h1>")Types
type media_type = {
type_ : string; (* e.g., "application" *)
subtype : string; (* e.g., "json" *)
quality : float; (* 0.0 - 1.0, default 1.0 *)
params : (string * string) list; (* Additional parameters *)
}Parsed media type from Accept header
type format =
| Json
| Html
| Text
| Xml
| Csv
| Custom of string (* Custom MIME type *)Response format - common formats plus custom
type error =
| Not_acceptableContent negotiation errors
exception Not_acceptable_exnFormat Utilities
val mime_type_of_format : format -> stringGet MIME type string for a format
val format_of_mime_type : string -> format optionGet format from MIME type string
val format_name : format -> stringGet short name for format (for internal use)
Accept Header Parsing
val skip_ws : string -> int -> intSkip whitespace in string starting at position i
val parse_token : string -> int -> string * intParse a token (sequence of non-separator chars)
val parse_quoted : string -> int -> string * intParse a quoted string
val parse_value : string -> int -> string * intParse a parameter value (token or quoted-string)
val parse_params : string -> int -> (string * string) list * float * intParse parameters after media type: ;name=value pairs
val parse_media_type : string -> int -> (media_type * int) optionParse a single media type entry
val parse_accept : string -> media_type listParse Accept header into list of media types, sorted by quality (highest first).
Handles:
- Multiple types separated by commas
- Quality values (q=0.8)
- Parameters (charset=utf-8)
- Wildcards (* / * and type/ *) parameter header Accept header value returns List of media types sorted by quality descending
Negotiation
val media_type_matches : media_type -> format -> boolCheck if a media type matches a format
val negotiate : accept:string -> available:format list -> format optionNegotiate best format from Accept header.
parameter accept Accept header value parameter available List of formats the server can produce returns Best matching format or None if no match
val negotiate_exn : accept:string -> available:format list -> formatNegotiate best format, raising exception if no match.
parameter accept Accept header value
parameter available List of formats the server can produce
returns Best matching format
raises Not_acceptable_exn if no format matches
Request Format Storage
val format_header : stringKey for storing negotiated format in request. We store format as a header since requests are immutable records.
val get_format : Server.request -> format optionGet negotiated format from request (set by Negotiate plug)
val set_format : Server.request -> format -> Server.requestSet negotiated format on request
Response Helpers
val respond_format : format -> body:string -> Server.responseCreate response with correct Content-Type for format
val respond :
Server.request ->
?json:(unit -> string) ->
?html:(unit -> string) ->
?text:(unit -> string) ->
?xml:(unit -> string) ->
unit ->
Server.responseRespond based on negotiated format with lazy body generation. Only evaluates the body function for the selected format.
parameter req Request with negotiated format parameter json Function to generate JSON body parameter html Function to generate HTML body parameter text Optional function for plain text (defaults to json) parameter xml Optional function for XML returns Response with appropriate body and Content-Type
Plug
val create :
formats:format list ->
unit ->
(Server.request -> Server.response) ->
Server.request ->
Server.responseCreate content negotiation plug.
Parses the Accept header and determines the best response format. If no acceptable format is found, returns 406 Not Acceptable.
parameter formats List of formats this endpoint can produce returns Plug that sets negotiated format on request