如何检查客户端是否接受特定的退货类型?

时间:2014-07-11 17:30:24

标签: php http http-headers

我正在构建一个API,我想让我的客户指定他们在我的回复中接受的内容格式(xml,json等)。

我也想使用请求的Accept部分来完成。

在我的代码中的某处,我调用以下行:

$request->getAcceptableContentTypes();

返回

array (size=5)
  0 => string 'text/html' (length=9)
  1 => string 'application/xhtml+xml' (length=21)
  2 => string 'image/webp' (length=10)
  3 => string 'application/xml' (length=15)
  4 => string '*/*' (length=3)

此特定请求同时接受xml和json返回...但我不确定如何验证。允许使用XML响应(*/*application/xhtml+xmlapplication/xml),但其主要MIME类型为text/xml,上面未明确说明。同样的事情适用于application/json*/*允许,但未明确写入。

我是否应该将所有可能的mimetypes映射到其等价物,我应该在我的客户端上强制执行特定的和明确的mimetype定义,还是有更优雅的方式来做到这一点?

编辑:澄清:我得到Accepts: application/xhtml+xml。我只准备回答Content-Type: text/xml。我应该抛出异常吗?或者我应该爆炸第二部分(xhtml + xml)并返回text/xml,因为接受的内容与我能回答的内容相似?如果是这样,我怎么知道他们“相似”?我需要一个查找表吗?我可以忽略第一部分(application)吗?

4 个答案:

答案 0 :(得分:2)

此过程称为内容协商

API Web服务返回的表示(html,json或xml)由URL或Accept HTTP标头确定。

让我们从客户端开始查看:

GET http://api.company.com/products/10 HTTP/1.1
Host: http://api.company.com
Accept: application/json, text/javascript, */*; q=0.01

服务器以

响应
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: ...
Connection: Close

{"Id":1,"ProductName":"Helicopter","Category":"Aircraft","Price":5000}

问题:如果客户接受特定的退货类型,我如何检查?"

答案:通过评估客户端发送的接受标头。

在GET请求中,客户端定义了多个接受头(按顺序)(优先级为q)。 Accept-Header列出了客户端愿意接受的mime类型。

服务器的任务是查看它拥有的可用表示,并为客户端提供最高优先级的表示。 您必须评估"接受" -headers和" q" -weight并向客户端发送适当的表示,指示Content-Type中的类型 - 标题。

" q" Accept标头中的参数是0到1之间的浮点数。它是一个权重,表示该媒体类型的首选项。较高的数字表示较高的偏好。 官方术语是相对质量因素"。

RFC描述了如何理解偏好的几个例子。 http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

这并不容易。其中一些是"重叠"。 保持简单。专注于" text / html"," application / json"," application / xml"。


如何在服务器端实现? 参加谈判课程是一种很好的做法。

让我们说:客户端请求一个有效的资源,但是具有未知的Accept标头。

GET http://api.company.com/products/10 HTTP/1.1
Host: http://api.company.com
Accept: application/karamba
  • 请求来到服务器。
  • 找到资源/产品/ 10 - 它有效。服务器有数据。
  • 现在" application / karamba"到了,但没有内容格式化。
  • 服务器无法提供客户想要其格式的格式。
  • 服务器必须发送错误406(不可接受)作为响应。

工作流程大致如下:

  1. 使用方法preferedResponseType()来请求对象以评估Accept Header
  2. 基于该类型实例化Response对象。 您可以通过将接受标头中的部分字符串映射到类来实现。
    • JsonResponse
    • XmlResponse
    • HtmlResponse
  3. 如果,此类型没有响应对象(它未知)
    • 发送ErrorResponse。 EXIT
  4. 从db
  5. 获取数据
  6. 将db结果插入响应对象,也许是JsonResponse()
  7. JsonResponse() - > flush()发送它......
  8. 关键部分是从Accept Header到可以回答该请求的Class的映射。

    最后,如果你真的想支持许多响应类型,你必须做出决定。


    好的,这么多理论。我们来看一个实时API:GitHub。


    更新(2014-09-29) 看看这个新图书馆: https://github.com/auraphp/Aura.Accept

    特别是:https://github.com/auraphp/Aura.Accept/blob/master/tests/unit/src/MediaTest.php

答案 1 :(得分:1)

你需要进行模式匹配而不是等式检查:

  • application/xml+json!= application/json
  • application/xml+json!= application/xml

application/json匹配application/xml+json

您可以做的一件事是使用+展平接受标头,如果只有*/*,则进行连续匹配,直到找到可接受的格式,否则返回406 Not Acceptable

<?php

$accept = [
    'text/html',
    'application/json+xml',
    'image/webp',
    'application/xml',
    '*/*'
];

function flattenAccept($accept) {
    $newAccpet = [];
    foreach ($accept as $mimeType) {
        if (strpos($mimeType, '+') !== false) { // Contains +
            $arr = explode('/', $mimeType);
            $type = $arr[0];
            $medias = explode('+', $arr[1]);
            foreach ($medias as $media) {
                array_push($newAccpet, $type."/".$media); // Flatten
            }
        } else {
            array_push($newAccpet, $mimeType);
        }
    }
    return array_unique($newAccpet);
}


function matching($mimeType, $accept) {
    if (in_array("*/*", $accept)) return true;
    return in_array($mimeType, $accept);
}

$newAccept = flattenAccept($accept);
var_dump($newAccept);
var_dump(matching("application/json", $newAccept));

结果

array(5) {
  [0] =>
  string(9) "text/html"
  [1] =>
  string(16) "application/json"
  [2] =>
  string(15) "application/xml"
  [3] =>
  string(10) "image/webp"
  [5] =>
  string(3) "*/*"
}
bool(true)

答案 2 :(得分:0)

大多数允许多个MIME类型响应的API都会强制客户端指定响应格式。这些通常以两种方式之一完成:作为查询参数('http://exampleAPI.com/getAllUsers?json')或作为请求标题('Accept:application / json')。

从客户端获取此信息将为您提供已接受的MIME类型的显式值,您可以决定是否要回复错误或在缺少或不正确时应用默认设置。

此解决方案也不会给您的客户端带来开销,因为在通用的客户端实现中,您将创建一个自动化身份验证的包装类,并可能在某些情况下组织您的请求并对您的响应进行后处理。 / p>

答案 3 :(得分:0)

为了将来的参考,我想指出一种替代的,更便宜的方式来完成我在这个问题上提出的问题。

Hoa/Mime是一个可以满足我要求的库。在问题发生时它还没有被释放。

print_r(Hoa\Mime\Mime::getExtensionsFromMime('text/html'));将打印

Array
(
    [0] => html
    [1] => htm
)

同样,var_dump(Hoa\Mime\Mime::getMimeFromExtension('webm'));会打印string(10) "video/webm"

Mime对象也有方法getExtension()getOtherExtensions(),因此我可以检查其他可接受的内容类型,而无需提供我自己的查找表。

这比@Issam Zoli的回答更完整,因为它提供了返回的空间,例如text/xml请求application/xml。如果一个人希望自己实施,而不是使用第三方软件包,那么他的回答非常适合对必须做的事情进行较低层次的理解。