deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

103

description

Guzzle es un cliente HTTP que facilita de forma increíble el crear, consumir e interactuar con servicios web, como por ejemplo, la api REST de un servicio web como Twitter, Amazon AWS, Github, entre otros. Nos ofrece completo control del estándar HTTP para la generación de un Request y nos permite de forma organizada manejar el Response y definir modelos sobre las respuestas para actuar mas facilmente sobre ellas. En esta charla se introducirá como configurar la libreria de forma independiente en menos de 10 minutos, y como integrarlo en un proyecto de Symfony2 y Silex, para empezar a atacar APIs, se explicaran sus usos mas comunes, se hablara de proyectos que ya lo tienen incorporado (como Drupal, el Amazon AWS SDK para PHP, y la libreria Goutte, para hacer Webscraping, de Fabien Potencier, entre otras). Se terminara con un ejemplo practico en dos proyectos de Acilia, para atacar a un servicio web (emailvision), para envíos de emails transaccionales y masivos de dos aplicaciones y posterior recopilación de reportes de dichos envíos, todo mediante llamadas y respuestas HTTP a una API REST.

Transcript of deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Page 1: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2
Page 2: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2
Page 3: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Albert Jessurum

Page 4: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Albert Jessurum

acilia.es

Page 5: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Albert Jessurum

acilia.es

chess.com

Page 6: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Albert Jessurum

acilia.es

chess.com

Maracaibo, Venezuela

Page 7: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Albert Jessurum

acilia.es

chess.com

Maracaibo, Venezuela

@ajessu

Page 8: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Albert Jessurum

acilia.es

chess.com

Maracaibo, Venezuela

@ajessu

github.com/ajessu

Page 9: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Albert Jessurum

acilia.es

chess.com

Maracaibo, Venezuela

@ajessu

github.com/ajessu

Fan de Symfony

Page 10: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Albert Jessurum

acilia.es

chess.com

Maracaibo, Venezuela

@ajessu

github.com/ajessu

Fan de Symfony

Page 11: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Albert Jessurum

acilia.es

chess.com

Maracaibo, Venezuela

@ajessu

github.com/ajessu

Fan de Symfony

Page 12: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle

Page 13: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

¿Que es Guzzle?

Page 14: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

¿Que es Guzzle?

Page 15: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

¿Que es Guzzle?

• Cliente para realizar peticiones http

Page 16: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

¿Que es Guzzle?

• Cliente para realizar peticiones http

• cURL

Page 17: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

¿Que es Guzzle?

• Cliente para realizar peticiones http

• cURL

• Conexiones persistentes

Page 18: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

¿Que es Guzzle?

• Cliente para realizar peticiones http

• cURL

• Conexiones persistentes

• Peticiones paralelas

Page 19: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

¿Que es Guzzle?

• Cliente para realizar peticiones http

• cURL

• Conexiones persistentes

• Peticiones paralelas

• Plugins

Page 20: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

¿Por que Guzzle?

Page 21: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

¿Por que Guzzle?

Page 22: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

¿Por que Guzzle?

• Peticiones http en general

Page 23: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

¿Por que Guzzle?

• Peticiones http en general

• API

Page 24: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

¿Por que Guzzle?

• Peticiones http en general

• API

• Scrapping

Page 25: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

¿Por que Guzzle?

• Peticiones http en general

• API

• Scrapping

• Envio de datos/reportes (POST)

Page 26: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Alternativas

Page 27: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Alternativas

Page 28: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Alternativas

• file_get_contents/fopen

Page 29: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Alternativas

• file_get_contents/fopen

• cURL

Page 30: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Alternativas

• file_get_contents/fopen

• cURL

• pecl_http

Page 31: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Alternativas

• file_get_contents/fopen

• cURL

• pecl_http

• Zend_Http

Page 32: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Alternativas

• file_get_contents/fopen

• cURL

• pecl_http

• Zend_Http

• Buzz

Page 33: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Proyectos que usan Guzzle

Page 34: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Proyectos que usan Guzzle

Page 35: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Proyectos que usan Guzzle

• Drupal 8

Page 36: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Proyectos que usan Guzzle

• Drupal 8

• Goutte

Page 37: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Proyectos que usan Guzzle

