内容协商
什么是内容协商?
内容协商是一种根据客户端的处理能力和服务器的处理能力,来确定返回给客户端什么类型的内容的方法。这可以用来确定是否应该返回 HTML 还是 JSON 给客户端,图片应该返回 JPEG 格式还是 PNG 格式,支持什么类型的压缩等等。这是通过分析四个不同的 header 来实现的,每个 header 都可以支持多种值选项,每个选项都有自己的优先级。
要手动匹配这些可以是非常具有挑战性的。CodeIgniter 提供了 Negotiator
类可以帮助你处理。
内容协商的核心只是 HTTP 规范的一部分,它允许一个资源可以服务于多种类型的内容,允许客户端请求对其最有效的的数据类型。
一个典型的例子是,一个不能显示 PNG 图片的浏览器可以请求只获取 GIF 或 JPEG 图片。当服务器收到请求时,它会查看客户端请求的可用文件类型,并从它支持的图片格式中选择最佳匹配,在这种情况下可能会选择返回一个 JPEG 图片。
同样的协商可以发生在四种类型的数据上:
媒体/文档类型 - 这可能是图像格式,或者 HTML 与 XML 或 JSON。
字符集 - 返回的文档应该使用的字符集,通常是 UTF-8。
文档编码 - 通常是对结果使用的压缩类型。
文档语言 - 对于支持多种语言的网站,这有助于确定返回哪种语言。
加载类
你可以通过 Service 类手动加载类的一个实例:
<?php
$negotiate = service('negotiator');
这将获取当前的请求实例并自动注入 Negotiator 类。
这个类不需要单独加载。相反,它可以通过这个请求的 IncomingRequest
实例访问。虽然你不能直接通过这种方式访问它,但你可以通过 negotiate()
方法轻松访问所有方法:
<?php
$request->negotiate('media', ['foo', 'bar']);
当以这种方式访问时,第一个参数是你正在尝试找到匹配项的内容类型,第二个是支持的值的数组。
协商
在这一节中,我们将讨论可以协商的 4 种类型的内容,并展示使用上面描述的两种方法访问协商器的方式。
媒体
首先要看的是处理“媒体”协商。这些是由 Accept
头提供的,它是可用的最复杂的头之一。一个常见的例子是客户端告诉服务器它希望数据的格式。这在 API 中特别常见。例如,一个客户端可能会从 API 端点请求 JSON 格式的数据:
GET /foo HTTP/1.1
Accept: application/json
服务器现在需要提供它可以提供的内容类型列表。在这个例子中,API 可能可以以原始 HTML、JSON 或 XML 的形式返回数据。这个列表应该按首选项顺序提供:
<?php
$supported = [
'application/json',
'text/html',
'application/xml',
];
$format = $request->negotiate('media', $supported);
// or
$format = $negotiate->media($supported);
在这种情况下,客户端和服务器都可以就将数据格式化为 JSON 达成一致,所以从 negotiate 方法返回的是 ‘json’。默认情况下,如果没有找到匹配项,将返回 $supported
数组中的第一个元素。然而,在某些情况下,你可能需要强制格式严格匹配。如果你传递 true
作为最后一个值,则如果未找到匹配项,它将返回一个空字符串:
<?php
$format = $request->negotiate('media', $supported, true);
// or
$format = $negotiate->media($supported, true);
语言
另一个常见的用法是确定应为内容提供的语言。如果你只运行单语言站点,这显然不会有太大差异,但任何可以提供内容多种翻译的站点都会发现这很有用,因为浏览器通常会在 Accept-Language
头中发送首选语言:
GET /foo HTTP/1.1
Accept-Language: fr; q=1.0, en; q=0.5
在这个例子中,浏览器更倾向于法语,其次是英语。 如果你的网站支持英语和德语,你会这样做:
<?php
$supported = [
'en',
'de',
];
$lang = $request->negotiate('language', $supported);
// or
$lang = $negotiate->language($supported);
在这个例子中,’en’ 将作为当前语言返回。如果没有找到匹配,它将返回 $supported
数组中的第一个元素,所以那应该总是首选语言。
编码
Accept-Encoding
头包含客户端偏好接收的字符集,并用于指定客户端支持的压缩类型:
GET /foo HTTP/1.1
Accept-Encoding: compress, gzip
你的 Web 服务器将定义你可以使用的压缩类型。一些,比如 Apache,只支持 gzip:
<?php
$type = $request->negotiate('encoding', ['gzip']);
// or
$type = $negotiate->encoding(['gzip']);
参见更多内容 Wikipedia。
字符集
期望的字符集通过 Accept-Charset
头传递:
GET /foo HTTP/1.1
Accept-Charset: utf-16, utf-8
默认情况下,如果未找到匹配项,将返回 utf-8:
<?php
$charset = $request->negotiate('charset', ['utf-8']);
// or
$charset = $negotiate->charset(['utf-8']);