pluggit/http-client

一个通过配置执行HTTP请求的库

3.0.0 2024-06-12 10:52 UTC

README

一个小型的PSR7兼容库,用于根据预配置的请求集执行http请求

Scrutinizer Code Quality Code Coverage Build Status

TL;DR

# requests.yml
my_cool_api:
  endpoint: http://my_cool_api.com/v2
  requests:
    get_user:
      path: /user/${USER_ID}
    create_user:
      path: /user/${USER_ID}
      method: POST
      headers:
        Content-Type: application/json
      body:
        name: ${NAME}
        email: ${EMAIL}
$coolApi = ClientBuilder::create()
    ->withYamlConfig('requests.yml')
    ->build('my_cool_api');

$user = $client->json('get_user', ['user_id' => $userId]);

echo "Hello ".$user->name;

目录

安装

像往常一样要求包

composer require pluggit/http-client

要使用Guzzle作为发送者(默认选项),您必须手动要求

composer require guzzlehttp/guzzle "^6.0"

需求

  • php >=5.5
  • guzzlehttp/psr7 ^1.0
  • psr/log ^1.0

注意: 此包依赖于guzzle实现PSR-7: HTTP消息接口

附加需求

Guzzle

此库允许使用不同的发送者来执行请求;并且包括了对guzzlehttp/guzzle的适配器,它包含在库中,但您必须手动要求依赖项

composer require guzzlehttp/guzzle "^6.0"

可选需求

Yaml解析

如果您想使用Yaml文件作为配置源,您可以:a) 使用您自己的解析器并将配置作为数组传递;b) 将symfony/yaml作为依赖项要求,并将yaml文件路径传递给构建器

composer require symfony/yaml "^3.1"

日志记录

您想知道为什么您的请求失败吗?没问题,该库接受任何Psr-3 logger logger实现(如Monolog)以写入调试和错误消息。

为了快速入门,您可以使用Symfony的控制台包在控制台中查看调试消息

composer require symfony/console "^3.1"

现在您将能够激活控制台调试模式

$builder->withConsoleDebug();

Pimple

如果您使用pimple构建依赖项,则可以使用提供者Cmp\Http\Provider\HttpClientServiceProvider来注册构建器多客户端

composer require pimple/pimple "^3.0"

注意: 请参阅附录了解如何注册提供者

兼容性

此库已在以下PHP版本和配置下进行了测试

使用方法

此库允许从一组配置值构建请求,通过客户端执行它们,并获取响应

定义配置

配置是告诉客户端如何构建请求的内容,让我们看看一些配置示例

my_users_api:
  endpoint: https://api.mysite.com
  headers:
    api_version: 1.2
  options:
    timeout: 2
  requests:
    list_users:
      path: /users
      options:
        timeout: 15
    get_user:
      path: /users/${USER_ID}
    create_user:
      path: /users/${USER_ID}
      headers: 
        - Content-Type: application/json
      method: POST
      
# Another service
the_comments_api:
  endpoint: http://messaging_service.com/v2/
  ...

第一行定义了一个服务名称,在本例中为my_users_api

注意: 您可以在config-sample/requests.yaml中看到一个完整的示例配置文件,用于处理REST API

服务配置值

  • endpoint (必需):这是此服务的所有请求的端点
  • headers:一个键值数组,包含要添加到所有此服务的请求的标头
  • query:一个键值数组,包含要附加到此服务的所有请求URI的查询参数
  • body:一个键值数组,包含要发送到此服务的所有请求的正文中的POST参数
  • version:HTTP协议版本,默认为1.1
  • options:一个键值数组,包含传递给HTTP客户端发送者以修改其行为的选项
  • requests:一个键值数组,包含此服务的允许请求

请求配置值

请求配置允许设置几乎与服务配置相同选项,覆盖服务提供的值,这允许为服务定义一般行为,但可以调整一些特定请求的选项,例如,允许更长的超时

  • path必需):定义跟随端点的路径
  • method:定义请求的HTTP方法。默认为GET
  • headers:一个键值数组,包含要添加到此请求的头部
  • query:一个键值数组,包含要附加到此请求URI的查询参数
  • body:一个键值数组,包含要发送到请求主体的post参数
  • version:HTTP协议版本,默认为1.1
  • options:一个键值数组,包含传递给HTTP客户端发送者以修改其行为的选项
底层客户端选项

请求选项允许自定义客户端行为,而无需库知道如何处理它们的特定细节。

以与Guzzle提供的集成为例,我们可以有

  • 基本认证:auth: ['myUser', 'myPass']
  • 超时:timeout: 2
  • 连接超时:timeout: 15
  • 添加证书:['cert' => ['/path/server.pem', 'password']]

配置占位符

库允许指示配置参数中存在动态值,作为占位符

