【问题标题】:a raw libcurl JSON PUT request using C使用 C 的原始 libcurl JSON PUT 请求
【发布时间】:2014-06-05 21:37:01
【问题描述】:

我目前正在编写一个类似 REST 的客户端,只需要执行 PUT 请求。

问题:
运行程序在 URL 的 API 上没有给我正确的结果,我不知道为什么。

使用 curl_easy_perform(curl) 在调用时不会引发错误。但在 URL 的 API 上没有生成预期的结果。

使用 curl_easy_send(curl,..,..,..) 会引发:不支持的协议错误

假设:
我假设我使用 curl_easy_opts 的顺序有问题?我什至错过了几条关键线?

我一直在这里阅读其他人如何执行 PUT 请求并一直在使用他们的方法。

计划摘要:

我的程序提示用户输入一些字符串/字符数据,然后我自己构建字符串,例如标题和有效负载。标头和有效负载均采用 JSON 格式,但有效负载只是一个字符串(在本例中为 char *str = (char *)mallo.. 等)。头部是如何构造的如下所示。

我的标题正在使用

构建
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Accept: application/json");
//there is more content being appended to the header

CURL 函数调用:

    //init winsock stuff
    curl_global_init(CURL_GLOBAL_ALL);

    //get a curl handle
    curl = curl_easy_init();

if(curl){
    //append the headers
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

    //specify the target URL
    curl_easy_setopt(curl, CURLOPT_URL, url);

    //connect ( //i added this here since curl_easy_send() says it requires it. )
    curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY,1L); 

    //specify the request (PUT in our case)
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");

    //append the payload
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload);

    res = curl_easy_perform(curl);
    //res = curl_easy_send(curl, payload, strlen(payload),&iolen);

    //check for errors
    if(res != CURLE_OK)
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));

    curl_easy_cleanup(curl);
}

