

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Controladores y middleware en la versión 3 de AWS SDK para PHP
<a name="guide_handlers-and-middleware"></a>

El mecanismo principal para ampliar AWS SDK para PHP es mediante **controladores** y **middleware**. Cada clase del cliente del SDK posee una instancia `Aws\HandlerList` a la que se puede obtener acceso utilizando el método `getHandlerList()` de un cliente. Puede recuperar la `HandlerList` de un cliente y modificarla para añadir o eliminar el comportamiento del cliente.

## Controladores
<a name="handlers"></a>

Un controlador es una función que realiza la transformación real de un comando y una solicitud en un resultado. Un controlador suele enviar solicitudes HTTP. Los controladores pueden incluir middleware para aumentar su comportamiento. Un controlador es una función que acepta una `Aws\CommandInterface` y una `Psr\Http\Message\RequestInterface` y devuelve una promesa que se cumple con una `Aws\ResultInterface` o se rechaza con un motivo `Aws\Exception\AwsException`.

A continuación se presenta un controlador que devuelve el mismo resultado simulado para cada llamada.

```
use Aws\CommandInterface;
use Aws\Result;
use Psr\Http\Message\RequestInterface;
use GuzzleHttp\Promise;

$myHandler = function (CommandInterface $cmd, RequestInterface $request) {
    $result = new Result(['foo' => 'bar']);
    return Promise\promise_for($result);
};
```

A continuación, puede utilizar este controlador con un cliente del SDK proporcionando una opción `handler` en el constructor de un cliente.

```
// Set the handler of the client in the constructor
$s3 = new Aws\S3\S3Client([
    'region'  => 'us-east-1',
    'version' => '2006-03-01',
    'handler' => $myHandler
]);
```

También puede cambiar el controlador de un cliente después de crearlo utilizando el método `setHandler` de una `Aws\ClientInterface`.

```
// Set the handler of the client after it is constructed
$s3->getHandlerList()->setHandler($myHandler);
```

**nota**  
Para cambiar el controlador de un cliente después de crearlo utilizando el método `useCustomHandler` de una `Aws\MultiRegionClient`.  

```
$multiRegionClient->useCustomHandler($myHandler);
```

### Controlador simulado
<a name="mock-handler"></a>

Le recomendamos que utilice el `MockHandler` para escribir pruebas que utilicen el SDK. Puede utilizar el parámetro `Aws\MockHandler` para devolver los resultados simulados o lanzar excepciones simuladas. El usuario debe poner los resultados o las excepciones a la cola, y MockHandler los saca de la cola siguiendo el orden FIFO.

```
use Aws\Result;
use Aws\MockHandler;
use Aws\DynamoDb\DynamoDbClient;
use Aws\CommandInterface;
use Psr\Http\Message\RequestInterface;
use Aws\Exception\AwsException;

$mock = new MockHandler();

// Return a mocked result
$mock->append(new Result(['foo' => 'bar']));

// You can provide a function to invoke; here we throw a mock exception
$mock->append(function (CommandInterface $cmd, RequestInterface $req) {
    return new AwsException('Mock exception', $cmd);
});

// Create a client with the mock handler
$client = new DynamoDbClient([
    'region'  => 'us-west-2',
    'version' => 'latest',
    'handler' => $mock
]);

// Result object response will contain ['foo' => 'bar']
$result = $client->listTables();

// This will throw the exception that was enqueued
$client->listTables();
```

## Middleware
<a name="middleware"></a>

El middleware es un tipo especial de función de alto nivel que aumenta el comportamiento de la transferencia de un comando y lo delega al "siguiente" controlador. Las funciones de middleware aceptan una `Aws\CommandInterface` y una `Psr\Http\Message\RequestInterface` y devuelven una promesa que se cumple con una `Aws\ResultInterface` o se rechaza con un motivo `Aws\Exception\AwsException`.

Un middleware es una función de orden superior que modifica un comando, solicitud o resultado al pasar por el middleware. Un middleware tiene el siguiente aspecto.

```
use Aws\CommandInterface;
use Psr\Http\Message\RequestInterface;

$middleware = function () {
    return function (callable $handler) use ($fn) {
        return function (
            CommandInterface $command,
            RequestInterface $request = null
        ) use ($handler, $fn) {
            // Do something before calling the next handler
            // ...
            $promise = $fn($command, $request);
            // Do something in the promise after calling the next handler
            // ...
            return $promise;
        };
    };
};
```

