我正在构建一个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+xml
和application/xml
),但其主要MIME类型为text/xml
,上面未明确说明。同样的事情适用于application/json
,*/*
允许,但未明确写入。
我是否应该将所有可能的mimetypes映射到其等价物,我应该在我的客户端上强制执行特定的和明确的mimetype定义,还是有更优雅的方式来做到这一点?
编辑:澄清:我得到Accepts: application/xhtml+xml
。我只准备回答Content-Type: text/xml
。我应该抛出异常吗?或者我应该爆炸第二部分(xhtml + xml)并返回text/xml
,因为接受的内容与我能回答的内容相似?如果是这样,我怎么知道他们“相似”?我需要一个查找表吗?我可以忽略第一部分(application
)吗?
答案 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
工作流程大致如下:
关键部分是从Accept Header到可以回答该请求的Class的映射。
最后,如果你真的想支持许多响应类型,你必须做出决定。
好的,这么多理论。我们来看一个实时API:GitHub。
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Content-Type:application/json; charset=utf-8
更新(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
。如果一个人希望自己实施,而不是使用第三方软件包,那么他的回答非常适合对必须做的事情进行较低层次的理解。