Git Product home page Git Product logo

easyhttp's Introduction

EasyHttp 是一个轻量级、语义化、对IDE友好的HTTP客户端,支持常见的HTTP请求、异步请求和并发请求,让你可以快速地使用 HTTP 请求与其他 Web 应用进行通信。

EasyHttp并不强制依赖于cURL,如果没有安装cURL,EasyHttp会自动选择使用PHP流处理,或者你也可以提供自己的发送HTTP请求的处理方式。

如果您觉得EasyHttp对您有用的话,别忘了给点个赞哦^_^ !

github:github.com/yzh52521/easyhttp

gitee:gitee.com/yzh52521/easyhttp

本包是基于 gouguoyin/easyhttp 进行扩展开发,主要实现了以下扩展:

  1. 增加 retry() 重试机制。
  2. 增加 debug 日志调试功能。
  3. 增加 withHost 指定服务端base_url
  4. 增加 withBody 发送原始数据(Raw)请求
  5. 增加 withMiddleware/withRequestMiddleware/withResponseMiddleware Guzzle 中间件
  6. 增加 connectTimeout 设置等待服务器响应超时
  7. 增加 sink 响应的主体部分将要保存的位置
  8. 增加 maxRedirects 请求的重定向行为最大次数

安装说明

环境依赖

  • PHP >= 7.2.5
  • 如果使用PHP流处理,allow_url_fopen 必须在php.ini中启用。
  • 如果使用cURL处理,cURL >= 7.19.4,并且编译了OpenSSL 与 zlib。

一键安装

composer require yzh52521/easyhttp

发起请求

同步请求

常规请求
$response = Http::get('http://httpbin.org/get');

$response = Http::get('http://httpbin.org/get?name=yzh52521');

$response = Http::get('http://httpbin.org/get?name=yzh52521', ['age' => 18]);

$response = Http::post('http://httpbin.org/post');

$response = Http::post('http://httpbin.org/post', ['name' => 'yzh52521']);

$response = Http::patch(...);

$response = Http::put(...);

$response = Http::delete(...);

$response = Http::head(...);

$response = Http::options(...);
指定服务端base_url的请求
// 指定服务端base_url地址,最终请求地址为 https://serv.yzh52521.com/login
$response = Http::withHost('https://serv.yzh52521.com')->post('/login');
发送原始数据(Raw)请求
$response = Http::withBody(
    base64_encode($photo), 'image/jpeg'
)->post(...);
发送 Content-Type 编码请求
// application/x-www-form-urlencoded(默认)
$response = Http::asForm()->post(...);

// application/json
$response = Http::asJson()->post(...);
发送 Multipart 表单请求
$response = Http::asMultipart(
    'file_input_name', file_get_contents('photo1.jpg'), 'photo2.jpg'
)->post('http://test.com/attachments');

$response = Http::asMultipart(
    'file_input_name', fopen('photo1.jpg', 'r'), 'photo2.jpg'
)->post(...);

$response = Http::attach(
    'file_input_name', file_get_contents('photo1.jpg'), 'photo2.jpg'
)->post(...);

$response = Http::attach(
    'file_input_name', fopen('photo1.jpg', 'r'), 'photo2.jpg'
)->post(...);

表单enctype属性需要设置成 multipart/form-data

携带请求头的请求
$response = Http::withHeaders([
    'x-powered-by' => 'yzh52521'
])->post(...);
携带重定向的请求
// 默认
$response = Http::withRedirect(false)->post(...);

$response = Http::withRedirect([
    'max'             => 5,
    'strict'          => false,
    'referer'         => true,
    'protocols'       => ['http', 'https'],
    'track_redirects' => false
])->post(...);

$response = Http::maxRedirects(5)->post(...);
携带认证的请求
// Basic认证
$response = Http::withBasicAuth('username', 'password')->post(...);

// Digest认证(需要被HTTP服务器支持)
$response = Http::withDigestAuth('username', 'password')->post(...);
携带 User-Agent 的请求
$response = Http::withUA('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36')->post(...);
携带Token令牌的请求
$response = Http::withToken('token')->post(...);
携带认证文件的请求
$response = Http::withCert('/path/server.pem', 'password')->post(...);
携带SSL证书的请求
// 默认
$response = Http::withVerify(false)->post(...);