• Drupal 8

• Goutte

• Amazon AWS

Page 38: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en menos de 1 minuto

Page 39: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en menos de 1 minuto

Page 40: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en menos de 1 minuto

// Version >= 3.7

Page 41: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en menos de 1 minuto

// Version >= 3.7Guzzle\Http\StaticClient::mount();

Page 42: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en menos de 1 minuto

// Version >= 3.7Guzzle\Http\StaticClient::mount();$response = Guzzle::get('http://desymfony.com');

Page 43: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en menos de 1 minuto

// Version >= 3.7Guzzle\Http\StaticClient::mount();$response = Guzzle::get('http://desymfony.com');

Page 44: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en menos de 1 minuto

// Version >= 3.7Guzzle\Http\StaticClient::mount();$response = Guzzle::get('http://desymfony.com');

// Para un poco mas de control

Page 45: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en menos de 1 minuto

// Version >= 3.7Guzzle\Http\StaticClient::mount();$response = Guzzle::get('http://desymfony.com');

// Para un poco mas de controluse Guzzle\Http\Client;

Page 46: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en menos de 1 minuto

// Version >= 3.7Guzzle\Http\StaticClient::mount();$response = Guzzle::get('http://desymfony.com');

// Para un poco mas de controluse Guzzle\Http\Client;

Page 47: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en menos de 1 minuto

// Version >= 3.7Guzzle\Http\StaticClient::mount();$response = Guzzle::get('http://desymfony.com');

// Para un poco mas de controluse Guzzle\Http\Client;

$client = new Client('http://desymfony.com');

Page 48: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en menos de 1 minuto

// Version >= 3.7Guzzle\Http\StaticClient::mount();$response = Guzzle::get('http://desymfony.com');

// Para un poco mas de controluse Guzzle\Http\Client;

$client = new Client('http://desymfony.com');$request = $client->get('/');

Page 49: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en menos de 1 minuto

// Version >= 3.7Guzzle\Http\StaticClient::mount();$response = Guzzle::get('http://desymfony.com');

// Para un poco mas de controluse Guzzle\Http\Client;

$client = new Client('http://desymfony.com');$request = $client->get('/');$response = $request->send();

Page 50: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle HTTP Client

Page 51: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle HTTP Client

Page 52: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle HTTP Client

• Clientes

Page 53: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle HTTP Client

• Clientes

• Requests

• Sin body

• Con body

Page 54: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle HTTP Client

• Clientes

• Requests

• Sin body

• Con body

• Responses

Page 55: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle HTTP Client

Client Object

Page 56: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Client Object

$client = new Client('https://api.github.com/test');

// Absoluta$request = $client->get('https://api.github.com/test');// https://api.github.com/test

// relativa (con slash)$request = $client->get('/users');// https://api.github.com/users

// relativa (sin slash)$request = $client->get('users');// https://api.github.com/test/users

// Configuración de la petición$client->setUserAgent('Test/123');

Page 57: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle HTTP Client

Request Object

Page 58: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Request Object

// Crear un método en especifico$client->createRequest('METODO', $uri, array $headers, $body, $options);

// GET$client->get($uri, array $headers, $options);

//HEAD$client->head($uri, array $headers, $options);

//DELETE$client->delete($uri, array $headers, $body, $options);

//POST$client->post($uri, array $headers, $postBody, $options);

//PUT$client->put($uri, array $headers, $body, $options);

//PATCH$client->patch($uri, array $headers, $body, $options);

Page 59: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Request Object

// Una petición simple:

$request = $client->head('/path?id=123&nombre=abc');$response = $request->send();

// Una petición con cuerpo adjunto$request = $client->post('http://test.com/upload') ->addPostFiles(array('file' => '/ruta/a/documento.json'));$response = $request->send();

// Content-Type: application/json

Page 60: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Request Object

$request = $client->createRequest('COPY', 'http://example.com/foo', array( 'Destination' => 'http://test.com/bar', 'Overwrite' => 'T'));$response = $request->send();

Page 61: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle HTTP Client

Response Object

Page 62: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Response Object

$response = $client->get('http://www.amazon.com')->send();

echo $response->getStatusCode();// 200

echo $response->isSuccessful();// true

