Amazon SDK for PHP 版本 3 中的处理程序和中间件 - Amazon SDK for PHP
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

Amazon SDK for PHP 版本 3 中的处理程序和中间件

Amazon SDK for PHP的主要扩展机制是,通过处理程序中间件进行扩展。每个开发工具包客户端类都拥有一个 Aws\HandlerList 实例,可通过客户端的 getHandlerList() 方法访问。您可以检索客户端的 HandlerList,并对其进行修改来添加或删除客户端行为。

处理程序

处理程序是一个将命令和请求实际转换为结果的函数。处理程序通常发送 HTTP 请求。处理程序可由中间件组成,以增强行为。处理程序是一个函数,它接受 Aws\CommandInterfacePsr\Http\Message\RequestInterface,并返回用 Aws\ResultInterface 执行或因 Aws\Exception\AwsException 原因而被拒绝的 Promise。

以下处理程序的每次调用均返回相同的模拟结果。

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); };

您可以在客户端的构造函数中提供 handler 选项,将此处理程序与开发工具包客户端结合使用。

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

您还可以在客户端构造完成后使用 setHandlerAws\ClientInterface 方法更改其处理程序。

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

要在多区域客户端构造完成后更改其处理程序,请使用 Aws\MultiRegionClientuseCustomHandler 方法。

$multiRegionClient->useCustomHandler($myHandler);

模拟处理程序

我们建议在使用开发工具包编写测试时使用 MockHandler。您可以使用 Aws\MockHandler 返回模拟结果或引发模拟异常。加入队列的结果或异常,MockHandler 会按 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();

中间件

中间件是一类特殊的高级函数,可对传输命令的行为进行增强,并委托给“下一个”处理程序。中间件函数接受 Aws\CommandInterfacePsr\Http\Message\RequestInterface,并返回用 Aws\ResultInterface 执行或因 Aws\Exception\AwsException 原因而被拒绝的 Promise。

中间件是更高阶的函数,可修改经过中间件传递的命令、请求或结果。中间件具有以下形式。

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; }; }; };

中间件接收要执行的命令和可选的请求对象。中间件可增强请求和命令,或将它们保留原样。然后中间件会调用链条中的下一个处理程序,或选择将下一个处理程序短路并返回 Promise。调用下一个处理程序创建的 Promise,可使用 Promise 的 then 方法进行增强,在将 Promise 返回中间件组之前,修改最终结果或错误。

HandlerList

开发工具包使用 Aws\HandlerList 管理执行命令所用的中间件和处理程序。每个开发工具包客户端都拥有一个 HandlerList,这个 HandlerList 会克隆并添加到客户端创建的每条命令。您可以将中间件添加到客户端的 HandlerList,为客户端创建的每条命令附加要使用的中间件和默认处理程序。您可以修改特定命令的 HandlerList,添加或删除特定命令的中间件。

HandlerList 表示用于包装处理程序的一组中间件。HandlerList 可将中间件组拆分为一些具名步骤,表示传输命令的生命周期中的各个部分。这样有助于管理中间件的列表,以及包装处理程序的顺序。

  1. init - 添加默认参数

  2. validate - 验证必要参数

  3. build - 序列化 HTTP 请求,准备发送

  4. sign - 对序列化 HTTP 请求进行签名

  5. <handler>(不是一个步骤,但执行实际传输过程)

init

生命周期中的这个步骤表示命令的初始化,以及尚未序列化的请求。此步骤通常用于在命令中添加默认参数。

您可以使用 initappendInit 方法在 prependInit 步骤中添加中间件,其中 appendInit 将中间件添加到 prepend 列表的最后,prependInit 将中间件添加到 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');
验证

生命周期中的这个步骤用于验证命令的输入参数。

您可以使用 validateappendValidate 方法在 prependValidate 步骤中添加中间件,其中 appendValidate 将中间件添加到 validate 列表的最后,prependValidate 将中间件添加到 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

