2011-11-24 11 views
10

Chương trình C++ của tôi hiện đang gọi curl qua một đường ống (popen("curl ...")) để POST một tệp dữ liệu JSON đến một máy chủ web. Điều này có những hạn chế về hiệu suất rõ ràng do cần phải lưu JSON vào một tệp và gọi curl trong một subshell. Tôi muốn viết lại nó để sử dụng libcurl, nhưng nó không phải là rõ ràng với tôi làm thế nào để làm điều này. Các dòng lệnh tôi vượt qua để popen() là:Làm cách nào để POST một bộ đệm của JSON bằng cách sử dụng libcurl?

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

Các dữ liệu JSON (khoảng 3K) đang ngồi trong một bộ đệm trong bộ nhớ RAM trước khi tôi cần phải đăng nó. Tôi đã mong đợi sử dụng CURLOPT_READFUNCTION của libcurl để đệm bộ đệm vào libcurl (nhưng tôi mở để lựa chọn thay thế), và CURLOPT_WRITEFUNCTION để nắm bắt trả lời của máy chủ, tương tự như cách tôi đọc trả lời từ đường ống của popen.

Tất cả điều đó có vẻ đơn giản. Điều gì là khó hiểu là sự kết hợp của CURLOPT_POST, CURLOPT_HTTPPOST, CURLOPT_POSTFIELDS, CURLOPT_HTTPHEADER tôi cần. Tôi đã đọc nhiều bài viết về chủ đề này (không có ý định chơi chữ), và không có bài viết nào khớp chính xác với kịch bản của tôi. Bất kỳ đề xuất?

[Lưu ý rằng tôi thường không có bất kỳ biểu mẫu form URL mã hóa, như thế này: http: //server/handler.php I = làm & không = sử dụng & những = trong & tôi = truy vấn?]

Trả lời

9

có mã ví dụ cho điều này ở đây: 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

Điều này là hoàn hảo. Cảm ơn. –

+1

Không phải lo lắng. BTW, nếu bạn đang viết C++ bạn nên kiểm tra curlpp, đó là một wrapper cho thẳng C libcurl, và là một cách tốt đẹp hơn nhiều về việc: http://curlpp.org/index.php/examples/71- example-21 –

12

Bạn có thể sử dụng 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); 

CURLOPT_POSTFIELDS không sửa đổi tải trọng theo bất kỳ cách nào, rất thuận tiện cho việc gửi dữ liệu JSON. Cũng lưu ý rằng, khi cung cấp CURLOPT_POSTFIELDS, nó sẽ tự động bật CURLOPT_POST do đó không cần phải cung cấp CURLOPT_POST trong yêu cầu.

+0

Bạn có đăng một mảng JSON hoặc một chuỗi theo cùng một cách không? [1, "blah"] không phải là một biểu mẫu và không có cặp khóa-giá trị. Bạn có lẽ nên đề cập đến một cách rõ ràng nếu đây là trường hợp. – dmitri

2

Ngoài ra, bạn có thể sử dụng đầu vào RAW thay vì thêm backslashes thêm:

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

với dấu phân cách hoặc không có nó.

3

Điều gì về yêu cầu Content-Type tiêu đề để khớp với application/json giống như câu hỏi được yêu cầu?

Sử dụng CURLOPT_POSTFIELDS từ hai câu trả lời ở trên cũng như CURLOPT_POST, tự động Content-Type được đặt thành application/x-www-form-urlencoded.

Cách duy nhất để tôi đặt tiêu đề chính xác là thêm nội dung được nêu trong câu trả lời này: JSON requests in C using libcurl