我正在使用最新的Google PHP Client库。如何使用此库发送 etag?
我已经尝试过这样的事情:
$opt_params = array(
'etag' => 'F9iA7pnxqNgrkOutjQAa9F2k8HY/mOihMVEDLCvdSVCzwDmYHpSV');
$response = $youtube->channels->listChannels('snippet', array('id' => $channel_id), $opt_params);
谢谢!
这篇文章有点长(如果不是太长的话)。如果您只需要代码,请向下滚动并跳过我的讨论。请随心所欲地进行。
背景
我不知道是否有人想出了解决这个问题的方法。Coding Monkey的答案是一个开始。问题在于,他提出的解决方案假设 API 仅使用开发人员密钥/api 密钥而不是 oauth 访问密钥发出请求。如果您使用开发人员密钥尝试他的代码,您将获得以下内容:
调用 GET 时出错 https://www.googleapis.com/youtube/v3/liveBroadcasts?part=id%2Csnippet%2Cstatus&mine=true&maxResults=50&key={此处的开发人员密钥}: (401) 需要登录
开发者密钥不足以检索用户的 youtube 数据,开发者密钥不会授予对任何帐号信息的访问权限。这包括YouTube数据。要访问YouTube数据,必须使用oauth访问密钥(在这里阅读:api密钥)
请注意,开发人员密钥将成为查询字符串的一部分。另一方面,当您使用 oauth 密钥时,该密钥将成为请求标头:
授权:持有者 {此处的 oauth 密钥}
但是,如果您尝试使用oauth密钥,您仍然会收到与上述相同的错误。这是非常令人沮丧的。我想这就是为什么Coding Monkey的答案没有得到赞成的原因。要了解原因,必须了解谷歌客户端如何发送请求。
Google PHP 客户端如何工作
我对谷歌客户端如何在幕后工作的解释并不完整,只是基于我从弄清楚如何解决这个问题的经验中学到的东西。
谷歌客户端,我将称之为$client
,使用Google_IO_Abstract
类实例,我将调用$io
,来发送请求。可以获取当前$io
实例并使用$client->setIo()
和$client->getIo()
方法进行更改。 Google_IO_Abstract
是一个抽象类。PHP Google API lib 中可用的具体子类之一是 Google_IO_Curl
。顾名思义,该类使用 curl
.
从现在开始,我将假定$io
是一个Google_IO_Curl
实例。以下内容(来自Code Mokey的解决方案):
$client->getIo()->setOptions(array(
CURLOPT_HTTPHEADER => array('If-None-Match: "4FSIjSQU83ZJMYAO0IqRYMvZX98/OPaxOhv3eeKc9owSnDQugGfL8ss"'),
));
将添加一个 If-None-Match HTTP 标头到 curl 的标头选项。仅当调用 $io->executeRequest($request)
时,才会实际添加标头。 $client
需要发送的请求$request
。
的标头是通过 curl_setopt($curl, CURLOPT_HTTPHEADER, $headers)
完成的,其中 $curl
是一个 curl 实例,$headers
是一个标头数组。
如果您将阅读executeRequest
实现。您将看到大多数部分用于设置 curl 的选项。一个重要的部分是将 http 标头从$request
收集到一个名为 $curlHeaders
的数组中,如下所示(这是来自Google_IO_Curl
源代码)。此处还添加了Authorization
标头。
$requestHeaders = $request->getRequestHeaders();
if ($requestHeaders && is_array($requestHeaders)) {
$curlHeaders = array();
foreach ($requestHeaders as $k => $v) {
$curlHeaders[] = "$k: $v";
}
curl_setopt($curl, CURLOPT_HTTPHEADER, $curlHeaders);
}
请注意,在收集标头后,将调用 curl_setopt()
来设置 curl 的标头选项。现在问题来了,在这些代码行之后是设置其他选项的部分。使用 setOptions()
方法设置的选项,这意味着将再次调用curl_setopt($curl, CURLOPT_HTTPHEADER, $headers)
但这次$headers仅包含 If-None-Match 标头。第二次调用将替换所有以前的标头。瞧!Authorization
标头消失了。我不确定是否有人报告过这件事。我不确定这是否是故意的。但对于像我这样的初学者来说,这真的很令人沮丧。
无论如何,我想出了一个快速的解决方案。
我的解决方案
我将在这里停止讨论,而是为您提供代码(Google_IO_Curl_Mod.php):
<?php
/**
* Wrapper class for Google_IO_Curl.
*
* This class fixes issues involving request headers added using setOptions()
*
* @author Russel Villacarlos<cvsurussel_AT_gmail.com>
*/
if (!class_exists('Google_Client')) {
require_once 'Google/autoload.php';
}
class Google_IO_Curl_Mod extends Google_IO_Curl
{
//Google_IO_Curl instance
private $io;
//Array for additional headers added using setOptions()
private $headers;
public function __construct(Google_IO_Curl $io)
{
$this->io = $io;
$this->headers=[];
}
/**
* Execute an HTTP Request
*
* @param Google_Http_Request $request the http request to be executed
* @return array containing response headers, body, and http code
* @throws Google_IO_Exception on curl or IO error
*/
public function executeRequest(Google_Http_Request $request)
{
foreach ($this->headers as $value) {
if(is_string($value) && strpos($value,":")!==false){
$header = split(":s*",$value);
$request->setRequestHeaders(array($header[0]=>$header[1]));
}
}
$result = $this->io->executeRequest($request);
return $result;
}
/**
* Set options that update the transport implementation's behavior.
* @param $options
*/
public function setOptions($options)
{
if($options){
if(array_key_exists(CURLOPT_HTTPHEADER, $options)){
$this->headers= array_merge($this->headers, $options[CURLOPT_HTTPHEADER]);
unset($options[CURLOPT_HTTPHEADER]);
}
$this->io->setOptions($options);
}
}
/**
* Set the maximum request time in seconds.
* @param $timeout in seconds
*/
public function setTimeout($timeout)
{
$this->io->setTimeout($timeout);
}
/**
* Get the maximum request time in seconds.
* @return timeout in seconds
*/
public function getTimeout()
{
return $this->io->getTimeout();
}
/**
* Test for the presence of a cURL header processing bug
*
* {@inheritDoc}
*
* @return boolean
*/
protected function needsQuirk()
{
return $this->io->needsQuirk();
}
}
要使用它,只需执行以下操作:
$client = new Google_Client();
//codes for setting the oauth credential appears here
$io = new Google_IO_Curl_Mod(new Google_IO_Curl($client));
$client->setIo($io);
$client->getIo()->setOptions(array(
CURLOPT_HTTPHEADER =>
array('If-None-Match: "NO6QTeg0-3ShswIeqLchQ_mzWJs/wtg8du-olFPZ73k6UW7Jk5JcwpQ"'),
));
//codes for calling youtube api or any google api goes here
请注意,这对双引号是 etag 的一部分。
Google API PHP 客户端没有对 etag 的原生支持。也就是说,您仍然可以更改 curl 请求并将 etag 应用为标头。
不幸的是,Google_Http_REST
在收到 304 Not Modified
标头时会引发异常,因此您需要在 catch
-block 中单独处理这些异常。
这是生成的 PHP 代码(注意,这是针对 Google API PHP 客户端版本 1):
class EmptyResponse { }
$DEVELOPER_KEY = '';
$client = new Google_Client();
$client->setDeveloperKey($DEVELOPER_KEY);
$youtube = new Google_Service_YouTube($client);
// Do some logic, catch some resources
// ..
// Set the "If-None-Match" header for the etag
// This assumes the Google API is using CURL
$client->getIo()->setOptions(array(
CURLOPT_HTTPHEADER => array('If-None-Match: "4FSIjSQU83ZJMYAO0IqRYMvZX98/OPaxOhv3eeKc9owSnDQugGfL8ss"'),
));
try {
$response = $youtube->playlists->listPlaylists('snippet', array(
'id' => 'PLOIvOnfHWjIkiz6fd5KYUXJY6ZpHRqPfW',
));
}
catch (Google_Service_Exception $e) {
if (304 == $e->getCode()) {
// If the error code is 304 (Not Modified) return an empty response
$response = new EmptyResponse;
}
else {
// The request was unsuccesful
$response = null;
}
}
// After the request set the headers back to default
$client->getIo()->setOptions(array(
CURLOPT_HTTPHEADER => array(),
));
var_dump($response);
// Catch some more resources
// ...
如果您查看 channels.list 的文档,您会注意到 etag 不是可选参数之一。
因此,您不能通过 api 将 etag 发送到列表通道,这不是客户端库的限制。