gupo/http-client

辅助第三方请求的Http请求客户端

v3.0.4 2025-01-08 06:51 UTC

README

  • 发布人:鲁银莲

获取

环境

  • PHP8.1+
  • Laravel9.33.0+

包继承关系

本包的Http请求客户端继承关系:Gupo\HttpClient\HttpClient -> Cloudladder\Http\Client -> GuzzleHttp\Client

我们的包解决了什么问题?

我们普遍如何使用 Client 类?

每个请求,在代码中直接调用 (new Client())->get("uri")

当前有什么痛点?

  1. 请求的第三方接口,散在项目各处,无法实现统一管理
  2. 同厂商的“站点地址”需要在每次调用时都设置一次
  3. 其他配置无法实现“系统统一配置”、“单厂商统一配置”、“单请求单独配置”的互相配合
  4. 对于请求的响应结果,无法实现统一处理

我们解决了什么问题?

  1. 单厂商的Api,可以在一个类里面统一管理
  2. “站点地址”只需配置一次
  3. 各种配置实现了“系统统一配置”、“单厂商统一配置”、“单请求单独配置”的互相配合
  4. 请求的响应结果实现统一处理,也可以设置单个厂商或单个请求的个性化处理
  5. 提供了许多公共方法以供使用。如果公共方法无法满足需求,可以进行系统全局重写或单厂商重写

安装

执行安装

命令:php artisan gupo:http-client:install

安装做了些什么?

  1. 发布资源:
    1. Api访问出口类:\App\Services\Api\ApiService
    2. 模块目录:\App\Services\Api\Modules\
    3. Api抽象类:\App\Services\Api\Support\BaseHttpClient
    4. 配置文件:config/apis.php
  2. \App\Services\Api\ApiService注册"类的别名"到"config/app.php"的"aliases"下,别名为Api

使用

文件结构

/
├── /app
│   └── /Services # 服务层
│       └── /Api # Api服务
│           ├── /Modules # 模块层,里面的所有Api类,都继承BaseHttpClient
│           │   ├──XxxApi.php # 子类-厂商A的Api
│           │   ├──XxxApi.php # 子类-厂商B的Api
│           │   ├──XxxApi.php # 子类-厂商C的Api
│           │   └──XxxApi.php # 子类-厂商D的Api
│           │   ...
│           ├── /Support # 支持层
│           │   └──BaseHttpClient.php # Api抽象类
│           └── ApiService.php # Api访问出口
└── /config
    └── apis.php # api配置

Api配置-config/apis.php

每个厂商的的api的"站点地址"与其他相关配置,请配置到config/apis.php。获取配置时,用config('apis.xxx'),例config('apis.wdPay.url')

注:配置文件中的内容,请从.env中获取,例:env('WD_PAY_URL')

创建厂商Api文件

为单个厂商访问的所有Api在app\Services\Api\Modules\目录下单独建一个Api类,继承抽象类\App\Services\Api\Support\BaseHttpClient,类名建议为厂商名(如果厂商业务模块区分较为明确,也可以以厂商下的业务模块为单独建类的单位)。

例:

  1. 如果以厂商为建Api类的单位,厂商为"万达",建议类名为WdApi;
  2. 例:如果以厂商与业务为建Api类的单位,厂商为"万达",业务为万达集团下的"统一支付",建议类名为WdPayApi;

Api类注册

注册Api单例访问出口

\App\Services\Api\ApiService类中注册Api单例的唯一访问出口,当我们需要访问Api类时,只从此处进行访问。例:

<?php

declare(strict_types=1);

namespace App\Services\Api;

use App\Services\Api\Modules\EmrApi;
use App\Services\Api\Modules\WdPayApi;
use App\Services\Api\Modules\ZlbApi;

class ApiService
{
    /**
     * 万达-统一支付
     *
     * @return WdPayApi
     */
    public static function wdPay(): WdPayApi
    {
        return WdPayApi::instance();
    }
    
    /**
     * 浙里办
     *
     * @return ZlbApi
     */
    public static function zlb(): ZlbApi
    {
        return ZlbApi::make();
    }

    /**
     * 移动医生
     *
     * @return EmrApi
     */
    public static function emr(): EmrApi
    {
        return EmrApi::make();
    }

}

访问Api单例

\Api::wdPay()

Api类开发

实现抽象内容

详见\Gupo\HttpClient\HttpClient类对哪些方法进行了抽象

 <?php

/**
  * 获取-基础Uri
  */
 abstract public function getBaseUri(): string;

 /**
  * 获取-日志实例
  */
 abstract public function getLogger(): LoggerInterface;

 /**
  * 获取-Api名称
  */
 abstract public function getApiName(): string;

初始化类

如果针对单个厂商,需要进行初始化。例如需要配置appid、密钥等信息。请在public function init(){}中实现。init方法,会在实例化类时被自动调用。

个性化配置

通过该扩展包与该设计结构的结合,可以对\Gupo\HttpClient\HttpClient类,及该类引用的特性HandlerStackTraitMiddlewareTraitResponseTraitLogTrait中的属性与方法进行重新设置或重写。