生命周期中的这个步骤用于针对所执行的命令序列化 HTTP 请求。下游生命周期事件会收到一条命令和 PSR-7 HTTP 请求。

您可以使用 buildappendBuild 方法在 prependBuild 步骤中添加中间件,其中 appendBuild 将中间件添加到 build 列表的最后,prependBuild 将中间件添加到 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');
签名

生命周期中的这个步骤用于在通过线路发送 HTTP 请求之间进行签名。通常,在 HTTP 请求经过签名后,您即不应该进行更改,以避免签名错误。

这是处理程序传输 HTTP 请求之前 HandlerList 中的最后一步。

您可以使用 signappendSign 方法在 prependSign 步骤中添加中间件,其中 appendSign 将中间件添加到 sign 列表的最后,prependSign 将中间件添加到 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');

可用中间件

您可使用开发工具包提供的若干中间件增强客户端的行为,或观察命令的执行。

mapCommand

如果要在将命令序列化为 HTTP 请求之前修改命令,Aws\Middleware::mapCommand 中间件很有用。例如,可使用 mapCommand 执行验证或添加默认参数。mapCommand 函数接受的可调用函数可接受 Aws\CommandInterface 对象并返回 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'] = 'mybucket'; // Be sure to return the command! return $command; }), 'add-param' );

mapRequest

如果您需要修改已序列化但尚未发送的请求,Aws\Middleware::mapRequest 中间件很有用。例如,可使用它为请求添加自定义 HTTP 标题。mapRequest 函数接受的可调用函数可接受 Psr\Http\Message\RequestInterface 参数并返回 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' => 'mybucket' ]); // 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' );

当执行命令时,会随自定义标题发送。

重要

请注意,中间件是在 build 步骤的最后追加到处理程序列表中的。这是为了确保在调用中间件之前生成请求。

mapResult

如果您需要修改命令执行的结果,Aws\Middleware::mapResult 中间件很有用。mapResult 函数接受的可调用函数可接受 Aws\ResultInterface 参数并返回 Aws\ResultInterface 对象。

use Aws\Middleware; use Aws\ResultInterface; $command = $s3Client->getCommand('HeadObject', [ 'Key' => 'test', 'Bucket' => 'mybucket' ]); $command->getHandlerList()->appendSign( Middleware::mapResult(function (ResultInterface $result) { // Add a custom value to the result $result['foo'] = 'bar'; return $result; }) );

命令执行后,返回的结果中将包含 foo 属性。

历史记录

history 中间件可用于测试开发工具包是否执行了您期望的命令、发送了您期望的 HTTP 请求,并收到了您期望的结果。这个中间件与 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));

在清除条目之前,Aws\History 历史记录容器中默认可存储 10 条记录,您可以将要保留的条目数量传递到构造函数中,从而自定义条目数量。

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

您可以在执行传递历史记录中间件的请求之后,检查历史记录容器。

// 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

tap 中间件可用作观察工具。您可以使用此中间件,在通过中间件链条发送命令时调用函数。tap 函数接受的可调用函数可接受 Aws\CommandInterface,以及被执行的可选 Psr\Http\Message\RequestInterface

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"; } } );

创建自定义处理程序

处理程序只是一个接受 Aws\CommandInterface 对象和 Psr\Http\Message\RequestInterface 对象的函数,它可返回由 GuzzleHttp\Promise\PromiseInterface 满足、或由 Aws\ResultInterface 拒绝的 Aws\Exception\AwsException

虽然开发工具包有多个 @http 选项,处理程序只需要知道如何使用以下选项:

除非此选项指定为可选,否则处理程序必须处理该选项,或必须返回拒绝的 Promise。

除了处理特定的 @http 选项,处理程序还必须添加具有以下形式的 User-Agent 标题,其中“3.X”可替换为 Aws\Sdk::VERSION,“HandlerSpecificData/version ...”应替换为您的处理程序特定的 User-Agent 字符串。

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