Back-End

25 jul, 2013

ZendService_Api, um micro framework HTTP para consumir chamadas a APIs genéricas em PHP

Publicidade

Quantas vezes você escreve código para realizar chamadas a um serviço de API? Isso pode ser uma tarefa tediosa e repetitiva que toma algum tempo. É preciso preparar a requisição HTTP, checar o código de resposta para entender se a requisição foi bem sucedida, extrair alguma informação do cabeçalho, formatar a saída, e assim por diante.

Há alguns meses, comecei a implementar o componente ZF2 para a nova API do OpenStack e decidi criar uma classe que reduzisse a quantidade de código necessária para invocar requisições HTTP, e acabei por escrever o ZendService_Api.

Esse componente pode ser considerado um “micro framework”, porque seu objetivo é criar uma solução leve e completa para o gerenciamento de chamadas a APIs, a partir de um cliente PHP em potencial. Ele utiliza o componente Zend\Http do Zend Framework 2.

Basicamente, o ZendService_Api é capaz de preparar a requisição HTTP (mapeando parâmetros PHP) para executar a requisição HTTP (usando um objeto Zend\Http\Client) e converter a saída HTTP de acordo com um formato específico (JSON, XML, ou texto puro). Além disso, o componente é capaz de gerenciar códigos de status HTTP e atribuí-lo a operações de sucesso ou erro, de acordo com a especificação da API.

Instalação

Você pode instalar o componente ZendService_Api utilizando o composer. É preciso acrescentar as linhas abaixo em seu composer.json para habilitar o repositório ZF2:

[javascript]

"repositories": [
{
"type": "composer",
"url": "https://packages.zendframework.com/"
}
],

[/javascript]

Então você poderá acrescentar os pacotes ZendService_Api na sua lista require. Por exemplo:

[javascript]

"require": {
"zendframework/zendservice-api": "dev-master"
},
[/javascript]

O pacote ZendService_Api ainda está em desenvolvimento, e você precisa utilizar a versão “dev-master”. Depois disso, poderá executar o comando install para resolver e baixar as dependências:

[shell]

$ php composer.phar install

[/shell]

Uso

Durante o desenvolvimento do ZendService_Api, tentei simplificar a API com uma classe contendo poucos métodos e parâmetros simples. Mapiei as requisições HTTP usando a função mágica __call do PHP. Dessa forma, uma chamada a uma API é gerenciada como uma função simples (ex. $api->listUsers(); executará a requisição HTTP listUser em uma API). A especificação das requisições HTTP é fornecida através de uma closure ou de um arquivo PHP que retorna a configuração (usando um array simples). Você pode configurar a requisição HTTP com os seguintes parâmetros:

  • URL;
  • Cabeçalhos;
  • Body;
  • Métodos (GET, POST, PUT, DELETE);
  • formato da saída: JSON, XML, ou nada (texto puro).

Você pode ajustar os parâmetros da API usando o método setApi. Esse método aceita dois parâmetros: o nome da API e uma closure (callback) que retorna a configuração com um array PHP.

Permita-me exibir um exemplo; imagine que você precisa usar uma chamada a uma API de autenticação com uma requisição HTTP POST utilizando formato de dados JSON com os seguintes parâmetros: username e password. A requisição HTTP pode ser representada da seguinte forma:

[javascript]

PUT /v1/auth HTTP/1.1
Host: localhost
Connection: close
Content-Type: application/json
Content-Length: 57

{ ‘auth’ : { ‘username’ : ‘admin’, ‘password’ : ‘test’ }}

[/javascript]

Uma resposta válida seria um código HTTP 200 com uma representação JSON do token de autenticação. Você pode configurar a chamada a API utilizando o método setApi desta forma (eu utilizei o nome “auth” para essa API):

[php]

use ZendService\Api\Api;

$api = new Api();
$api->setApi(‘auth’, function ($params) {
return array(
‘uri’ => ‘http://localhost/v1/auth’,
‘header’ => array(
‘Content-Type’ => ‘application/json’
),
‘method’ => ‘POST’,
‘body’ => json_encode(array(
‘auth’ => array(
‘username’ => $params[0],
‘password’ => $params[1]
)
)),
‘response’ => array(
‘format’ => ‘json’,
‘valid_codes’ => array(‘200’)
)
);
});

[/php]

Depois disso, você pode utilizar a chamada a API utilizando a função auth (essa função é gerenciada pela função __call do PHP):

[php]

managed by the magic __call function of PHP):

$result = $api->auth(‘username’, ‘password’);
if ($api->isSuccess()) {
if (isset($result[‘token’])) {
printf("The authentication token is %s\n", $result[‘token’]);
} else {
var_dump($result);
}
} else {
printf("Error (%d): %s\n", $api->getStatusCode(), $api->getErrorMsg());
}

[/php]

O mapeamento com os argumentos do auth e a especificação da API é gerenciado utilizando o array $params. É preciso utilizar o índice numérico de $params para combinar a ordem dos argumentos na função. Utilizando o array de configuração, você pode especificar todos os dados HTTP para requisições à API (cabeçalhos, body, uri etc.). Você também pode especificar os códigos de status HTTP para requisições bem sucedidas utilizando o parâmetro valid_codes na seção response. O formato do body da resposta HTTP é especificado pela chave [‘response’][‘format’]. Se você especificar o formato de resposta, o resultado será um array.