个性化配置-配置思想
  1. 如果对"全系统"进行配置的内容,请写在\App\Services\Api\Support\BaseHttpClient类中。
  2. 如果对"单厂商"进行配置的内容,请写在\App\Services\Api\Modules\厂商Api类类中。
  3. 如果对"单接口"进行配置的内容,请写在单个请求中进行传参。
个性化配置-可在"全系统"或"单厂商"中配置的内容举例(但不限于)
  • 参考\Gupo\HttpClient\HttpClient,如下
 <?php

/**
  * @var string|null 基础uri,通过'getBaseUri'方法的返回值初始化
  * 如果同一厂商的站点地址变更,请在"发起请求前"重写该属性
  */
 protected ?string $base_uri;

 /**
  * @var LoggerInterface|null 日志实例,通过'getLogger'方法的返回值初始化
  */
 public ?LoggerInterface $logger = null;

 /**
  * @var string|null 异常前缀
  * 如果要个性化定义某个类的"异常前缀",请重写该属性
  */
 protected ?string $default_exception_prefix = null;

 /**
  * @var int|null 超时时间(单位:秒)
  */
 protected ?int $default_timeout = 10;

 /**
  * @var bool 是否执行"为4xx或5xx响应抛出异常"的中间件:true-执行;false-不执行;
  */
 protected bool $default_http_errors = true;

 /**
  * 异常前缀
  *
  * @param string|null $exceptionPrefix
  * @return string
  * @author lyl
  */
 public function exceptionPrefix(?string $exceptionPrefix = null): string;

 /**
  * 处理异常
  * 请求过程中出现异常时,默认调用当前类。如果"全局-个性化处理异常",请在子类中"重写"该方法;如果"单请求-个性化处理异常",请在调用请求方法时传入"回调方法"
  *
  * @param Throwable              $exception
  * @param ResponseInterface|null $response
  * @throws HttpClientException
  * @throws Throwable
  * @author lyl
  */
 protected function handleException(\Throwable $exception, ?ResponseInterface $response): void;
  • 参考\Gupo\HttpClient\Traits\HandlerStackTrait,如下
 <?php

/**
  * 头部的中间件(可重写)
  *
  * @return array<string|int, callable>
  * @author lyl
  */
 protected function headMiddlewareList(): array;

 /**
 * 尾部的中间件(建议根据系统实际情况重写)
 *
 * @return array<string|int, callable>
 * @author lyl
 */
 protected function tailMiddlewareList(): array;
个性化配置-可在"单接口"中配置的内容举例
  1. 请求中可以传入的参数
 <?php

/**
  * 发起GET请求
  *
  * @param string        $uri             请求地址
  * @param array         $query           请求数据-query
  * @param array         $middlewareList  中间件列表
  * @param callable|null $handleException 自定义异常处理
  * @param array         $options         请求配置
  * @return ResponseInterface|null
  * @throws HttpClientException
  * @throws Throwable
  * @author lyl
  */
 protected function get(
     string    $uri,
     array     $query = [],
     array     $middlewareList = [],
     ?callable $handleException = null,
     array     $options = []
 );

 /**
  * 发起POST请求
  *
  * @param string        $uri             请求地址
  * @param array         $query           请求数据-query
  * @param array         $json            请求数据-json
  * @param array         $formParams      请求数据-form_params
  * @param array         $middlewareList  中间件列表
  * @param callable|null $handleException 自定义异常处理
  * @param array         $options         请求配置
  * @return ResponseInterface|null
  * @throws HttpClientException
  * @throws Throwable
  * @author lyl
  */
 protected function post(
     string    $uri,
     array     $query = [],
     array     $json = [],
     array     $formParams = [],
     array     $middlewareList = [],
     ?callable $handleException = null,
     array     $options = []
 )
  1. 请求参数options中的内容,详见 中文文档-请求选项English Document-Request Options

写接口请求方法

在Api类中,为某接口写一个请求方法。例:

 <?php

public function getApplyDetail(string $card_no, int $id): array
 {
     $uri = '/open_api/application_patient/record/detail';

     $response = $this->get(
         uri: $uri,
         query: [
             'card_no' => $card_no,
             'id'      => $id,
         ],
     );

     return $this->getResponseValue($response);
 }

BaseUri根据不同逻辑变更

有的厂商的BaseUri不是固定的,会根据不同接口或不同逻辑变更。像这种情况,可以在发起请求前,重写属性base_uri。例:

 <?php

/**
  * 获取-基础Uri
  */
 public function getBaseUri(): string
 {
     return config('apis.emr.url');
 }

 /**
  * 重写-基础Uri
  *
  * @param string $hid
  */
 private function setBaseUri(string $main_hid)
 {
     $base_uri       = $this->getBaseUri();
     $this->base_uri = str_replace('{hid}', $main_hid, $base_uri);
 }

 /**
  * 移动医生-接口转发
  */
 public function relay(string $hid, string $url, array $query)
 {
     $main_hid = explode('_', $hid)[0];

     // 重写-基础Uri
     $this->setBaseUri((string) $main_hid);

     $response = $this->get(
         uri: $url,
         query: $query,
         handleResponse: $this->customHandleResponse(),
         options: [
             RequestOptions::HEADERS => [
                 'ase' => 'false',
             ],
         ],
     );

     return $this->getResponseValue($response);
 }

访问Api请求接口

例: $data = \Api->emr()->relay($hid, $url, $query)