radiergummi/wander

适用于现代世界的PHP客户端

0.1.0 2020-09-24 13:43 UTC

This package is auto-updated.

Last update: 2024-09-24 22:10:10 UTC


README

PHP 7.4的现代、轻量级、快速且类型安全的HTTP客户端

注意:我仍在积极开发Wander。如果您想帮忙,可以,但建议您暂不使用它。

简介

在PHP中发送HTTP请求可能会很痛苦。要么您进入完全的低级模式并使用流或curl,要么您需要使用基于数组的配置、很多开销或缺少PSR兼容性的现有选择之一。Wander试图提供一个替代方案:它不会默认解决所有问题,而是保持简单和可扩展。Wander做出了一些合理的假设,但如果这些假设对于您的用例不正确,您仍然可以扩展它。

功能

  • 简单、易发现API Wander将所有请求选项都公开为可链式方法。
  • 完全符合标准 Wander依赖于PSR-17工厂、PSR-18客户端驱动程序和PSR-7请求/响应。使用我们的首选实现(nyholm/psr7)或自行提供。
  • 可插拔序列化 请求和响应体根据内容类型进行序列化,透明且自动。如果您使用我们尚未知的格式?添加自己的(并提交一个PR!)
  • 与其他解决方案兼容 由于驱动程序本质上是PSR-18客户端,因此您可以交换任何其他客户端库并使其立即工作。这提供了一条平滑的迁移路径。
  • 广泛的异常 Wander抛出了几个异常,所有这些异常都遵循清晰的继承结构。这使得处理错误变得特别容易,可以根据需要以粗粒度或细粒度进行处理。
$responseBody = (new Wander())
    ->patch('https://example.com')
    ->withQueryParameter('foo', 'bar')
    ->withBasicAuthorization('User', 'Pass')
    ->withHeaders([
        Header::ACCEPT => MediaType::APPLICATION_JSON,
    ])
    ->withoutHeader(Header::USER_AGENT)
    ->withBody([ 'test' => true ])
    ->asJson()
    ->run()
    ->getBody()
    ->getContents();

安装

使用composer安装

composer require radiergummi/wander

用法

以下部分提供了几个具体的用法示例。对于完整参考,请参阅参考部分

请求简写

Wander内置了多个简写层,使其尽可能简单。要执行简单的GET请求,以下就足够了

$client = new Wander();
$response = $client
    ->get('https://example.com')
    ->run();

Wander在Wander对象上内置了多个对常用HTTP方法(GETPUTPOSTDELETE等)的简写。

创建请求上下文

以上示例的稍长版本,使用内部也使用的createContext方法

$client = new Wander();
$response = $client
    ->createContext(Method::GET, 'https://example.com')
    ->run();

此处创建的上下文围绕PSR-7请求包装,并添加了一些辅助方法,使得可以链式调用方法。这样做需要创建请求实例,这当然依赖于您可以替换为自己的PSR-17工厂。下面将详细介绍。

直接发送PSR-7请求

Wander还支持直接处理请求实例

$client = new Wander();
$request = new Request(Method::GET, 'https://example.com');
$response = $client->request($request);

发送任意请求体

Wander接受任何类型的作为请求体的数据。它将在发送请求之前根据当时设置的Content-Type头进行序列化。这意味着您不需要关心体序列化。
但是,如果您将PSR-7 StreamInterface实例设置为体,Wander将不会尝试修改流并按原样使用它。

发送JSON请求

$client = new Wander();
$response = $client
    ->post('https://example.com')
    ->withBody([
        'anything' => true
    ])
    ->asJson()
    ->run();

发送原始流

$client = new Wander();
$response = $client
    ->post('https://example.com')
    ->withBody(Stream::create('/home/moritz/large.mp4'))
    ->run();

检索解析后的响应体

随着请求上下文围绕着请求实例,还有响应上下文围绕着PSR-7 响应,提供额外的辅助工具,例如获取响应体的解析表示,如果可能的话。

$client = new Wander();
$response = $client
    ->get('https://example.com')
    ->run()
    ->getParsedBody();

异常处理

Wander遵循一个异常层次结构,代表不同类别的错误。与PSR-18客户端相反,我坚信400或500范围内的响应状态码应该抛出异常,因为你最终还是需要检查它们。异常是朋友!尤其是在HTTP的情况下,错误可能是流程中预期的部分。

