循环中的 cURL 请求有时根本不返回任何内容

Kek*_*lus 3 php error-handling json curl http

问题:

我正在使用 PHP、cURL 和公共 API 来获取 json 字符串。这些 json 字符串的格式如下(简化,平均大小约为 50-60 kB):

{
   "data": {},
   "previous": "url",
   "next": "url"
}
Run Code Online (Sandbox Code Playgroud)

我想要做的是通过检查“next”属性来获取从第一个开始的所有 json 字符串。所以我有一个 while 循环,只要有“next”属性,我就会获取下一个 URL。

问题是有时,循环在结束前随机停止,经过多次测试后我无法弄清楚原因。

我说是随机的,因为有时循环到最后并没有出现问题。有时它会在 N 次循环后崩溃。

到目前为止,我无法提取任何信息来帮助我调试它。

我正在使用 PHP 7.3.0 并从 CLI 启动我的代码。

到目前为止我尝试过的:

检查标题:

不返回标题。什么都没有。

使用 curl_errno() 和 curl_error():

我在执行请求 (curl_exec($ch)) 后立即尝试了以下代码,但它从未触发。

if(curl_errno($ch)) {
   echo 'curl error ' . curl_error($ch) . PHP_EOL;
   echo 'response received from curl error :' . PHP_EOL;
   var_dump($response); // the json string I should get from the server.
}
Run Code Online (Sandbox Code Playgroud)

检查响应是否返回 null:

if(is_null($response))
Run Code Online (Sandbox Code Playgroud)

或者如果我的 json 字符串有错误:

if(!json_last_error() == JSON_ERROR_NONE)
Run Code Online (Sandbox Code Playgroud)

虽然我认为它没有用,因为如果 cURL 响应为 null 或为空,它永远不会有效。这段代码触发时,json错误码为3(JSON_ERROR_CTRL_CHAR)

有问题的代码:

function apiCall($url) {
   ...
   $ch = curl_init();
   curl_setopt($ch, CURLOPT_URL, $url);
   curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
   curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
   $response = curl_exec($ch);
}
Run Code Online (Sandbox Code Playgroud)
$inc = 0;
$url = 'https://api.example.com/' . $id;
$jsonString = apiCall($url);

if(!is_null($jsonString)) {
file_put_contents('pathToDirectory/' . $id + $inc, $jsonString);
$nextUrl = getNextUrl($jsonString);

    while ($nextUrl) {
        $jsonString = apiCall($url . '?page=' . $nextUrl);

        if(!is_null($jsonString)) {
            $inc++;
            file_put_contents('pathToDirectory/' . $id + $inc, $jsonString);
            $nextUrl = getNextUrl($jsonString);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我期待我的代码做什么:

不要随意停止,或者至少给我一个明确的错误代码。

yiv*_*ivi 5

问题是您的 API 可能会返回空响​​应、格式错误的 JSON,甚至是不同的状态代码200,您会立即停止执行。

由于您无法控制 API 响应,因此您知道它可能会随机失败,并且您无权访问 API 服务器日志(因为您没有,是吗?);你需要在你的消费者中建立某种弹性。

一些非常简单的(你需要调整它)可能是

function apiCall( $url, $attempts = 3 ) {
    // ..., including setting "$headers"
    $ch = curl_init();
    curl_setopt( $ch, CURLOPT_URL, $url );
    curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );

    for ( $i = 0; $i < $attempts; $i ++ ) {
        $response  = curl_exec( $ch );
        $curl_info = curl_getinfo( $ch );

        if ( curl_errno( $ch ) ) {
            // log your error & try again
            continue;
        }

        // I'm only accepting 200 as a status code. Check with your API if there could be other posssible "good" responses
        if ( $curl_info['http_code'] != 200 ) {
            // log your error & try again
            continue;
        }

        // everything seems fine, but the response is empty? not good.
        if ( empty( $response ) ) {
            // log your error & and try again
            continue;
        }

        return $response;
    }

    return null;
}
Run Code Online (Sandbox Code Playgroud)

这将允许您执行以下操作(从您的代码中提取):

do {
    $jsonString = apiCall($url . '?page=' . $nextUrl);
    $nextUrl    = false;

    if(!is_null($jsonString)) {
        $inc++;
         file_put_contents('pathToDirectory/' . $id + $inc, $jsonString);
         $nextUrl = getNextUrl($jsonString);
    }
}
while ($nextUrl);
Run Code Online (Sandbox Code Playgroud)

我不是在检查 API 的返回值是否为非空、不是连接错误、状态与“200”不同还是无效的 JSON。

您可能还想检查这些内容,具体取决于您使用的 API 的脆弱程度。