dormilich/http-client

一个围绕 PSR-18 HTTP 客户端的请求/响应处理程序。

dev-main 2021-08-13 08:06 UTC

This package is auto-updated.

Last update: 2024-09-13 15:00:17 UTC


README

该库的目的是定义围绕 PSR-18 HTTP 客户端的处理程序,将数据转换为请求,并将响应转换回所需的数据结构,同时独立于实际的 HTTP 客户端实现。

安装

您可以通过 composer 安装此库

composer require dormilich/http-client

要使用此库,请安装您选择的 PSR-18 HTTP 客户端和 PSR-17 HTTP 工厂。

HTTP 客户端

HTTP 客户端需要使用 PSR-18 HTTP 客户端和 PSR-17 请求和流工厂进行设置。

use Dormilich\HttpClient\Client;

// replace this with the actual implementations
$httpClient = new HttpClient();         // PSR-18
$requestFactory = new RequestFactory(); // PSR-17
$streamFactory = new StreamFactory();   // PSR-17

$client = new Client($httpClient, $requestFactory, $streamFactory);

虽然手动设置可能看起来很繁琐,但在大多数框架中使用依赖注入会使这变得简单。

在发送请求之前,客户端将应用任何已定义的默认头信息以及任何请求修改(请参阅 请求修改)。

use Dormilich\HttpClient\Client;

$client = new Client($httpClient, $requestFactory, $streamFactory);
$client->getHeaders()->add('User-Agent', 'curl/7.64.1');
// all requests will now contain the `User-Agent` header

如果没有定义数据转换器(请参阅 数据转换器),则只能发送 PSR-7 请求。否则,客户端将报错说没有为要处理的数据定义编码器,并抛出 UnsupportedDataTypeException

use Dormilich\HttpClient\Client;

$client = new Client($httpClient, $requestFactory, $streamFactory);

$request = new Request('GET', 'https://example.com'); // PSR-7
// $result will contain the response body content
$result = $client->request($request);

客户端支持最常见的 HTTP 方法作为简写方法。这需要预先定义数据编码器。

use Dormilich\HttpClient\Client;
use Dormilich\HttpClient\Transformer\JsonEncoder;

$client = new Client($httpClient, $requestFactory, $streamFactory);
$client->addTransformer(new JsonEncoder());

$client->get('https://example.com/item');
$client->post('https://example.com/item', $data);
$client->put('https://example.com/item', $data);
$client->patch('https://example.com/item', $data);
$client->delete('https://example.com/item');

可以使用 fetch() 方法提交一次性头信息和其他请求方法。

use Dormilich\HttpClient\Client;
use Dormilich\HttpClient\Transformer\UrlEncoder;

$client = new Client($httpClient, $requestFactory, $streamFactory);
$client->addTransformer(new UrlEncoder());

$search['foo'] = 'bar';
$header['authorization'] = 'Bearer 279ca9e0-ce59-48b2-8b6d-c0a6822195a1';
$result = $client->fetch('get', 'https://example.com/item', $search, $header);

注意:对于没有请求体(GET、HEAD)的请求,数据将放入查询字符串中。由于没有查询字符串结构的正式定义,因此使用应用的数据编码器的格式。

数据转换器

数据转换器允许将任意数据转换为 PSR-7 请求和将 PSR-7 响应转换回特定的数据结构。为此,已预定义了几个数据转换器。

实现 TransformerInterface 的数据转换器可以编码请求并解码响应。

use Dormilich\HttpClient\Client;
use Dormilich\HttpClient\Transformer\DomTransformer;
use Dormilich\HttpClient\Transformer\TextTransformer;
use Dormilich\HttpClient\Transformer\XmlTransformer;

$client = new Client($httpClient, $requestFactory, $streamFactory);
# converts DOMDocument
$client->addTransformer(new DomTransformer());
# converts data that can be cast to string
$client->addTransformer(new TextTransformer());
# converts SimpleXML objects
$client->addTransformer(new XmlTransformer());

$result = $client->post('https://example.com/item', $data);

实现 DataEncoderInterface 的数据转换器可以编码请求但忽略响应。

use Dormilich\HttpClient\Client;
use Dormilich\HttpClient\Transformer\JsonEncoder;
use Dormilich\HttpClient\Transformer\UrlEncoder;

$client = new Client($httpClient, $requestFactory, $streamFactory);
# converts JsonSerializable & plain objects
$client->addTransformer(new JsonEncoder());
# converts arrays
$client->addTransformer(new UrlEncoder());

$result = $client->post('https://example.com/item', $data);

实现 DataDecoderInterface 的数据转换器可以解码响应但忽略请求。这些转换器被设置为仅解码成功的响应。

use Dormilich\HttpClient\Client;
use Dormilich\HttpClient\Transformer\JsonDecoder;
use Dormilich\HttpClient\Transformer\UrlDecoder;

$client = new Client($httpClient, $requestFactory, $streamFactory);
$client->addTransformer(new JsonDecoder(JSON_OBJECT_AS_ARRAY));
$client->addTransformer(new UrlDecoder());