占位符规则
  • 仅在pathqueryheadersbody中允许占位符
  • 使用此格式添加占位符:${PLACEHOLDER_NAME}
  • 占位符标签名称必须为大写(尽管在替换时可以使用小写键)
  • 可以在所有选项中多次使用相同的占位符,它将在所有选项中替换。
在请求中替换占位符

要替换占位符,请在创建请求时作为参数传递值

secure_api:
  endpoint: https://topsecret.com
  headers:
    api_key: MyPersonalApiKey
    token: ${TOKEN}
  requests:
    get_user:
      path: /users/${USER_ID}
$request = $client->create('secure_api', 'get_user', [
    'token'   => $oath->sign($secret),
    'user_id' => $userId
]);

配置构建器

构建器(Cmp\Http\ClientBuilder)是一个将帮助您创建客户端的对象,简化了自定义过程。以下是可用的选项

$builder = ClientBuilder::create()
    ->withConfig($requests)
    ->withGuzzleSender($myCustomGuzzleClient)
    ->withConsoleDebug();

配置构建器后,您就准备好为您的HTTP服务创建客户端了

构建客户端

有两种不同的客户端允许创建和执行请求

  • Cmp\Http\Client\MultiClientInterface:此客户端可以访问所有服务
  • Cmp\Http\Client\ServiceClientInterface:此客户端只能从单个服务执行请求

注意:您应该始终尝试在服务中注入ServiceClientInterface,这将防止意外触发其他服务的请求