$response->isRedirect();// false

Page 63: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Response Object

// Respuesta de la peticiónecho $response->getBody();

// Si el formato es correcto:

// Devuelve un array de la respuesta json de la peticiónecho $response->json();

// Devuelve un objeto SimpleXMLElement de la peticiónecho $response->xml();

Page 64: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Plugins

Page 65: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Response Object

// Envía una petición si, y otra no con el plugin:// UnaSiOtraNoPlugin

$unaSiOtraNo = new UnaSiOtraNoPlugin();$client->addSubscriber($unaSiOtraNo);

Page 66: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Plugins

• Async plugin

• Backoff retry plugin

• HTTP Cache plugin

• OAuth plugin

• Cookie plugin

• History plugin

• Log plugin

• MD5 validator plugin

• Mock plugin

Page 67: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Plugins

Async plugin

Page 68: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Plugins

Backoff retry plugin

Page 69: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Plugins

HTTP Cache Plugin

Page 70: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

HTTP Cache Plugin

Page 71: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

HTTP Cache Plugin

Page 72: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

HTTP Cache Plugin

• Chequea el HEAD del Request, para verificar el status.

Page 73: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

HTTP Cache Plugin

• Chequea el HEAD del Request, para verificar el status.

Page 74: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

HTTP Cache Plugin

• Chequea el HEAD del Request, para verificar el status.

• Se puede crear una cache falsa

Page 75: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Plugins

OAuth Plugin

Page 76: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

OAuth Plugin

Page 77: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

OAuth Plugin

Page 78: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

OAuth Plugin

• OAuth 1.0 (Twiiter)

Page 79: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

OAuth Plugin

• OAuth 1.0 (Twiiter)

• OAuth 2.0 (Facebook)

Page 80: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

OAuth Plugin

• OAuth 1.0 (Twiiter)

• OAuth 2.0 (Facebook)

