NAME
PAGI::Request::Negotiate - Content negotiation utilities for PAGI
SYNOPSIS
use PAGI::Request::Negotiate;
# Parse Accept header
my @types = PAGI::Request::Negotiate->parse_accept(
'text/html, application/json;q=0.9, */*;q=0.1'
);
# Returns: (['text/html', 1], ['application/json', 0.9], ['*/*', 0.1])
# Find best match
my $best = PAGI::Request::Negotiate->best_match(
['application/json', 'text/html'],
'text/html, application/json;q=0.9'
);
# Returns: 'text/html'
# Check if type is acceptable
my $accepts = PAGI::Request::Negotiate->accepts_type(
'text/html, application/json',
'json'
);
# Get quality value for type
my $quality = PAGI::Request::Negotiate->quality_for_type(
'application/json;q=0.9',
'json'
);
DESCRIPTION
PAGI::Request::Negotiate provides utilities for HTTP content negotiation, including parsing Accept headers and finding the best matching content type.
This module supports quality values, wildcards (*/*, type/*), and common MIME type shortcuts (json, html, xml, etc.).
CLASS METHODS
parse_accept
my @types = PAGI::Request::Negotiate->parse_accept($header);
Parse an Accept header and return a list of arrayrefs containing [media_type, quality] sorted by preference (quality descending, then specificity descending).
If no Accept header is provided, returns a single entry for */* with quality 1.
Quality values are clamped to the range [0, 1].
Specificity ordering: exact types > type/* > */*
best_match
my $type = PAGI::Request::Negotiate->best_match(\@supported, $accept_header);
Find the best matching content type from @supported based on the Accept header. Returns the best match or undef if none acceptable.
@supported can contain full MIME types or shortcuts (html, json, xml, etc.)
The returned value is from the @supported array (preserves shortcuts).
type_matches
my $bool = PAGI::Request::Negotiate->type_matches($type, $pattern);
Check if a media type matches a pattern. Patterns can include wildcards like */* or text/*.
Both type and pattern are compared case-insensitively.
normalize_type
my $mime = PAGI::Request::Negotiate->normalize_type($type);
Convert a type shortcut to its full MIME type. Known shortcuts include:
html => text/html
json => application/json
xml => application/xml
atom => application/atom+xml
rss => application/rss+xml
text => text/plain
txt => text/plain
css => text/css
js => application/javascript
png => image/png
jpg => image/jpeg
jpeg => image/jpeg
gif => image/gif
svg => image/svg+xml
pdf => application/pdf
zip => application/zip
form => application/x-www-form-urlencoded
If the type is already a MIME type (contains '/'), it's returned as-is.
Unknown shortcuts are prefixed with 'application/'.
accepts_type
my $bool = PAGI::Request::Negotiate->accepts_type($accept_header, $type);
Check if a specific content type is acceptable based on the Accept header.
The type can be a full MIME type or a shortcut.
Returns false if the type has quality=0 (explicitly rejected).
quality_for_type
my $q = PAGI::Request::Negotiate->quality_for_type($accept_header, $type);
Get the quality value for a specific type. Returns 0 if not acceptable.
When multiple patterns match (e.g., both text/* and text/html), returns the quality of the most specific match.
The type can be a full MIME type or a shortcut.
EXAMPLES
Content Negotiation in a PAGI App
use PAGI::Request::Negotiate;
async sub app ($scope, $receive, $send) {
my $accept = $scope->{headers}{accept} // '*/*';
my $format = PAGI::Request::Negotiate->best_match(
['json', 'html', 'xml'],
$accept
);
my ($body, $content_type);
if ($format eq 'json') {
$body = '{"message":"Hello"}';
$content_type = 'application/json';
} elsif ($format eq 'html') {
$body = '<h1>Hello</h1>';
$content_type = 'text/html';
} else {
$body = '<message>Hello</message>';
$content_type = 'application/xml';
}
await $send->({
type => 'http.response.start',
status => 200,
headers => [['content-type', $content_type]],
});
await $send->({
type => 'http.response.body',
body => $body,
});
}
SEE ALSO
RFC 7231 Section 5.3 - Content Negotiation
AUTHOR
PAGI Contributors