Php клиент Google API: отправить etag
Я использую новейшую клиентскую библиотеку Google PHP. Как я могу отправить etag с этой библиотекой?
Я уже пробовал что-то вроде этого:
$opt_params = array(
'etag' => 'F9iA7pnxqNgrkOutjQAa9F2k8HY/mOihMVEDLCvdSVCzwDmYHpSV');
$response = $youtube->channels->listChannels('snippet', array('id' => $channel_id), $opt_params);
Спасибо!
3 ответа
Этот пост немного длинный (если не слишком длинный). Если вам нужен код, прокрутите вниз и пропустите мои обсуждения. Пожалуйста, продолжайте, как хотите.
Фон
Я не знаю, нашел ли кто-нибудь решение для этого. Ответ Кодированной Обезьяны - это начало. Проблема в том, что представленное им решение предполагает, что 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, вы все равно получите ту же ошибку, что и выше. Это довольно сложно. И я думаю, именно поэтому ответ Coded Monkey не получил одобрения. Чтобы понять почему, нужно понять, как клиент Google отправляет запросы.
Как работает клиент Google PHP
Мое объяснение того, как клиент Google работает за кулисами, не является полным и основано только на том, что я узнал из своего опыта в выяснении, как решить эту проблему.
Клиент Google, который я назову $client
, использует Google_IO_Abstract
экземпляр класса, я позвоню $io
, для отправки запросов. Можно получить ток $io
экземпляр и изменить его с помощью $client->setIo()
а также $client->getIo()
методы. Google_IO_Abstract
это абстрактный класс. Одним из его конкретных подклассов, доступных в PHP API Google Google, является Google_IO_Curl
, Как следует из названия, класс использует curl
,
С этого момента я буду предполагать, что $io
это Google_IO_Curl
пример. Следующее (из решения Code Mokey):
$client->getIo()->setOptions(array(
CURLOPT_HTTPHEADER => array('If-None-Match: "4FSIjSQU83ZJMYAO0IqRYMvZX98/OPaxOhv3eeKc9owSnDQugGfL8ss"'),
));
добавит HTTP-заголовок If-None-Match к опции заголовка curl. Фактическое добавление заголовка происходит только тогда, когда $io->executeRequest($request)
называется. $request
это запрос $client
необходимо отправить.
Настройка заголовка curl осуществляется через curl_setopt($curl, CURLOPT_HTTPHEADER, $headers)
, где $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)
но на этот раз заголовки $ содержат только заголовок 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.
PHP-клиент Google API не имеет встроенной поддержки etags. Тем не менее, вы все равно можете изменить запрос curl и применить etag в качестве заголовка.
к несчастью Google_Http_REST
выдает исключение, когда получает 304 Not Modified
заголовок, так что вам нужно обрабатывать их отдельно в catch
-блок.
Вот результирующий PHP-код (обратите внимание, это для версии 1 Google API PHP Client):
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
// ...
Если вы проверите документацию для channel.list, вы заметите, что etag не является одним из необязательных параметров.
Таким образом, вы не можете отправить etag в список каналов через API, это не ограничение клиентской библиотеки.