• OAuth2 en los planes pero sin fecha: ( https://github.com/guzzle/guzzle/issues/319 )

Page 81: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

OAuth Plugin

• OAuth 1.0 (Twiiter)

• OAuth 2.0 (Facebook)

• OAuth2 en los planes pero sin fecha: ( https://github.com/guzzle/guzzle/issues/319 )

Page 82: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

OAuth Plugin

• OAuth 1.0 (Twiiter)

• OAuth 2.0 (Facebook)

• OAuth2 en los planes pero sin fecha: ( https://github.com/guzzle/guzzle/issues/319 )

Page 83: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

// Cargando el plugin OAuth

$client = new Client('http://api.twitter.com/1.1');$oauth = new OauthPlugin(array( 'consumer_key' => 'mi_key', 'consumer_secret' => 'mi_string_secreto', 'token' => 'mi_token', 'token_secret' => 'mi_token_secreto'));$client->addSubscriber($oauth);

$request = $client->get('statuses/public_timeline.json');

$response = $request->send();

Page 84: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Service Builder

Page 85: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Service Builder

// Create cliente de twitter$client = TwitterClient::factory(array( 'consumer_key' => '****', 'consumer_secret' => '****', 'token' => '****', 'token_secret' => '****'));

Page 86: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Service Builder

// Crear un cliente desde un factory

$builder = ServiceBuilder::factory('/ruta/a/config.json');$twitter = $builder->get('twitter');

Page 87: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Service Builder

{ "services": { "twitter_ajessu": { "class": "ajessu\TwitterClient", "params": { "consumer_key": "****", "consumer_secret": "****", "token": "****", "token_secret": "****" } }, "twitter_desymfony": { "class": "ajessu\TwitterClient", "params": { "consumer_key": "****", "consumer_secret": "****", "token": "****", "token_secret": "****" } } }}

$twitter_ajessu = $builder->get('twitter_ajessu');$twitter_desymfony = $builder->get('twitter_desymfony');

Page 88: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Service Definition

Page 89: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Service Builder

{ "name": "string", "apiVersion": "string|number", "baseUrl": "string", "description": "string", "operations": {}, "models": {}, "includes": ["string.php", "string.json"]}

Page 90: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Service Builder

{ "includes":[ "connection.json" ], "name": "IndividualMember", "apiVersion": "0.1", "description": "Individual Member Api for Emailvision", "baseUrl": "{schema}://{server}/apimember/services/rest", "operations": { "InsertOrUpdateMember": { "httpMethod": "POST", "uri": "member/insertOrUpdateMember/{token}", "summary": "Updates a user if it exists, otherwise it updates it", "responseClass": "InsertOrUpdateMember", "parameters": { "token": { "location": "uri", "description": "Connection token", "required": true }, "user": { "location": "body", "description": "User values in xml", "required": true }, "Content-Type": { "location": "header", "default": "application/xml" } } } }}

Page 91: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Service Builder

{ "name": "Connection", "apiVersion": "0.1", "description": "Connection Api for Emailvision", "operations": { "OpenConnection": { "httpMethod": "GET", "uri": "connect/open/{apiLogin}/{apiPassword}/{apiKey}", "summary": "Opens the connection", "responseClass": "OpenConnection", "parameters": { "apiLogin": { "location": "uri", "description": "API username", "required": true }, "apiPassword": { "location": "uri", "description": "API password", "required": true }, "apiKey": { "location": "uri", "description": "API secret key", "required": true } } }, ... }, "models": { "OpenConnection": { "type": "object", "properties": { "token": { "location": "xml", "type": "string", "sentAs": "result" }, "status": { "type": "int", "location": "statusCode" } } }, ... }}

Page 92: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Service Builder

$memberClient = DataIndividualClient::factory();

// Get the token $openConnectionCommand = $memberClient->getCommand( 'OpenConnection', array( 'apiLogin' => '*****', 'apiPassword' => '****', 'apiKey' => ‘*********’ ) );

$openConnectionResponse = $memberClient->execute($openConnectionCommand);

$token = $openConnectionResponse['token']; $body = EntityBody::factory(‘/foo/user.xml’);

$updateUserCommand = $memberClient->getCommand( 'InsertOrUpdateMember', array( 'token' => $token, 'user' => $body ) );

$memberClient->execute($updateUserCommand);

// Close connection $closeConnectionCommand = $memberClient->getCommand( 'CloseConnection', array('token' => $token) ); $memberClient->execute($closeConnectionCommand);

Page 93: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en silex

Page 94: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en silex

$app = new Application();

$app->register(new GuzzleServiceProvider(), array( 'guzzle.services' => '/ruta/a/servicios.json',));

Page 95: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en Symfony2

Page 96: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en Symfony2

Page 97: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en Symfony2

• Varios bundles

Page 98: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en Symfony2

• Varios bundles

• Mejor opción hoy: MisdGuzzleBundle( https://github.com/misd-service-development/guzzle-bundle )

Page 99: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en Symfony2

• Varios bundles

• Mejor opción hoy: MisdGuzzleBundle( https://github.com/misd-service-development/guzzle-bundle )

• Discusion (hace tres dias) = Vamos a unirlos!( https://github.com/misd-service-development/guzzle-bundle/issues/28 )k

Page 100: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en Symfony2

// app/AppKernel.php

class AppKernel extends Kernel{ // ... public function registerBundles() { $bundles = array( // ... new Misd\GuzzleBundle\MisdGuzzleBundle() ); }}

Page 101: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en Symfony2

// MyBundle/Resources/config/services.xml

<service id="example.client" class="%guzzle.client.class%"> <tag name="guzzle.client"/> <argument>http://api.example.com/</argument> <argument type="collection"> <argument key="setting1">true</argument> <argument key="setting2">false</argument> </argument> <call method="addSubscriber"> <argument type="service" id="some_guzzle_plugin"/> </call> <call method="setUserAgent"> <argument>My Guzzle client</argument> <argument>true</argument> </call> <call method="setDescription"> <argument type="service" id="example.client.service_description"/> </call></service>

Page 102: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Guzzle en Symfony2

$client = $this->get('example.client');

// Service description:<service id="example.client.service_description" class="%guzzle.service_description.class%" factory-class="%guzzle.service_description.class%" factory-method="factory"> <argument>%path.to.my.service_description.file%</argument></service>

Page 103: deSymfony 2013 Consumiendo APIs REST con Guzzle y Symfony2

Gracias!

Feedback: https://joind.in/8840

Albert [email protected]/ajessu