客户端中可用的方法是

  • create:创建请求(Cmp\Http\Message\Request
  • send:发送请求,返回一个Cmp\Http\Message\Response

它们还提供了一些快捷方式以简化使用

  • execute:在单个调用中创建并发送请求
  • json:创建并发送请求,解析JSON响应
  • jsonAsArray:创建并发送请求,从JSON响应解析为数组

构建多客户端

构建客户端最简单的方法是使用构建器上的build方法

$client = $builder->build();

构建服务客户端

要构建服务客户端,请在构建器的最后一步指定服务名称

$myServiceApi = $builder->build('my_service');

自定义构建

客户端需要3个依赖项才能工作,构建器有方法可以覆盖所有这些依赖项,使用自定义实现

发送者

这是一个发送请求的类,它必须实现Cmp\Http\Sender\SenderInterface并返回一个Psr-7兼容的响应

构建器将默认尝试使用提供的 Cmp\Http\Sender\GuzzleSender,要指定不同的一个,请使用

$builder->withSender($sender);

即使你想使用guzzle,也可以自定义内部客户端

// tweak the client
$customGuzzleClient = new Guzzlehttp\Guzzle();

// Pass it to the builder
$builder->withGuzzleSender($customGuzzleClient);
配置

这是唯一的必需依赖项,一个定义可用请求的配置

$builder->withConfig($config);

如果已安装 symfony/yaml,你可以传递一个包含yaml配置的文件路径

$builder->withYamlConfig($yamlFile);
日志记录器

你可以传递一个 Psr\Log\LoggerInterface 来接收调试和错误消息

$builder->withLogger($logger);

如果你想在控制台调试原始http请求/响应,你可以在构建器中激活调试输出(你需要安装 symfony/console

$builder->withConsoleDebug();

创建请求

要创建一个请求,你需要识别你想要的服务和请求。此外,你可以传递动态值以替换占位符

$request = $client->request('weather', 'forecast', ['city' => $city]);

// Service specific clients do not need the service name
$request = $weather->request('forecast', ['city' => $city]);

创建的请求是 Cmp\Http\Message\Request 实例,实现了 Psr\Http\Message\RequestInterface,这使得它们适合在库和框架之间共享

库请求对象提供了一些帮助器,以便更容易地使用它们

  • withQueryParameter($key, $value):允许你添加或修改一个查询参数
  • withPost(array $params):允许传递一个键值数组作为正文发送
  • withJsonPost(array $params):允许传递一个键值数组;它将被编码为json,并将添加头 Content-Type: application/json
  • __toString():这使得请求可以嵌入到字符串中

自定义请求创建

如果你提供给构建器的请求工厂符合 Cmp\Http\RequestFactoryInterface,你将能够扩展请求类并更改行为。

你可能想知道"我为什么要这样做呢?"

例如,为了

  • 将所有出站端点更改为模拟服务器,以防止在测试环境中执行某些请求
  • 将配置应用于所有服务的所有请求
$factory = new TestEnvironmentsRequestFactory($config);
$builder->withRequestFactory($factory);

发送请求

发送请求后,你会收到一个兼容 Psr\Http\Message\ResponseInterfaceCmp\Http\Message\Response 实例

此响应对象还提供了一些帮助器方法

  • json($asArray = true):将正文解析为json并返回一个stdClass对象或一个array
  • jsonAsArray():与之前相同,但强制返回类型为数组
  • __toString():这使得响应可以嵌入到字符串中

异常处理

并非所有事情都按预期工作;为了使你的生活更轻松地处理这些问题,库提供了一组小的异常

  • Cmp\Http\Exception\RuntimeException:这是库中使用的基异常,所有在库代码中抛出的异常都使用或扩展了此异常
  • Cmp\Http\Exception\RequestBuildException:当无法完成请求构建过程时抛出此异常
  • Cmp\Http\Exception\RequestExecutionException:当发送请求时发生某些错误时抛出此异常

REST API集成示例

我将向你展示如何执行REST API中最常见的操作。
让我们假设我们想要与API端点交互来管理我们的应用程序用户。用户实体如下所示

{
  "id": "1",
  "first_name": "John",
  "last_name": "Doe"
}

现在让我们在我们的配置文件中定义典型的请求

# Service definition
my_app:
  # The endpoint is required
  endpoint: https://api.myapp.com/v1
  options:
    auth: ['apikey', 'secret']

  # At least one request is required too
  requests:
    list_users:
      path: /users

    get_user:
      path: /users/${USER_ID}

    delete_user:
      path: /users/${USER_ID}
      method: DELETE

    put_user:
      path: /users/${USER_ID}
      method: PUT
      headers:
        Content-Type: application/json
      body:
        first_name: ${FIRST_NAME}
        last_name: ${LAST_NAME}

    create_user:
      path: /users
      method: POST
      headers:
        Content-Type: application/json
      body:
        first_name: ${FIRST_NAME}
        last_name: ${LAST_NAME}

    update_user:
      path: /users/${USER_ID}
      method: PATCH
      headers:
        Content-Type: application/json
      # body: No need to define all the body params here, we can do it at request time

构建API客户端

让我们根据配置为我们的api构建一个服务客户端,所有请求都将包含基本身份验证

$api = $builder->build('my_app'); 

###列出用户此调用将返回一个用户数组

// We are going to modify the request to pass a limit on the number of users to retrieve
$users = $api->send($api->request('list_users')->withQueryParameter('limit', 25))->json();

获取用户

此调用将向uri路径中注入用户id,以请求用户

// The json shortcut creates, executes and parses the body response all in a single call
$users = $api->json('get_user', ['user_id => 1]);

创建用户

在这里我们将替换正文数组中的占位符,如果api只接受json正文,我们必须添加正确的content type头,如示例所示,以便正确创建请求

$user = $api->json('create_user', ['name' => 'Jane', 'last_name' => 'Roe']);

替换用户

我们可以在路径或正文参数同时替换占位符

$user = $api->json('put_user', ['user_id' => 1, 'name' => 'Jane', 'last_name' => 'Roe']);

更新用户

当我们只更新某些字段时,我们事先不知道将要发送哪些字段,因此最好稍后决定

$request = $api->create('update_user', ['user_id' => 1]);
// Remember to assign the result, as the request return always a new instance when modifying it
$request = $request->withPost(['name' => 'Jane']);
$updatedUser = $api->send($request)->json();

删除用户

删除用户更加简单

$api->execute('delete_user', ['user_id' => 1]);

附录

Pimple服务提供者

库中包含了一个服务提供者,用于以简单的方式注册客户端:Cmp\Http\Provider\HttpClientServiceProvider。该提供者将在键http_client.builder处注册构建器对象Cmp\Http\ClientBuilder,并在http_client.client中注册一个通用多客户端Cmp\Http\Client\MultiClient,但它也接受这两个服务的别名。

$container->register(new HttpClientServiceProvider('requester', 'client'), [
    'http_client.yaml' => 'config/requests.yml',
]);
$builder = $container['requester']; // or http_client.builder
$client = $container['client'];     // or http_client.client

提供者接受的选项有

  • http_client.yaml:一个包含yaml配置文件的文件路径
  • http_client.config:包含请求的配置数组
  • http_client.logger:传递一个Psr\Logger\LoggerInterface对象以向客户端添加日志记录
  • http_client.sender:实现Cmp\Http\Sender\SenderInterface接口的自定义实现
  • http_client.guzzle:自定义的GuzzleHttp\ClientInterface对象(用于保留引用)
  • http_client.factory:自定义的Cmp\Http\RequestFactoryInterface实现
  • http_client.debug:传递true,这将激活控制台输出以进行调试

开发环境

要构建测试环境,您需要安装docker和docker-compose

make dev

运行测试

make unit
make integration

您只能以这种方式运行针对特定PHP版本的测试

make unit PHP_VERSION=5.6
make integration PHP_VERSION=5.6

代码覆盖率

您可以为代码覆盖率构建HTML格式的报告。它将在bin/code-coverage中可用

make code-coverage

停止环境

make nodev

删除环境

您可以通过删除docker镜像来实现完全清理

 make nodev IMAGES=true