内容协商

什么是内容协商?

内容协商是一种根据客户端的处理能力和服务器的处理能力,来确定返回给客户端什么类型的内容的方法。这可以用来确定是否应该返回 HTML 还是 JSON 给客户端,图片应该返回 JPEG 格式还是 PNG 格式,支持什么类型的压缩等等。这是通过分析四个不同的 header 来实现的,每个 header 都可以支持多种值选项,每个选项都有自己的优先级。

要手动匹配这些可以是非常具有挑战性的。CodeIgniter 提供了 Negotiator 类可以帮助你处理。

内容协商的核心只是 HTTP 规范的一部分,它允许一个资源可以服务于多种类型的内容,允许客户端请求对其最有效的的数据类型。

一个典型的例子是,一个不能显示 PNG 图片的浏览器可以请求只获取 GIF 或 JPEG 图片。当服务器收到请求时,它会查看客户端请求的可用文件类型,并从它支持的图片格式中选择最佳匹配,在这种情况下可能会选择返回一个 JPEG 图片。

同样的协商可以发生在四种类型的数据上:

  • 媒体/文档类型 - 这可能是图像格式,或者 HTML 与 XML 或 JSON。

  • 字符集 - 返回的文档应该使用的字符集,通常是 UTF-8。

  • 文档编码 - 通常是对结果使用的压缩类型。

  • 文档语言 - 对于支持多种语言的网站,这有助于确定返回哪种语言。

加载类

你可以通过 Service 类手动加载类的一个实例:

<?php

$negotiate = \Config\Services::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']);