异常树如下所示

 WanderException (inherits from \RuntimeException)
  ├─ ClientException (implements PSR-18 ClientExceptionInterface)
  ├─ DriverException (implements PSR-18 RequestExceptionInterface)
  ├─ ConnectionException (implements PSR-18 NetworkExceptionInterface)
  ├─ SslCertificateException (implements PSR-18 NetworkExceptionInterface)
  ├─ UnresolvableHostException (implements PSR-18 NetworkExceptionInterface)
  └─ ResponseErrorException
      ├─ ClientErrorException
      │   ├─ BadRequestException
      │   ├─ UnauthorizedException
      │   ├─ PaymentRequiredException
      │   ├─ ForbiddenException
      │   ├─ NotFoundException
      │   ├─ MethodNotAllowedException
      │   ├─ NotAcceptableException
      │   ├─ ProxyAuthenticationRequiredException
      │   ├─ RequestTimeoutException
      │   ├─ ConflictException
      │   ├─ GoneException
      │   ├─ LengthRequiredException
      │   ├─ PreconditionFailedException
      │   ├─ PayloadTooLargeException
      │   ├─ UriTooLongException
      │   ├─ UnsupportedMediaTypeException
      │   ├─ RequestedRangeNotSatisfyableException
      │   ├─ ExpectationFailedException
      │   ├─ ImATeapotException
      │   ├─ MisdirectedRequestException
      │   ├─ UnprocessableEntityException
      │   ├─ LockedException
      │   ├─ FailedDependencyException
      │   ├─ TooEarlyException
      │   ├─ UpgradeRequiredException
      │   ├─ PreconditionRequiredException
      │   ├─ TooManyRequestsException
      │   ├─ RequestHeaderFieldsTooLargeException
      │   └─ UnavailableForLegalReasonsException
      └─ ServerErrorException
          ├─ InternalServerErrorException
          ├─ NotImplementedException
          ├─ BadGatewayException
          ├─ ServiceUnavailableException
          ├─ GatewayTimeoutException
          ├─ HTTPVersionNotSupportedException
          ├─ VariantAlsoNegotiatesException
          ├─ InsufficientStorageException
          ├─ LoopDetectedException
          ├─ NotExtendedException
          └─ NetworkAuthenticationRequiredException

所有响应错误异常都提供了请求和响应实例的getter方法,因此你可以轻松地做这样的事情

try {
    $request->run();
} catch (UnauthorizedException | ForbiddenException $e) {
    $this->refreshAccessToken();

    return $this->retry();
} catch (GoneException $e) {
    throw new RecordDeletedExeption(
        $e->getRequest()->getUri()->getPath()
    );
} catch (BadRequestException $e) {
    $responseBody = $e->getResponse()->getBody()->getContents();
    $error = json_decode($responseBody, JSON_THROW_ON_ERROR);
    $field = $error['field'] ?? null;
    
    if ($field) {
        throw new ValidatorException("Failed to validate {$field}");
    }
    
    throw new UnknownException($error);
} catch (WanderException $e) {

    // Simply catch all others
    throw new RuntimeException(
        'Server returned an unknown error: ' .
        $e->getResponse()->getBody()->getContents()
    );
}

这只是处理这些异常类型错误的一种方法!

设置请求超时

请求超时可以在您的驱动实例上配置

$driver = new StreamDriver();
$driver->setTimeout(3000); // 3000ms / 3s

$client = new Wander($driver);

注意
请求超时是驱动程序的可选功能,由SupportsTimeoutsInterface表示。但是,所有默认驱动程序都实现了此接口,所以你只需要检查如果你使用其他实现。

禁用跟随重定向

默认情况下,驱动程序将跟随重定向。如果您想禁用此行为,请配置您的驱动实例

$driver = new StreamDriver();
$driver->followRedirects(false);

$client = new Wander($driver);

注意
重定向是驱动程序的可选功能,由SupportsRedirectsInterface表示。但是,所有默认驱动程序都实现了此接口,所以你只需要检查如果你使用其他实现。

限制最大重定向次数

默认情况下,驱动程序将无限期地跟随重定向。如果您想限制最大重定向次数,请在您的驱动实例上配置

$driver = new StreamDriver();
$driver->setMaximumRedirects(3);

$client = new Wander($driver);

注意
重定向是驱动程序的可选功能,由SupportsRedirectsInterface表示。但是,所有默认驱动程序都实现了此接口,所以你只需要检查如果你使用其他实现。

添加体序列化器

Wander通过通过序列化器类传递数据支持请求和响应的透明体序列化。开箱即用,Wander提供了纯文本、JSON、XML、表单数据和多部分体的序列化器。序列化器遵循一个定义良好的接口,因此您可以轻松地为任何数据格式添加自己的序列化器

$client = new Wander();
$client->addSerializer('your/media-type', new CustomSerializer());

序列化器将为任何具有此媒体类型设置为Content-Type头部的请求和响应调用。

使用自定义驱动程序

驱动程序实际处理请求的调度和响应的处理。它们只有一个简单职责:将请求实例转换为响应实例。默认情况下,Wander使用一个包装流的驱动程序,但它也提供了一个curl驱动程序。如果您需要其他内容或需要默认驱动程序的变体,您可以创建一个新的实现DriverInterface的驱动程序,或者扩展一个默认驱动程序。

