logo
0
0
WeChat Login
鲁银莲<luyl786731784@163.com>
feat: 文档完善

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

  • 发布人:鲁银莲

获取

环境

  • 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)