$result = $client->post('https://example.com/item', $data);

如果需要解码错误响应,可以将转换器包装到 Decoder 对象中,并使用状态匹配器实例添加状态限制。

use Dormilich\HttpClient\Client;
use Dormilich\HttpClient\Decoder\Decoder;
use Dormilich\HttpClient\Transformer\JsonDecoder;
use Dormilich\HttpClient\Utility\StatusMatcher;

$decoder = new Decoder(new JsonDecoder());
$decoder->setStatusMatcher(StatusMatcher::clientError());

$client = new Client($httpClient, $requestFactory, $streamFactory);
// only decodes HTTP 4xx errors in JSON format
$client->addDecoder($decoder);

如果多个数据转换器编码相同的数据类型或处理相同的响应类型,则首先定义的那个有效。

处理 JSON 和 URL 编码数据的转换器已分成编码器和解码器,以便独立于请求编码器解码响应(例如,当您想将 JSON 响应解码为特定对象时)。

JSON 转换器

JsonDecoderJsonEncoder 接受任何 JSON_* 常量作为构造函数参数进行编码和解码。

use Dormilich\HttpClient\Transformer\JsonEncoder;

$default = new JsonEncoder();
$slashes = new JsonEncoder(JSON_UNESCAPED_SLASHES);

$data = 'text/plain';
$result = $default->encode($data);  // "text\/plain"
$result = $slashes->encode($data);  // "text/plain"
use Dormilich\HttpClient\Transformer\JsonDecoder;

$object = new JsonDecoder();
$array = new JsonDecoder(JSON_OBJECT_AS_ARRAY);

$json = '{"foo":"bar"}';
$result = $object->decode($json);   // $result->foo = 'bar';
$result = $array->decode($json);    // $result['foo'] = 'bar';

注意:请注意,JSON_NUMERIC_CHECK 将解码任何大于 PHP_INT_MAX 的整数字符串为浮点数。它还会将可能不打算转换的数字字符串(例如电话号码、邮政编码等)转换为数字。

URL 转换器

这些转换器对数据进行 URL 编码/解码。默认情况下,这使用 PHP 风格解析。还有一个可用的解析器,它严格解析键值对(即没有嵌套数组)。

use Dormilich\HttpClient\Transformer\UrlDecoder;
use Dormilich\HttpClient\Transformer\UrlEncoder;
use Dormilich\HttpClient\Utility\NvpQuery;

$php_encoder = new UrlEncoder();
$nvp_encoder = new UrlEncoder(new NvpQuery());

$data['q'][] = 'foo';
$data['q'][] = 'bar';

$query_php = $php_encoder->encode($data);   // "q%5B0%5D=foo&q%5B1%5D=bar"
$query_nvp = $nvp_encoder->encode($data);   // "q=foo&q=bar"

$php_decoder = new UrlEncoder();
$nvp_decoder = new UrlEncoder(new NvpQuery());

$result = $php_decoder->decode($query_php)  // ['q' => ['foo', 'bar']]
$result = $php_decoder->decode($query_nvp)  // ['q' => 'bar']
$result = $nvp_decoder->decode($query_nvp)  // ['q' => ['foo', 'bar']]
$result = $nvp_decoder->decode($query_php)  // ['q[0]' => 'foo', 'q[1]' => 'bar']

请求修改

尽管编码请求数据,编码器也可以用于修改请求。因此,编码器的 supports() 方法必须支持 Psr\Http\Message\RequestInterface 作为负载数据类型。

这种用法的一个例子是修改请求的编码器,例如添加头部信息。

use Dormilich\HttpClient\Client;
use Dormilich\HttpClient\Encoder\ContentLength;

$client = new Client($httpClient, $requestFactory, $streamFactory);
// this will add the `Content-Length` header, if appropriate
$client->addEncoder(new ContentLength());

解析响应

可以通过定义响应解码器或数据解码器来处理响应。数据解码器仅用于成功响应,而响应解码器可以配置为处理任何(特定)响应。

与编码器不同,客户端不需要定义解码器,如果没有匹配(或存在)的解码器,它将返回响应体内容。

use Psr\Log\LoggerInterface;
use Dormilich\HttpClient\Client;
use Dormilich\HttpClient\Decoder\ErrorDecoder;
use Dormilich\HttpClient\Exception\RequestException;
use Dormilich\HttpClient\Transformer\JsonDecoder;

try {
    $client = new Client($httpClient, $requestFactory, $streamFactory);
    // converts a failed request into an exception
    // using the request body as exception message
    $client->addDecoder(new ErrorDecoder());
    $client->addTransformer(new JsonDecoder());
    $result = $client->get('https://example.com/toc')
} catch (RequestException $e) {
    $context['request'] = $e->getRequest();
    $context['response'] = $e->getResponse();
    $logger->error($e->getMessage(), $context);
}

异常

除了上述的表示设置问题的 UnsupportedDataTypeException 异常外,客户端还可以抛出 RequestException。PSR-18 异常被封装到 RequestException 中,编码或解码失败将分别抛出 EncoderExceptionDecoderException