【问题讨论】:

    标签: c json curl put


    【解决方案1】:

    您不应使用 CURLOPT_CONNECT_ONLY 选项或 curl_easy_send() 函数,它们旨在用于自定义的非 HTTP 协议。

    有关如何使用 libcurl 执行 PUT 请求的示例,请参阅 this page。基本上,您希望启用 CURLOPT_UPLOADCURLOPT_PUT 选项来表示您正在执行 PUT 请求并启用使用请求上传正文,然后设置 CURLOPT_READDATACURLOPT_INFILESIZE_LARGE 选项来告诉libcurl 如何读取你上传的数据以及数据有多大。

    在你的情况下,如果你已经在内存中有数据,那么你不需要从文件中读取它,你可以在你的读取回调中memcpy()它。

    下面复制的示例代码:

    /***************************************************************************
     *                                  _   _ ____  _
     *  Project                     ___| | | |  _ \| |
     *                             / __| | | | |_) | |
     *                            | (__| |_| |  _ <| |___
     *                             \___|\___/|_| \_\_____|
     *
     * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, 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.
     *
     ***************************************************************************/ 
    #include <stdio.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <curl/curl.h>
    
    /*
     * This example shows a HTTP PUT operation. PUTs a file given as a command
     * line argument to the URL also given on the command line.
     *
     * This example also uses its own read callback.
     *
     * Here's an article on how to setup a PUT handler for Apache:
     * http://www.apacheweek.com/features/put
     */ 
    
    static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
    {
      size_t retcode;
      curl_off_t nread;
    
      /* in real-world cases, this would probably get this data differently
         as this fread() stuff is exactly what the library already would do
         by default internally */ 
      retcode = fread(ptr, size, nmemb, stream);
    
      nread = (curl_off_t)retcode;
    
      fprintf(stderr, "*** We read %" CURL_FORMAT_CURL_OFF_T
              " bytes from file\n", nread);
    
      return retcode;
    }
    
    int main(int argc, char **argv)
    {
      CURL *curl;
      CURLcode res;
      FILE * hd_src ;
      struct stat file_info;
    
      char *file;
      char *url;
    
      if(argc < 3)
        return 1;
    
      file= argv[1];
      url = argv[2];
    
      /* get the file size of the local file */ 
      stat(file, &file_info);
    
      /* get a FILE * of the same file, could also be made with
         fdopen() from the previous descriptor, but hey this is just
         an example! */ 
      hd_src = fopen(file, "rb");
    
      /* In windows, this will init the winsock stuff */ 
      curl_global_init(CURL_GLOBAL_ALL);
    
      /* get a curl handle */ 
      curl = curl_easy_init();
      if(curl) {
        /* we want to use our own read function */ 
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
    
        /* enable uploading */ 
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    
        /* HTTP PUT please */ 
        curl_easy_setopt(curl, CURLOPT_PUT, 1L);
    
        /* specify target URL, and note that this URL should include a file
           name, not only a directory */ 
        curl_easy_setopt(curl, CURLOPT_URL, url);
    
        /* now specify which file to upload */ 
        curl_easy_setopt(curl, CURLOPT_READDATA, hd_src);
    
        /* provide the size of the upload, we specicially typecast the value
           to curl_off_t since we must be sure to use the correct data size */ 
        curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE,
                         (curl_off_t)file_info.st_size);
    
        /* Now run off and do what you've been told! */ 
        res = curl_easy_perform(curl);
        /* Check for errors */ 
        if(res != CURLE_OK)
          fprintf(stderr, "curl_easy_perform() failed: %s\n",
                  curl_easy_strerror(res));
    
        /* always cleanup */ 
        curl_easy_cleanup(curl);
      }
      fclose(hd_src); /* close the local file */ 
    
      curl_global_cleanup();
      return 0;
    }
    

    【讨论】:

    • 我没有使用 libcurl 网页中的示例的原因是因为我没有上传文件,我正在上传原始数据。 read_callback 函数究竟做了什么?它只是显示 libcurl 正在读取的内容吗?另外,当您说“允许通过请求上传正文”时,是否有可用的选项来执行此操作? - 另外,您在 read_callback 中说 memcpy(),但是由于我已经有了指向内存的指针,我会将内存复制到什么位置?
    • @mrJTparadise:read_callback 只是 curl 在需要知道要上传的数据时调用的函数,它不需要来自文件。如果您已经将数据加载到内存中,那么您的 read_callback() 可以非常简单,例如 { memcpy(ptr, my_payload, size*nmemb); return nmemb; }
    • 非常感谢您的回复和建议,帮助我理解这些功能。我明天要试试这个,让你知道它是怎么回事。还将使用解决方案更新帖子。
    • 我对这个read_callback 函数感到很困惑。我需要以某种方式将我的有效负载传递给read_callback 函数,对吗?在显示的示例中,他们正在调用 curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback) 内的 read_callback 函数。他们没有向函数传递任何参数,所以read_callback() 如何查看文件?就我而言,我需要将我的有效载荷传递给它,对吗?
    • 我读到的关于CURLOPT_READDATA Data pointer to pass to the file read function. If you use the CURLOPT_READFUNCTION option, this is the pointer you'll get as input. If you don't specify a read callback but instead rely on the default internal read function, this data must be a valid readable FILE * (cast to 'void *'). 的另一件事是说参数必须是指向文件的指针。在我的代码中,我有 curl_easy_setopt(curl, CURLOPT_READDATA, payload); 这足够了吗? - 有效载荷只是一个字符*
    【解决方案2】:

    我同意,不要使用 CUSTOMREQUEST。我在这里看到的与 PUT 和 CURL 相关的每个细节都遗漏了一个细节,即您需要设置文件大小,否则您将收到 HTTP 错误 411。 为此使用 CURLOPT_INFILESIZE 或 CURLOPT_INFILESIZE_LARGE。 在此处查看更多详细信息:

    How do I send long PUT data in libcurl without using file pointers?

    【讨论】:

      【解决方案3】:

      我知道这是一个非常古老的问题,但如果有人想将 libcurl 与 GLib 和 json-glib 一起使用来发送带有 PUT 请求的 JSON。 下面的代码对我有用:

          #include <curl/curl.h>
          #include <json-glib/json-glib.h>
      
          //this is callback function for CURLOPT_READFUNCTION: 
      
          static size_t
          curlPutJson ( void *ptr, size_t size, size_t nmemb, void *_putData )
          {
                  GString *putData = ( GString * ) _putData;
                  size_t realsize = ( size_t ) putData->len;
                  memcpy ( ptr, putData->str, realsize );
                  return realsize;
          }
      
         /*now inside main or other function*/
      
         //json_to_string ( jsonNode, FALSE ) is from json-glib to stringify JSON
         //created in jsonNode
      
         GString *putData = g_string_new ( json_to_string ( mainNode, FALSE ) );
      
         //now goes curl as usual: headers, url, other options and so on
         //and 4 most important lines
      
         curl_easy_setopt ( curl, CURLOPT_READFUNCTION, curlPutJson );
         curl_easy_setopt ( curl, CURLOPT_UPLOAD, 1L );
      
         curl_easy_setopt ( curl, CURLOPT_READDATA, putData );        //GString
         curl_easy_setopt ( curl, CURLOPT_INFILESIZE, putData->len ); //type long     
      

      【讨论】:

      • 很好的答案,尤其是CURLOPT_PUT 现在已被弃用。
      猜你喜欢
      • 2012-08-12
      • 2017-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多