$response = Http::withVerify('/path/to/cert.pem')->post(...);
携带COOKIE的请求
$response = Http::withCookies(array $cookies, string $domain)->post(...);
携带协议版本的请求
$response = Http::withVersion(1.1)->post(...);
携带代理的请求
$response = Http::withProxy('tcp://localhost:8125')->post(...);

$response = Http::withProxy([
    'http'  => 'tcp://localhost:8125', // Use this proxy with "http"
    'https' => 'tcp://localhost:9124', // Use this proxy with "https",
    'no'    => ['.com.cn', 'yzh52521.cn'] // Don't use a proxy with these
])->post(...);
设置超时时间(单位秒)
$response = Http::timeout(60)->post(...);
设置等待服务器响应超时的最大值(单位秒)
$response = Http::connectTimeout(60)->post(...);
设置延迟时间(单位秒)
$response = Http::delay(60)->post(...);
设置并发次数
$response = Http::concurrency(10)->promise(...);
重发请求,设置retry方法。重试次数/两次重试之间的时间间隔(毫秒):
$response = Http::retry(3, 100)->post(...);
响应的主体部分将要保存的位置
$response = Http::sink('/path/to/file')->post(...);

Guzzle 中间件

use GuzzleHttp\Middleware;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

$response = Http::withMiddleware(
    Middleware::mapRequest(function (RequestInterface $request) {
        $request = $request->withHeader('X-Example', 'Value');
        return $request;
    })
)->get('http://example.com');

………………
$response = Http::withRequestMiddleware(
    function (RequestInterface $request) {
        $request = $request->withHeader('X-Example', 'Value');
        return $request;
    }
)->get('http://example.com');

………………

$response = Http::withResponseMiddleware(
    function (RequestInterface $response) {
        $response = $response->getHeader('X-Example');
        return $response;
    }
)->get('http://example.com');

异步请求

use yzh52521\EasyHttp\Response;
use yzh52521\EasyHttp\RequestException;

$promise = Http::getAsync('http://easyhttp.yzh52521.cn/api/sleep3.json', ['token' => TOKEN], function (Response $response) {
    echo '异步请求成功,响应内容:' . $response->body() . PHP_EOL;
}, function (RequestException $e) {
    echo '异步请求异常,错误码:' . $e->getCode() . ',错误信息:' . $e->getMessage() . PHP_EOL;
});

$promise->wait();
echo json_encode(['code' => 200, 'msg' => '请求成功'], JSON_UNESCAPED_UNICODE) . PHP_EOL;

//输出
{"code":200,"msg":"请求成功"}
异步请求成功,响应内容:{"code":200,"msg":"success","second":3}

$promise = Http::getAsync('http1://easyhttp.yzh52521.cn/api/sleep3.json', function (Response $response) {
    echo '异步请求成功,响应内容:' . $response->body() . PHP_EOL;
}, function (RequestException $e) {
    echo '异步请求异常,错误信息:' . $e->getMessage() . PHP_EOL;
});

$promise->wait();
echo json_encode(['code' => 200, 'msg' => '请求成功'], JSON_UNESCAPED_UNICODE) . PHP_EOL;