$driver = new class implements DriverInterface {
    public function sendRequest(RequestInterface $request): ResponseInterface
    {
      // TODO: Implement sendRequest() method.
    }
    
    public function setResponseFactory(ResponseFactoryInterface $responseFactory): void
    {
      // TODO: Implement setResponseFactory() method.
    }
};

$client = new Wander($driver);

参考

本参考展示了所有可用方法。

Wander: HTTP 客户端

本节描述了HTTP客户端的所有方法。在创建新实例时,您可以传递多个依赖项

new Wander(
    DriverInterface $driver = null,
    ?RequestFactoryInterface $requestFactory = null,
    ?ResponseFactoryInterface $responseFactory = null
)

get:创建上下文简写

GET请求创建新的请求上下文。

get(UriInterface|string $uri): Context

post:创建上下文简写

POST请求创建新的请求上下文。

post(UriInterface|string $uri, ?mixed $body = null): Context

put:创建上下文简写

PUT请求创建新的请求上下文。

put(UriInterface|string $uri, ?mixed $body = null): Context

patch:创建上下文简写

PATCH请求创建新的请求上下文。

patch(UriInterface|string $uri, ?mixed $body = null): Context

delete:创建上下文简写

DELETE请求创建新的请求上下文。

delete(UriInterface|string $uri, ?mixed $body = null): Context

head:创建上下文简写

HEAD请求创建一个新的请求上下文。

head(UriInterface|string $uri, ?mixed $body = null): Context

options:创建上下文简写

OPTIONS请求创建一个新的请求上下文。

options(UriInterface|string $uri, ?mixed $body = null): Context

createContext

允许创建任意请求方法的新的请求上下文。

createContext(string $method, UriInterface|string $uri): Context

createContextFromRequest

允许从现有的请求实例创建新的请求上下文。

createContextFromRequest(RequestInterface $request): Context

request

在客户端实例的驱动上调度请求实例,并返回响应。

request(RequestInterface $request): ResponseInterface

上下文:请求上下文

上下文对象对底层请求实例进行转换。遵循PSR-7的精神,请求当然是不可变的。上下文将仅保留对当前实例的引用。这使得我们可以链接所有方法调用和调度请求,而不离开“链”一次。我们还可以添加辅助方法并保留对其他对象的引用——例如客户端本身——使其非常易于使用和扩展。请注意,您应依赖客户端为您创建上下文;手动使用构造函数是不被推荐的。

new Context(
    HttpClientInterface $client,
    RequestInterface $request
)

setRequest

替换请求实例。

getRequest

检索请求实例。

withMethod

替换HTTP请求方法。

getMethod

检索HTTP请求方法。

withUri

替换URI实例。

getUri

检索URI实例。

withQueryString

将查询字符串添加到URI中。

getQueryString

从URI中检索查询字符串。

withQueryParameters

将多个查询参数添加到URI中。

getQueryParameters

将URI的所有查询参数作为字典检索。

withQueryParameter

将查询参数添加到URI中。

withoutQueryParameter

从URI中移除单个查询参数。

getQueryParameter

通过名称从URI中检索单个查询参数。

withHeaders

向请求中添加多个标题。

getHeaders

作为字典检索所有请求标题。代理PSR-7请求方法。

withHeader

向请求中添加指定的标题。代理PSR-7请求方法。

withoutHeader

如果请求上设置了指定的标题,则移除它。代理PSR-7请求方法。

getHeader

检索所有标题值的数组。代理PSR-7请求方法。

getHeaderLine

将所有标题值,由逗号分隔,作为一个单独的字符串检索。代理PSR-7请求方法。

withAuthorization

Authorization标题设置为给定的身份验证类型和凭据。

withBasicAuthorization

Authorization标题设置为类型Basic并将逗号分隔的凭据编码为Base64。

withBearerAuthorization

Authorization标题设置为类型Bearer并使用令牌作为凭据。

withContentType

设置Content-Type标题。

getContentType

如果设置了Content-Type标题,则检索其值,否则返回null

asJson

Content-Type标题设置为JSON(application/json)。

asXml

Content-Type标题设置为XML(text/xml)。

asPlainText

Content-Type标题设置为纯文本(text/plain)。

withBody

在上下文中设置(未序列化)的正文数据。在调度请求之前,这将根据Content-Type标题进行序列化,自动处理序列化,因此您不必这样做。通过传递Stream实例,此过程将被跳过,正文将作为请求上的原始数据设置。

getBody

检索当前的正文数据。

hasBody

检查上下文是否在其正文中包含任何数据。

run

将请求调度到客户端实例并创建一个响应上下文。

贡献

欢迎所有贡献,但请注意以下几点要求

  • 我们使用 psalm 进行静态分析,并希望保持至少 2级(但长远目标是达到1级)。任何分析结果下降的PR将不会被接受。要运行psalm,使用 composer run static-analysis 命令。
  • 每个PR都必须提供单元和集成测试。要运行所有测试套件,使用 composer run test 命令。