如何使用 Libcurl 读取从服务器返回的 JSON 错误响应

How to read the JSON Error response returned from Server using Libcurl

我有一个要求,我必须从后端服务器读取 returns 500 内部服务器错误的错误响应。错误响应采用 JSON 格式。

下面是我们的应用程序中使用的代码片段

INT CCurlHTTP::HTTPSPost(const CString& endPointUrl, const CString& urlparam,const CString& cookie){
    
    CURL *curl;
    CURLcode res;
    struct curl_slist *headers=NULL;
    char errbuf[CURL_ERROR_SIZE];
    curl = curl_easy_init();

    get_request req;
    req.buffer =0;
    req.len =0;
    req.buflen =0;

    if(curl) 
    {
        //add url, headers, and parameters to the request
        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
        curl_easy_setopt(curl, CURLOPT_URL, endPointUrl);
        curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
        headers = curl_slist_append(headers, m_httpHeadAccept);
        headers = curl_slist_append(headers, m_httpContentType);
        //callback function used to save response
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_String);
        
        req.buffer = (unsigned char*) malloc(CHUNK_SIZE);
        req.buflen = CHUNK_SIZE;
        req.len = 0;
        curl_easy_setopt(curl,CURLOPT_WRITEDATA, (void *)&req);
        
        if (!cookie.IsEmpty())
        {
            headers = curl_slist_append(headers, m_DBAuthCertficate); //What is difference between this and line no 118?
            CString pCookie = "DBAuthTicket=" + cookie;
            curl_easy_setopt(curl,CURLOPT_COOKIE, pCookie);
        }
        else
        {
            headers = curl_slist_append(headers, m_OAuthToken);
        }
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, urlparam);
        curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
        errbuf[0] = 0;
        
        curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, 512000);
        curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
        res = curl_easy_perform(curl);

        if(res != CURLE_OK)
        {
            /* if errors have occured, tell us wath's wrong with 'result'*/
            m_response.Format("%s", curl_easy_strerror(res));
            return res;
        }

        m_response = (char*)req.buffer;
        m_errDescription = errbuf;
        len = req.len;
        buflen = req.buflen;

        curl_easy_cleanup(curl);
        free(req.buffer);
    }
    return res;
}
/****************************************************************************
Function: CurlWrite_CallbackFunc_String
Description: Read data from the connected URL
Return: String of data and size
****************************************************************************/
size_t CCurlHTTP::CurlWrite_CallbackFunc_String(void *contents, size_t size, size_t nmemb, void *userdata)
{
    size_t rLen = size*nmemb;
    get_request* req = (get_request*)userdata;
    while(req->buflen < req->len + rLen + 1)
    {
        req->buffer = (unsigned char*)realloc(req->buffer,req->buflen + CHUNK_SIZE);
        req->buflen += CHUNK_SIZE;
    }
    memcpy(&req->buffer[req->len], contents, rLen);
    req->len += rLen;
    req->buffer[req->len] = 0;
    return rLen;
}

以上代码适用于成功 200 OK 请求。它读取 JSON 响应就好了。但是,当我收到 500 Internal Server 错误时,它不会读取随之而来的 JSON 错误响应。在这种情况下如何读取 JSON 响应?

通过将 CURLOPT_FAILONERROR 选项设置为 TRUE,您告诉 curl_easy_perform() 在任何 HTTP 响应 >= 400 时立即以 CURLE_HTTP_RETURNED_ERROR 失败。它不会调用 CURLOPT_WRITEFUNCTION 回调,因为它只会关闭连接,甚至不会尝试读取响应的其余部分。

要获得所需的响应数据,只需删除 CURLOPT_FAILONERROR 选项即可。 Curl 的默认行为是将响应数据传递给您,而不管 HTTP 响应代码是什么。在这种情况下,curl_easy_perform() 将 return CURLE_OK,然后您可以使用 curl_easy_getinfo(CURLINFO_RESPONSE_CODE) 检索响应代码以检查 HTTP 请求是否成功。


附带说明一下,由于显示的代码是用 C++ 编写的,我强烈建议您不要为 get_request::buffer 使用动态 char[] 缓冲区。不仅因为您根本不处理 malloc()/realloc() 故障,而且因为在 C++ 中通常应避免手动内存管理。使用 std::stringstd::vector<char> 代替,在这种情况下,您可以完全消除此代码中的 get_request,例如:

INT CCurlHTTP::HTTPSPost(const CString& endPointUrl, const CString& urlparam,const CString& cookie){
    
    ...

    std::string resp;
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
        
    ...

    //curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
    res = curl_easy_perform(curl);

    if (res != CURLE_OK)
    {
        /* if errors have occured, tell us what's wrong with 'result'*/
        m_response.Format("%s", curl_easy_strerror(res));
        curl_easy_cleanup(curl);
        return res;
    }

    m_response = resp.c_str();
    m_errDescription = errbuf;
    len = resp.size();
    buflen = resp.capacity();

    curl_easy_cleanup(curl);
    return res;
}

size_t CCurlHTTP::CurlWrite_CallbackFunc_String(void *contents, size_t size, size_t nmemb, void *userdata)
{
    size_t rLen = size * nmemb;
    static_cast<std::string*>(userdata)->append(static_cast<char*>(contents), rLen);
    return rLen;
}