//输出
{"code":200,"msg":"请求成功"}
异步请求异常,错误信息:cURL error 1: Protocol "http1" not supported or disabled in libcurl (see https://curl.haxx.se/libcurl/c/libcurl-errors.html)

Http::postAsync(...);

Http::patchAsync(...);

Http::putAsync(...);

Http::deleteAsync(...);

Http::headAsync(...);

Http::optionsAsync(...);

使用 等待异步回调处理完成
Http::wait();

异步并发请求

use yzh52521\EasyHttp\Response;
use yzh52521\EasyHttp\RequestException;

$promises = [
    Http::getAsync('http://easyhttp.yzh52521.cn/api/sleep3.json'),
    Http::getAsync('http1://easyhttp.yzh52521.cn/api/sleep1.json', ['name' => 'yzh52521']),
    Http::postAsync('http://easyhttp.yzh52521.cn/api/sleep2.json', ['name' => 'yzh52521']),
];

$pool=Http::concurrency(10)->multiAsync($promises, function (Response $response, $index) {
    echo "发起第 $index 个异步请求,请求时长:" . $response->json()->second . '秒' . PHP_EOL;
}, function (RequestException $e, $index) {
    echo "发起第 $index 个请求失败,失败原因:" . $e->getMessage() . PHP_EOL;
});

$promise = $pool->promise();
$promise->wait();

//输出
发起第 1 个请求失败,失败原因:cURL error 1: Protocol "http1" not supported or disabled in libcurl (see https://curl.haxx.se/libcurl/c/libcurl-errors.html)
发起第 2 个异步请求,请求时长:2 秒
发起第 0 个异步请求,请求时长:3

如果未调用concurrency()方法,并发次数默认为$promises的元素个数,$promises数组里必须是异步请求

使用响应

发起请求后会返回一个 yzh52521\EasyHttp\Response $response的实例,该实例提供了以下方法来检查请求的响应:

$response->body() : string;
$response->json() : object;
$response->array() : array;
$response->status() : int;
$response->ok() : bool;
$response->successful() : bool;
$response->serverError() : bool;
$response->clientError() : bool;
$response->headers() : array;
$response->header($header) : string;

异常处理

请求在发生客户端或服务端错误时会抛出 yzh52521\EasyHttp\RequestException $e异常,该实例提供了以下方法来返回异常信息:

$e->getCode() : int;
$e->getMessage() : string;
$e->getFile() : string;
$e->getLine() : int;
$e->getTrace() : array;
$e->getTraceAsString() : string;

调试日志

有时候难免要对 Http 的请求和响应包体进行记录以方便查找问题或做什么

//传递一个日志类 thinkphp  \think\facade\Log  laravel  Illuminate\Support\Facades\Log
Http::debug(Log::class)->post(...);

更新日志

2023-08-31

  • 新增 withBody 可以发送原始数据(Raw)请求
  • 新增 withMiddleware/withRequestMiddleware/withResponseMiddleware 支持Guzzle中间件
  • 新增 connectTimeout 设置等待服务器响应超时
  • 新增 sink 响应的主体部分将要保存的位置
  • 新增 maxRedirects 请求的重定向行为最大次数

2022-05-11

  • 新增removeBodyFormat() 用于withOptions 指定body时,清除原由的bodyFromat

2022-05-10

  • 新增发送原生请求的方法client()
  • 新增发送原生异步请求的方法clientASync()

2021-09-03

  • 新增 debug() 调试日志
  • 新增 retry() 重试机制
  • 修复header重叠的bug

2020-03-30

  • 修复部分情况下IDE不能智能提示的BUG
  • get()、getAsync()方法支持带参数的url
  • 新增withUA()方法
  • 新增withStream()方法
  • 新增asMultipart()方法,attach()的别名
  • 新增multiAsync()异步并发请求方法

2020-03-20

  • 新增异步请求getAsync()方法
  • 新增异步请求postAsync()方法
  • 新增异步请求patchAsync()方法
  • 新增异步请求putAsync()方法
  • 新增异步请求deleteAsync()方法
  • 新增异步请求headAsync()方法
  • 新增异步请求optionsAsync()方法

Todo List

  • 异步请求
  • 并发请求
  • 重试机制
  • 支持http2
  • 支持swoole

easyhttp

easyhttp's People

Contributors

gouguoyin avatar yuanzhihai avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

easyhttp's Issues

异步提示 Call to undefined function GuzzleHttp\Promise\settle()

使用webman最新版加载了这个包,使用文档例子进行异步操作。出现错误,提示调用了没有定义的function。
$promise = Http::getAsync($url, function (Response $response) { echo '异步请求成功,响应内容:' . $response->body() . PHP_EOL; }, function (RequestException $e) { echo '异步请求异常,错误信息:' . $e->getMessage() . PHP_EOL; }); $promise->wait(); echo json_encode(['code' => 200, 'msg' => '请求成功'], JSON_UNESCAPED_UNICODE) . PHP_EOL;

GUZZLE版本
`guzzlehttp/guzzle 7.8.0 Guzzle is a PHP HTTP client library

guzzlehttp/promises 2.0.1 Guzzle promises library

guzzlehttp/psr7 2.6.1 PSR-7 message implementation that also provides common utility methods
`
下面是报错信息
Error: Call to undefined function GuzzleHttp\Promise\settle() in D:\Web\rsapi\vendor\yzh52521\easyhttp\src\Request.php:100
Stack trace:
#0 D:\Web\rsapi\vendor\guzzlehttp\promises\src\TaskQueue.php(50): yzh52521\EasyHttp\Request->__destruct()
#1 D:\Web\rsapi\vendor\guzzlehttp\guzzle\src\Handler\CurlMultiHandler.php(163): GuzzleHttp\Promise\TaskQueue->run()
#2 D:\Web\rsapi\vendor\guzzlehttp\guzzle\src\Handler\CurlMultiHandler.php(189): GuzzleHttp\Handler\CurlMultiHandler->tick()
#3 D:\Web\rsapi\vendor\guzzlehttp\promises\src\Promise.php(251): GuzzleHttp\Handler\CurlMultiHandler->execute(true)
#4 D:\Web\rsapi\vendor\guzzlehttp\promises\src\Promise.php(227): GuzzleHttp\Promise\Promise->invokeWaitFn()
#5 D:\Web\rsapi\vendor\guzzlehttp\promises\src\Promise.php(272): GuzzleHttp\Promise\Promise->waitIfPending()
#6 D:\Web\rsapi\vendor\guzzlehttp\promises\src\Promise.php(229): GuzzleHttp\Promise\Promise->invokeWaitList()
#7 D:\Web\rsapi\vendor\guzzlehttp\promises\src\Promise.php(69): GuzzleHttp\Promise\Promise->waitIfPending()
#8 D:\Web\rsapi\app\controller\TestController.php(34): GuzzleHttp\Promise\Promise->wait()
#9 D:\Web\rsapi\vendor\workerman\webman-framework\src\App.php(325): app\controller\TestController->index(Object(support\Request))
#10 D:\Web\rsapi\vendor\workerman\webman-framework\src\App.php(168): Webman\App::Webman{closure}(Object(support\Request))
#11 D:\Web\rsapi\vendor\workerman\workerman\Connection\TcpConnection.php(646): Webman\App->onMessage(Object(Workerman\Connection\TcpConnection), Object(support\Request))
#12 D:\Web\rsapi\vendor\workerman\workerman\Events\Select.php(311): Workerman\Connection\TcpConnection->baseRead(Resource id #366)
#13 D:\Web\rsapi\vendor\workerman\workerman\Worker.php(1485): Workerman\Events\Select->loop()
#14 D:\Web\rsapi\vendor\workerman\workerman\Worker.php(1402): Workerman\Worker::forkWorkersForWindows()
#15 D:\Web\rsapi\vendor\workerman\workerman\Worker.php(560): Workerman\Worker::forkWorkers()
#16 D:\Web\rsapi\vendor\workerman\webman-framework\src\support\App.php(131): Workerman\Worker::runAll()
#17 D:\Web\rsapi\start.php(4): support\App::run()
#18 {main} [] []

偶尔报错,不知道哪的问题

#31 {main}Error: Call to a member function getHeaders() on null in /app/webman/vendor/yzh52521/easyhttp/src/Request.php:3
Stack trace:
#0 [internal function]: yzh52521\EasyHttp\Request->yzh52521\EasyHttp{closure}()
#1 /app/webman/vendor/yzh52521/easyhttp/src/Logger.php(178): call_user_func()
#2 /app/webman/vendor/yzh52521/easyhttp/src/Logger.php(141): yzh52521\EasyHttp\Logger->getLogMessage()
#3 /app/webman/vendor/yzh52521/easyhttp/src/Logger.php(244): yzh52521\EasyHttp\Logger->log()
#4 /app/webman/vendor/guzzlehttp/promises/src/RejectedPromise.php(42): yzh52521\EasyHttp\Logger->yzh52521\EasyHttp{closu
#5 /app/webman/vendor/guzzlehttp/promises/src/TaskQueue.php(48): GuzzleHttp\Promise\RejectedPromise::GuzzleHttp\Promise{
#6 /app/webman/vendor/guzzlehttp/promises/src/Promise.php(248): GuzzleHttp\Promise\TaskQueue->run()
#7 /app/webman/vendor/guzzlehttp/promises/src/Promise.php(224): GuzzleHttp\Promise\Promise->invokeWaitFn()
#8 /app/webman/vendor/guzzlehttp/promises/src/Promise.php(269): GuzzleHttp\Promise\Promise->waitIfPending()
#9 /app/webman/vendor/guzzlehttp/promises/src/Promise.php(226): GuzzleHttp\Promise\Promise->invokeWaitList()
#10 /app/webman/vendor/guzzlehttp/promises/src/Promise.php(62): GuzzleHttp\Promise\Promise->waitIfPending()
#11 /app/webman/vendor/guzzlehttp/guzzle/src/Client.php(187): GuzzleHttp\Promise\Promise->wait()
#12 /app/webman/vendor/yzh52521/easyhttp/src/Request.php(497): GuzzleHttp\Client->request()
#13 /app/webman/vendor/yzh52521/easyhttp/src/Request.php(364): yzh52521\EasyHttp\Request->request()

Webman使用easyhttp时,异步访问有问题

代码类似这样:

public function test(Request $request)
{
    Http::getAsync('http://wisersportclub.com:8000', [], function (Response $response) {
        echo '异步请求成功,响应内容:' . $response->body() . PHP_EOL;
    }, function (RequestException $e) {
        echo '异步请求异常,错误码:' . $e->getCode() . ',错误信息:' . $e->getMessage() . PHP_EOL;
    });
    echo json_encode(['code' => 200, 'msg' => '请求成功'], JSON_UNESCAPED_UNICODE) . PHP_EOL;

    return json(['code' => 0, 'msg' => 'ok', 'data' =>$request->post('data')]);
}

public function test1(Request $request)
{
    $response = Http::get('http://wisersportclub.com:8000');
    echo '同步请求成功,响应内容:' . $response->body() . PHP_EOL;

    return json(['code' => 0, 'msg' => 'ok', 'data' =>$request->post('data')]);
}

出现问题:
访问test时,异步输出一直没有,此时如果访问一下test1,异步立刻输出,且同步也输出:

worker listen processes status
webman http://0.0.0.0:8100 2 [ok]
monitor none 1 [ok]
{"code":200,"msg":"请求成功"}
(此时一直不动,直到访问test1)
异步请求成功,响应内容:hello webman
同步请求成功,响应内容:hello webman
(再次访问test,仍然是这样)
{"code":200,"msg":"请求成功"}

请问是什么情况呢?

Retry无法正常使用

只要设置了retry,就直接报错
PHP Fatal error: Uncaught TypeError: yzh52521\EasyHttp\Retry::yzh52521\EasyHttp\{closure}(): Argument #4 ($exception) must be of type ?yzh52521\EasyHttp\RequestException, GuzzleHttp\Exception\RequestException given, called in /www/webman/miner-galacticash/vendor/guzzlehttp/guzzle/src/RetryMiddleware.php on line 101 and defined in /www/webman/miner-galacticash/vendor/yzh52521/easyhttp/src/Retry.php:26

easyhttp不支持请求本地域名?

$response = Http::get('http://127.0.0.1:8000/index/');

当请求thinkphp启动服务的域名时,内置服务器直接崩溃,网站一直处于请求状态,如果请求非本地域名地址就正常

关于异步并发请求,文档的使用demo 不完整

你好,我在webman项目中引入了你的http库,然后发现http并发请求无法执行,或者是执行后callback没有执行。当php进程结束时,console才输出了所有的callback内容;

然后我去阅读源代码,我发现在 并发异步请求后,你是不是少了2行代码?我现在将它标记出来,你可以检测看是否缺失;

这是你的demo

$promises = [
            Http::getAsync('http://easyhttp.yzh52521.cn/api/sleep3.json'),
            Http::getAsync('http1://easyhttp.yzh52521.cn/api/sleep1.json', ['name' => 'yzh52521']),
            Http::postAsync('http://easyhttp.yzh52521.cn/api/sleep2.json', ['name' => 'yzh52521']),
        ];

Http::concurrency(3)->multiAsync($promises, function (Response $response, $index) {
    echo "发起第 $index 个异步请求,请求时长:" . $response->json()->second . '秒' . PHP_EOL;
}, function (RequestException $e, $index) {
    echo "发起第 $index 个请求失败,失败原因:" . $e->getMessage() . PHP_EOL;
});

我补充后的demo用法:

$promises = [
    Http::getAsync('http://easyhttp.yzh52521.cn/api/sleep3.json'),
    Http::getAsync('http1://easyhttp.yzh52521.cn/api/sleep1.json', ['name' => 'yzh52521']),
    Http::postAsync('http://easyhttp.yzh52521.cn/api/sleep2.json', ['name' => 'yzh52521']),
];

$pool = Http::concurrency(3)->multiAsync($promises, function (Response $response, $index) {
    echo "发起第 $index 个异步请求,请求时长:" . $response->json()->second . '秒' . PHP_EOL;
}, function (RequestException $e, $index) {
    echo "发起第 $index 个请求失败,失败原因:" . $e->getMessage() . PHP_EOL;
});

// 等待请求完成
$promise = $pool->promise();

// 强制结束请求
$promise->wait();

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.