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

Time:03-26

I have a requirement where I have to read the error response from backend server which returns 500 Internal Server error. The error response is in JSON Format.

Below is the code snippet used in our application

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 paramaters 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;
}

The above code works fine for the Success 200 OK Requests. It reads the JSON Response just fine. However, when I get a 500 Internal Server error, it does not read the JSON Error response that comes along with it. How do I read the JSON response in this scenario?

CodePudding user response:

By setting the CURLOPT_FAILONERROR option to TRUE, you are telling curl_easy_perform() to fail immediately with CURLE_HTTP_RETURNED_ERROR on any HTTP response >= 400. It will not call the CURLOPT_WRITEFUNCTION callback, as it will simply close the connection and not even attempt to read the rest of the response.

To get the response data you want, simply remove the CURLOPT_FAILONERROR option. Curl's default behavior is to deliver the response data to you regardless of the HTTP response code. In which case, curl_easy_perform() will return CURLE_OK, and you can then retrieve the response code using curl_easy_getinfo(CURLINFO_RESPONSE_CODE) to check if the HTTP request was successful or not.


On a side note, since the code shown is written in C , I would strongly advise you NOT to use a dynamic char[] buffer for get_request::buffer. Not only because you are not handling malloc()/realloc() failures at all, but also because manual memory management should be avoided in C in general. Use std::string or std::vector<char> instead, in which case you can eliminate get_request in this code altogether, eg:

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;
}
  • Related