调用方:
$response = (new \App\Library\httpclient\HttpClient())->withHeader('key', 'value')->withTimeout(3)->withOnFailedRetry(3, 1)->withResponseHeaders(['Journal-Id'])->get("https://xxx.com/xxx");// $response->getHTTPStatusCode();// $response->getHeaders();// $response->getBody();// $response->getErrorMessage();
文件:App/Library/httpclient/HttpClient.php
<?phpnamespace App\Library\httpclient;use Closure;use GuzzleHttp\Client;use GuzzleHttp\Exception\ConnectException;use GuzzleHttp\Exception\GuzzleException;use GuzzleHttp\Handler\CurlHandler;use GuzzleHttp\HandlerStack;use GuzzleHttp\Middleware;use GuzzleHttp\Psr7\Request;use GuzzleHttp\Psr7\Response;use Psr\Http\Message\ResponseInterface;use Throwable;class HttpClient{/*** The http request header** @var array*/protected $headers;/*** The http request timeout (second)** The default timeout is 10s** @var float*/protected $timeout = 10;/*** The retry times** The default retry times is 0** @var int*/protected $retryTimes = 0;/**** The retry delay (second)** The default delay is 1s** @var float*/protected $retryDelay = 1;/*** The retry http code** The default retry http code* 408 Request Timeout RFC 7231, 6.5.7* 423 Locked RFC 4918, 11.3* 425 Too Early RFC 8470, 5.2.* 429 Too Many Requests RFC 6585, 4* 503 Service Unavailable RFC 7231, 6.6.4* 504 Gateway Timeout RFC 7231, 6.6.5** @var array*/protected $retryHttpCode = [408, 423, 425, 429, 503, 504];/*** @var callable*/protected $retryDeciderCallable;/*** The response headers** @var string[]*/protected $responseHeaders = ['Date'];/*** Configures a header item** @param string $name* @param mixed $value** @return HttpClient*/public function withHeader(string $name, $value): HttpClient{$this->headers[$name] = $value;return $this;}/*** Configures timeout (second)** @param float $timeout** @return HttpClient*/public function withTimeout(float $timeout): HttpClient{$this->timeout = $timeout;return $this;}/*** Configures retry** @param int $retryTimes* @param float $retryDelay* @param callable|null $retryDecider* @return HttpClient*/public function withOnFailedRetry(int $retryTimes, float $retryDelay, callable $retryDecider = null): HttpClient{$this->retryTimes = $retryTimes;$this->retryDelay = $retryDelay ?? $this->retryDelay;if ($retryDecider) {$this->retryDeciderCallable = $retryDecider;} else {$this->retryDeciderCallable = function (Response $response) {if (in_array($response->getStatusCode(), $this->retryHttpCode)) {return true;}return false;};}return $this;}/*** Configures response headers** @param array $headers** @return HttpClient*/public function withResponseHeaders(array $headers): HttpClient{$this->responseHeaders = array_merge($headers, $this->responseHeaders);return $this;}/*** get** @param string $url* @param array $query** @return \App\Library\httpclient\Response*/public function get(string $url, array $query = []): \App\Library\httpclient\Response{$guzzleOptions = $this->guzzleHttpOptions();$guzzleOptions['query'] = $query ?? [];try {$response = $this->response($this->guzzleHttpClient()->get($url, $guzzleOptions));} catch (Throwable $throwable) {$response = $this->response(null, $throwable->getMessage());}return $response;}/*** postForm** @param string $url* @param array $form** @return \App\Library\httpclient\Response*/public function postForm(string $url, array $form = []): \App\Library\httpclient\Response{$guzzleOptions = $this->guzzleHttpOptions();$guzzleOptions['form_params'] = $form ?? [];$guzzleOptions['headers']['Content-Type'] = 'application/x-www-form-urlencoded';try {$response = $this->response($this->guzzleHttpClient()->post($url, $guzzleOptions));} catch (GuzzleException $e) {$response = $this->response(null, $e->getMessage());}return $response;}/*** postJson** @param string $url* @param array $json** @return \App\Library\httpclient\Response*/public function postJson(string $url, array $json = []): \App\Library\httpclient\Response{$guzzleOptions = $this->guzzleHttpOptions();$guzzleOptions['json'] = $json ?? [];$guzzleOptions['headers']['Content-Type'] = 'application/json';try {$response = $this->response($this->guzzleHttpClient()->post($url, $guzzleOptions));} catch (GuzzleException $e) {$response = $this->response(null, $e->getMessage());}return $response;}/*** guzzleOptions** @return array*/private function guzzleHttpOptions(): array{$options['http_errors'] = false;$options['headers'] = $this->headers ?? [];$options['timeout'] = $this->timeout ?? 0;return $options;}/*** guzzleHttpClient** @return Client*/private function guzzleHttpClient(): Client{$handlerStack = HandlerStack::create(new CurlHandler());if (!empty($this->retryTimes)) {$handlerStack->push(Middleware::retry($this->retryDecider(), $this->retryDelay()), 'retry');}return new Client(['handler' => $handlerStack]);}/*** retryDecider** @return Closure*/private function retryDecider(): Closure{return function ($retries,Request $request,Response $response = null,ConnectException $exception = null) {if ($retries >= $this->retryTimes) {return false;}$shouldRetry = false;if ($response) {if (($this->retryDeciderCallable)($response)) {$shouldRetry = true;}}return $shouldRetry;};}/*** retryDelay** @return Closure*/private function retryDelay(): Closure{return function () {return 1000 * $this->retryDelay;};}/*** response** @param ResponseInterface|null $guzzleResponse* @param string $errorMessage* @return \App\Library\httpclient\Response*/private function response(ResponseInterface $guzzleResponse = null, string $errorMessage = ''): \App\Library\httpclient\Response{$responseHeaders = [];if (!empty($guzzleResponse)) {foreach ($this->responseHeaders as $v) {$responseHeaders[$v] = $guzzleResponse->getHeader($v);}}return (new \App\Library\httpclient\Response((!empty($guzzleResponse) ? $guzzleResponse->getStatusCode() : 0),($responseHeaders ?? []),(!empty($guzzleResponse) ? $guzzleResponse->getBody()->getContents() : ''),$errorMessage));}}
文件:App/Library/httpclient/Response.php
<?phpnamespace App\Library\httpclient;class Response{/*** @var int*/protected $statusCode;/*** @var array*/protected $headers;/*** @var string*/protected $body;/*** @var string*/protected $errorMessage;public function __construct(int $statusCode, array $headers, string $body, string $errorMessage){$this->statusCode = $statusCode;$this->headers = $headers;$this->body = $body;$this->errorMessage = $errorMessage;}public function getHTTPStatusCode(): int{return $this->statusCode;}public function getHeaders(): array{return $this->headers;}public function getBody(): string{return $this->body;}public function getErrorMessage(): string{return $this->errorMessage;}}