Pode-se usar também um arquivo de configuração para chamadas à API em vez do método setApi. É preciso criar um arquivo PHP com o mesmo nome da chamada à API. Esse arquivo contém o array de configuração da API. Como ilustração, no exemplo anterior você teria que ter criado um arquivo auth.php contendo o seguinte array:

[php]

return array(
‘uri’ => ‘http://localhost/v1/auth’,
‘header’ => array(
‘Content-Type’ => ‘application/json’
),
‘method’ => ‘POST’,
‘body’ => json_encode(array(
‘auth’ => array(
‘username’ => $params[0],
‘password’ => $params[1]
)
)),
‘response’ => array(
‘format’ => ‘json’,
‘valid_codes’ => array(‘200’)
)
);

[/php]

É necessário configurar um diretório contendo estes arquivos de configuração utilizando o setApiPath:

[php]

use ZendService\Api\Api;

$api = new Api();
$api->setApiPath(‘path/to/api/config’);
$result = $api->auth(‘username’, ‘password’);
if ($api->isSuccess()) {
if (isset($result[‘token’])) {
printf("The authentication token is %s\n", $result[‘token’]);
} else {
var_dump($result);
}
} else {
printf("Error (%d): %s\n", $api->getStatusCode(), $api->getErrorMsg());
}

[/php]

Se você precisar chamar uma API diferente com a mesma URL de base, pode usar a função setUrl. Essa função ajusta a URL base, e você pode fazer uso de URI relativa para especificar as chamadas à API. Como exemplo, imagine que você precisa utilizar a API v.2.0 de autenticação do OpenStack de acordo com a especificação reportada aqui.

Podemos ajustar a configuração abaixo, utilizando o endereço principal como URL base e fazendo uso de endereços relativos para cada chamada a API.

[php]

use ZendService\Api\Api;

$api = new Api();
$api->setUri(‘http://identity.api.openstack.org’);
$api->setApi(‘authentication’, function ($params) {
return array(
‘url’ => ‘/v2.0/tokens’,
‘header’ => array(
‘Content-Type’ => ‘application/json’
),
‘method’ => ‘POST’,
‘body’ => json_encode(array(
‘auth’ => array(
‘passwordCredentials’ => array(
‘username’ => $params[0],
‘password’ => $params[1]
)
)
)),
‘response’ => array(
‘format’ => ‘json’,
‘valid_codes’ => array(‘200’, ‘203’)
)
);
});
$result = $api->authentication(‘username’, ‘password’);
if ($api->isSuccess()) {
printf("Authentication token: %s\n", $result[‘access’][‘token’][‘id’]);
} else {
printf("Error (%d): %s\n", $api->getStatusCode(), $api->getErrorMsg());
}

[/php]

Note o uso de endereços relativos no parâmetro url da configuração da API.

Se precisar passar uma string de query para uma chamada HTTP, a API poderá usar o método setQueryParams da classe Api. Como exemplo, imagine que você precisa passar a string de query HTTP ?auth=strong. No exemplo anterior, você poderia usar o seguinte código:

[php]

use ZendService\Api\Api;

$api = new Api();
$api->setQueryParams(array( ‘auth’ => ‘strong’ ));
$result = $api->authenticate(‘username’, ‘password’);
if ($api->isSuccess()) {
printf("OK!\n");
} else {
printf("Error (%d): %s\n", $api->getStatusCode(), $api->getErrorMsg());
}

[/php]

É possível resetar a string de query ao chamar a função setQueryParams() sem parâmetro algum.

Você pode especificar o cabeçalho HTTP padrão para ser usado para todas as requisições HTTP. Por exemplo, se precisar fazer uso de uma API e passar um token de autenticação utilizando um campo de cabeçalho especial, você pode usar esse recurso para definir o cabeçalho padrão para utilizado para as próximas chamadas à API.

Para definir um cabeçalho padrão, você pode usar a função setHeader, o código abaixo relata um exemplo:

[php]

use ZendService\Api\Api;

$api = new Api();
$api->setApiPath(‘path/to/api/config’);
$api->setHeaders(array( ‘X-Auth-Token’ => ‘token’ ));
$result = $api->test(‘foo’);
if ($api->isSuccess()) {
var_dump($result);
} else {
printf("Error (%d): %s\n", $api->getStatusCode(), $api->getErrorMsg());
}

[/php]

A API test executará uma requisição HTTP usando os cabeçalhos especificados no arquivo de configuração test.php mais o cabeçalho X-Auth-Token. Basicamente, os cabeçalhos especificados no arquivo de configuração são mesclados com o cabeçalho padrão especificado ao utilizar a função setHeaders. Você pode sobrescrever o cabeçalho padrão utilizando a mesma chave armazenada no arquivo de configuração.

Conclusão

Espero que o componente ZendService_Api possa auxiliar os desenvolvedores PHP a consumirem APIs HTTP e a escrever bibliotecas PHP para fornecedores de APIs com um mínimo de esforço. Estou usando essa classe em alguns componentes ZendService do projeto Zend Framework 2 e posso dizer que ele reduziu drasticamente o tempo de desenvolvimento dessas bibliotecas.

A implementação atual do ZendService_Api ainda está em desenvolvimento, portanto, se você quiser contribuir com código ou apenas fazer comentários e fornecer feedback, será mais do que bem-vindo. Para contribuições, utilize o repositório github oficial do componente em https://github.com/zendframework/ZendService_Api

***

Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://www.zimuel.it/en/zendservice-api-micro-http-framework/