Un middleware recibe un comando a ejecutar y un objeto Request opcional. El middleware puede elegir aumentar la solicitud y el comando o dejarlas tal y como están. A continuación, un middleware invoca al siguiente controlador de la cadena o puede optar por cortocircuitar el siguiente controlador y devolver una promesa. La promesa que se crea invocando el siguiente controlador también se puede aumentar utilizando el método `then` de la promesa para modificar el posible resultado o error antes de devolver la promesa a la pila de middleware.

### HandlerList
<a name="handlerlist"></a>

El SDK utiliza una `Aws\HandlerList` para administrar el middleware y los controladores utilizados al ejecutar un comando. Cada cliente del SDK posee una `HandlerList` y dicha `HandlerList` se clona y se agrega a cada comando que crea un cliente. Puede asociar un middleware y el controlador predeterminado que se va a utilizar para cada comando creado por un cliente añadiendo un middleware a la del cliente `HandlerList`. Puede añadir y eliminar middleware de comandos específicos modificando la `HandlerList` propiedad de un comando específico.

Una `HandlerList` representa una pila de middleware que se utiliza para encapsular un **controlador**. Para ayudar a administrar la lista de middleware y el orden en que encapsulan un controlador, la `HandlerList` divide la pila en pasos denominados que representan partes del ciclo de vida de la transferencia de un comando:

1.  `init`: añade parámetros predeterminados

1.  `validate`: valida los parámetros obligatorios

1.  `build`: serializa una solicitud HTTP para enviarla

1.  `sign`: firma la solicitud HTTP serializada

1. <controlador> (no es un paso pero ejecuta la transferencia real)

**init**  
Este paso del ciclo de vida representa la inicialización de un comando. Aún no se ha serializado ninguna solicitud. Este paso se suele utilizar para añadir los parámetros predeterminados a un comando.  
Puede añadir un middleware al paso `init` con los métodos `appendInit` y `prependInit`, donde `appendInit` añade el middleware al final de la lista `prepend` mientras que `prependInit` añade el middleware al principio de la lista `prepend`.  

```
use Aws\Middleware;

$middleware = Middleware::tap(function ($cmd, $req) {
    // Observe the step
});

// Append to the end of the step with a custom name
$client->getHandlerList()->appendInit($middleware, 'custom-name');
// Prepend to the beginning of the step
$client->getHandlerList()->prependInit($middleware, 'custom-name');
```

**validar**  
Este paso del ciclo de vida se utiliza para validar los parámetros de entrada de un comando.  
Puede añadir un middleware al paso `validate` con los métodos `appendValidate` y `prependValidate`, donde `appendValidate` añade el middleware al final de la lista `validate` mientras que `prependValidate` añade el middleware al principio de la lista `validate`.  

```
use Aws\Middleware;

$middleware = Middleware::tap(function ($cmd, $req) {
    // Observe the step
});

// Append to the end of the step with a custom name
$client->getHandlerList()->appendValidate($middleware, 'custom-name');
// Prepend to the beginning of the step
$client->getHandlerList()->prependValidate($middleware, 'custom-name');
```

**build**  
Este paso del ciclo de vida se utiliza para serializar una solicitud HTTP para el comando que se está ejecutando. Los eventos del ciclo de vida posteriores recibirán un comando y una solicitud HTTP PSR-7.  
Puede añadir un middleware al paso `build` con los métodos `appendBuild` y `prependBuild`, donde `appendBuild` añade el middleware al final de la lista `build` mientras que `prependBuild` añade el middleware al principio de la lista `build`.  

```
use Aws\Middleware;

$middleware = Middleware::tap(function ($cmd, $req) {
    // Observe the step
});

// Append to the end of the step with a custom name
$client->getHandlerList()->appendBuild($middleware, 'custom-name');
// Prepend to the beginning of the step
$client->getHandlerList()->prependBuild($middleware, 'custom-name');
```

**sign**  
Este paso del ciclo de vida se suele utilizar para firmar solicitudes HTTP antes de que se envíen a través de la red. Debe abstenerse de realizar cambios en una solicitud HTTP cuando ya esté firmada para evitar errores de firma.  
Este es el último paso de la `HandlerList` antes de que un controlador transfiera la solicitud HTTP.  
Puede añadir un middleware al paso `sign` con los métodos `appendSign` y `prependSign`, donde `appendSign` añade el middleware al final de la lista `sign` mientras que `prependSign` añade el middleware al principio de la lista `sign`.  

