dormilich / http-client
一个围绕 PSR-18 HTTP 客户端的请求/响应处理程序。
Requires
- php: >=7.4
- ext-dom: *
- ext-json: *
- ext-mbstring: *
- ext-simplexml: *
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
Requires (Dev)
- phpunit/phpunit: ^9.5
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 转换器
JsonDecoder
和 JsonEncoder
接受任何 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
中,编码或解码失败将分别抛出 EncoderException
或 DecoderException
。