2011-11-24 14 views
10

Mi programa C++ invoca actualmente curl a través de un conducto (popen("curl ...")) para POSTAR un archivo de datos JSON a un servidor web. Esto tiene limitaciones obvias de rendimiento debido a la necesidad de guardar el JSON en un archivo e invocar el curl en una subcadena. Me gustaría volver a escribirlo para usar libcurl, pero no tengo claro cómo hacerlo. La línea de comandos que pase a popen() es:¿Cómo PUESO un buffer de JSON usando libcurl?

curl -s -S -D /dev/null -H "Content-Type: application/json" -X POST -d file-of-json http://server/handler.php 

Los datos JSON (unos 3K) está sentado en un buffer en la memoria RAM antes de lo necesario publicarlo. Esperaba usar CURLOPT_READFUNCTION de libcurl para colocar el búfer en libcurl (pero estoy abierto a alternativas), y CURLOPT_WRITEFUNCTION para capturar la respuesta del servidor, de forma similar a cómo leí la respuesta de la tubería de popen.

Todo parece sencillo. Lo que es confuso es qué combinación de CURLOPT_POST, CURLOPT_HTTPPOST, CURLOPT_POSTFIELDS, CURLOPT_HTTPHEADER que necesito. He leído muchas publicaciones sobre este tema (sin juego de palabras), y ninguna coincide exactamente con mi escenario. ¿Alguna sugerencia?

[Tenga en cuenta que normalmente no tengo ninguna campos de formulario con codificación URL, como esto: http: //server/handler.php I = No & no utilizo = = & estos & en mi consulta =?]

Respuesta

9

No es código de ejemplo para esta aquí: http://curl.haxx.se/libcurl/c/post-callback.html

 

/*************************************************************************** 
*         _ _ ____ _ 
* Project      ___| | | | _ \| | 
*       /__| | | | |_) | | 
*       | (__| |_| | _ <| |___ 
*        \___|\___/|_| \_\_____| 
* 
* Copyright (C) 1998 - 2011, Daniel Stenberg, <[email protected]>, et al. 
* 
* This software is licensed as described in the file COPYING, which 
* you should have received as part of this distribution. The terms 
* are also available at http://curl.haxx.se/docs/copyright.html. 
* 
* You may opt to use, copy, modify, merge, publish, distribute and/or sell 
* copies of the Software, and permit persons to whom the Software is 
* furnished to do so, under the terms of the COPYING file. 
* 
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 
* KIND, either express or implied. 
* 
***************************************************************************/ 
/* An example source code that issues a HTTP POST and we provide the actual 
* data through a read callback. 
*/ 
#include 
#include 
#include 

const char data[]="this is what we post to the silly web server"; 

struct WriteThis { 
    const char *readptr; 
    int sizeleft; 
}; 

static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp) 
{ 
    struct WriteThis *pooh = (struct WriteThis *)userp; 

    if(size*nmemb sizeleft) { 
    *(char *)ptr = pooh->readptr[0]; /* copy one single byte */ 
    pooh->readptr++;     /* advance pointer */ 
    pooh->sizeleft--;    /* less data left */ 
    return 1;      /* we return 1 byte at a time! */ 
    } 

    return 0;       /* no more data left to deliver */ 
} 

int main(void) 
{ 
    CURL *curl; 
    CURLcode res; 

    struct WriteThis pooh; 

    pooh.readptr = data; 
    pooh.sizeleft = strlen(data); 

    curl = curl_easy_init(); 
    if(curl) { 
    /* First set the URL that is about to receive our POST. */ 
    curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/index.cgi"); 

    /* Now specify we want to POST data */ 
    curl_easy_setopt(curl, CURLOPT_POST, 1L); 

    /* we want to use our own read function */ 
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); 

    /* pointer to pass to our read function */ 
    curl_easy_setopt(curl, CURLOPT_READDATA, &pooh); 

    /* get verbose debug output please */ 
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); 

    /* 
     If you use POST to a HTTP 1.1 server, you can send data without knowing 
     the size before starting the POST if you use chunked encoding. You 
     enable this by adding a header like "Transfer-Encoding: chunked" with 
     CURLOPT_HTTPHEADER. With HTTP 1.0 or without chunked transfer, you must 
     specify the size in the request. 
    */ 
#ifdef USE_CHUNKED 
    { 
     struct curl_slist *chunk = NULL; 

     chunk = curl_slist_append(chunk, "Transfer-Encoding: chunked"); 
     res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); 
     /* use curl_slist_free_all() after the *perform() call to free this 
     list again */ 
    } 
#else 
    /* Set the expected POST size. If you want to POST large amounts of data, 
     consider CURLOPT_POSTFIELDSIZE_LARGE */ 
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (curl_off_t)pooh.sizeleft); 
#endif 

#ifdef DISABLE_EXPECT 
    /* 
     Using POST with HTTP 1.1 implies the use of a "Expect: 100-continue" 
     header. You can disable this header with CURLOPT_HTTPHEADER as usual. 
     NOTE: if you want chunked transfer too, you need to combine these two 
     since you can only set one list of headers with CURLOPT_HTTPHEADER. */ 

    /* A less good option would be to enforce HTTP 1.0, but that might also 
     have other implications. */ 
    { 
     struct curl_slist *chunk = NULL; 

     chunk = curl_slist_append(chunk, "Expect:"); 
     res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); 
     /* use curl_slist_free_all() after the *perform() call to free this 
     list again */ 
    } 
#endif 

    /* Perform the request, res will get the return code */ 
    res = curl_easy_perform(curl); 

    /* always cleanup */ 
    curl_easy_cleanup(curl); 
    } 
    return 0; 
} 
 
+0

Esto es perfecto. Gracias. –

+1

Sin preocupaciones. Por cierto, si estás escribiendo C++ deberías echarle un vistazo a curlpp, que es un envoltorio para C libcurl directo, y es una manera mucho más agradable de hacer las cosas: http://curlpp.org/index.php/examples/71- example-21 –

12

puede utilizar CURLOPT_POSTFIELDS:

CURL *curl = curl_easy_init(); 

curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/api/endpoint"); 
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "{\"hi\" : \"there\"}"); 

curl_easy_perform(curl); 

Dado que CURLOPT_POSTFIELDS no modifica la carga útil de ninguna manera, es muy conveniente para POSTING datos JSON. También tenga en cuenta que, cuando se suministra CURLOPT_POSTFIELDS, automáticamente habilita CURLOPT_POST por lo que no es necesario proporcionar CURLOPT_POST en la solicitud.

+0

¿Publicarías una matriz JSON o una cadena de la misma manera? [1, "blah"] no es una forma y no hay pares clave-valor. Probablemente deberías mencionar eso explícitamente si este es el caso. – dmitri

2

Además, es posible utilizar la entrada bruta en lugar de añadir barras invertidas adicionales:

curl_easy_setopt(curl, CURLOPT_POSTFIELDS, R"anydelim({"hi" : "there"})anydelim"); 

con delimitador o sin ella.

3

¿Qué pasa con el encabezado Content-Type requerido para que coincida con application/json al igual que el op está preguntando?

Usando los CURLOPT_POSTFIELDS de dos respuestas anteriores, así como CURLOPT_POST, la Content-Type automáticamente se establece en application/x-www-form-urlencoded.

La única manera para que consiga las cabeceras configuradas correctamente era añadir lo que se describe en esta respuesta: JSON requests in C using libcurl

Cuestiones relacionadas