```
use Aws\Middleware;

$middleware = Middleware::tap(function ($cmd, $req) {
    // Observe the step
});

// Append to the end of the step with a custom name
$client->getHandlerList()->appendSign($middleware, 'custom-name');
// Prepend to the beginning of the step
$client->getHandlerList()->prependSign($middleware, 'custom-name');
```

### Middleware disponible
<a name="available-middleware"></a>

El SDK proporciona varios middleware que puede utilizar para aumentar el comportamiento de un cliente o para observar la ejecución de un comando.

#### mapCommand
<a name="map-command"></a>

El middleware `Aws\Middleware::mapCommand` resulta útil cuando hay que modificar un comando antes de que se serialice como solicitud HTTP. Por ejemplo, `mapCommand` se puede utilizar para llevar a cabo una validación o para añadir parámetros predeterminados. La función `mapCommand` acepta una función invocable que admite un objeto `Aws\CommandInterface` y devuelve un objeto `Aws\CommandInterface`.

```
use Aws\Middleware;
use Aws\CommandInterface;

// Here we've omitted the require Bucket parameter. We'll add it in the
// custom middleware.
$command = $s3Client->getCommand('HeadObject', ['Key' => 'test']);

// Apply a custom middleware named "add-param" to the "init" lifecycle step
$command->getHandlerList()->appendInit(
    Middleware::mapCommand(function (CommandInterface $command) {
        $command['Bucket'] = 'amzn-s3-demo-bucket';
        // Be sure to return the command!
        return $command;
    }),
    'add-param'
);
```

#### mapRequest
<a name="map-request"></a>

El middleware `Aws\Middleware::mapRequest` resulta útil cuando hay que modificar una solicitud después de serializarla pero antes de enviarla. Por ejemplo, se puede utilizar para añadir encabezados HTTP personalizados a una solicitud. La función `mapRequest` acepta una función invocable que admite un argumento `Psr\Http\Message\RequestInterface` y devuelve un objeto `Psr\Http\Message\RequestInterface`.

```
use Aws\Middleware;
use Psr\Http\Message\RequestInterface;

// Create a command so that we can access the handler list
$command = $s3Client->getCommand('HeadObject', [
    'Key'    => 'test',
    'Bucket' => 'amzn-s3-demo-bucket'
]);

// Apply a custom middleware named "add-header" to the "build" lifecycle step
$command->getHandlerList()->appendBuild(
    Middleware::mapRequest(function (RequestInterface $request) {
        // Return a new request with the added header
        return $request->withHeader('X-Foo-Baz', 'Bar');
    }),
    'add-header'
);
```

Ahora al ejecutar el comando, se envía con el encabezado personalizado.

**importante**  
Tenga en cuenta que el middleware se adjuntó a la lista de controladores al final del paso `build`. De este modo, se garantiza que se cree una solicitud antes de invocar este middleware.

#### mapResult
<a name="mapresult"></a>

El middleware `Aws\Middleware::mapResult` resulta útil para modificar el resultado de una ejecución del comando. La función `mapResult` acepta una función invocable que admite un argumento `Aws\ResultInterface` y devuelve un objeto `Aws\ResultInterface`.

```
use Aws\Middleware;
use Aws\ResultInterface;

$command = $s3Client->getCommand('HeadObject', [
    'Key'    => 'test',
    'Bucket' => 'amzn-s3-demo-bucket'
]);

$command->getHandlerList()->appendSign(
    Middleware::mapResult(function (ResultInterface $result) {
        // Add a custom value to the result
        $result['foo'] = 'bar';
        return $result;
    })
);
```

Ahora cuando se ejecute el comando, el resultado incluirá un atributo `foo`.

#### historial
<a name="history"></a>

El middleware `history` es útil para probar que el SDK ejecuta los comandos previstos, envía las solicitudes HTTP esperadas y recibe los resultados previstos. Se trata básicamente de un middleware que actúa de forma similar al historial de un navegador web.

```
use Aws\History;
use Aws\Middleware;

$ddb = new Aws\DynamoDb\DynamoDbClient([
    'version' => 'latest',
    'region'  => 'us-west-2'
]);

// Create a history container to store the history data
$history = new History();

// Add the history middleware that uses the history container
$ddb->getHandlerList()->appendSign(Middleware::history($history));
```

Un contenedor de historiales `Aws\History` almacena 10 entradas de forma predeterminada antes de purgar las entradas. Puede personalizar el número de entradas transfiriendo el número de entradas que desea conservar en el constructor.

```
// Create a history container that stores 20 entries
$history = new History(20);
```

Puede examinar el contenedor de historiales después de ejecutar las solicitudes que transfieren el middleware del historial.

```
// The object is countable, returning the number of entries in the container
count($history);

// The object is iterable, yielding each entry in the container
foreach ($history as $entry) {
    // You can access the command that was executed
    var_dump($entry['command']);
    // The request that was serialized and sent
    var_dump($entry['request']);
    // The result that was received (if successful)
    var_dump($entry['result']);
    // The exception that was received (if a failure occurred)
    var_dump($entry['exception']);
}

// You can get the last Aws\CommandInterface that was executed. This method
// will throw an exception if no commands have been executed.
$command = $history->getLastCommand();

// You can get the last request that was serialized. This method will throw an exception
// if no requests have been serialized.
$request = $history->getLastRequest();

// You can get the last return value (an Aws\ResultInterface or Exception).
// The method will throw an exception if no value has been returned for the last
// executed operation (e.g., an async request has not completed).
$result = $history->getLastReturn();

// You can clear out the entries using clear
$history->clear();
```

#### tap
<a name="tap"></a>

El middleware `tap` se utiliza como observador. Puede utilizar este middleware para invocar a funciones al enviar los comandos a través de la cadena de middleware. La función `tap` acepta una función invocable que admite la `Aws\CommandInterface` y una `Psr\Http\Message\RequestInterface` opcional que se está ejecutando.

```
use Aws\Middleware;

$s3 = new Aws\S3\S3Client([
    'region'  => 'us-east-1',
    'version' => '2006-03-01'
]);

$handlerList = $s3->getHandlerList();

// Create a tap middleware that observes the command at a specific step
$handlerList->appendInit(
    Middleware::tap(function (CommandInterface $cmd, RequestInterface $req = null) {
        echo 'About to send: ' . $cmd->getName() . "\n";
        if ($req) {
            echo 'HTTP method: ' . $request->getMethod() . "\n";
        }
    }
);
```

## Crear controladores personalizados
<a name="creating-custom-handlers"></a>

Un controlador es simplemente una función que acepta un objeto `Aws\CommandInterface` y un objeto `Psr\Http\Message\RequestInterface`, y que devuelve una `GuzzleHttp\Promise\PromiseInterface` que se cumple con una `Aws\ResultInterface` o se rechaza con una `Aws\Exception\AwsException`.

Aunque el SDK tiene varias opciones `@http`, un controlador solo necesita saber cómo utilizar las siguientes opciones:
+  [connect\_timeout](guide_configuration.md#http-connect-timeout) 
+  [depuración](guide_configuration.md#http-debug) 
+  [decode\_content](guide_configuration.md#http-decode-content) (opcional)
+  [delay](guide_configuration.md#http-delay) 
+  [progress](guide_configuration.md#http-progress) (opcional)
+  [Proxy de](guide_configuration.md#http-proxy) 
+  [sink](guide_configuration.md#http-sink) 
+  [synchronous](guide_configuration.md#http-sync) (opcional)
+  [stream](guide_configuration.md#http-stream) (opcional)
+  [timeout](guide_configuration.md#http-timeout) 
+  [verificar](guide_configuration.md#http-verify) 
+ http\_stats\_receiver (opcional): es una función para invocar con una matriz asociativa de estadísticas de transferencia HTTP si se solicita utilizando el parámetro de configuración [stats](guide_configuration.md#config-stats).

A menos que la opción se especifique como opcional, un controlador DEBE controlar la opción o devolver una promesa rechazada.

Además de la gestión de opciones `@http` específicas, un controlador DEBE añadir un encabezado `User-Agent` que tiene el siguiente formato, donde "3.X" se puede reemplazar por `Aws\Sdk::VERSION` y "HandlerSpecificData/version..." debe sustituirse por la cadena User-Agent específica del controlador.

 `User-Agent: aws-sdk-php/3.X HandlerSpecificData/version ...` 