web-dev-qa-db-ja.com

libcurlを使用したCでのJSONリクエスト

Cでlibcurlを使用して、JSONリクエスト本文でPUTリクエストを定義しています。

これは私がそれをしている方法です:

    sprintf(jsonObj, "\"name\" : \"%s\", \"age\" : \"%s\"", name, age);

    struct curl_slist *headers = NULL;
    curl_slist_append(headers, "Accept: application/json");
    curl_slist_append(headers, "Content-Type: application/json");
    curl_slist_append(headers, "charset: utf-8");

    curl_easy_setopt(curl, CURLOPT_URL, url);

    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); 
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonObj);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcrp/0.1");

    res = curl_easy_perform(curl);

リクエスト本文は次のように到着します。

    { '"name" : "Pedro", "age" : "22"' }

{ '開始時および' } 最後に。

- - より詳しい情報 - - -

このコードを宣言すると

    char* jsonObj = "{ \"name\" : \"Pedro\" , \"age\" : \"22\" }"; 

    struct curl_slist *headers = NULL;
    curl_slist_append(headers, "Accept: application/json");
    curl_slist_append(headers, "Content-Type: application/json");
    curl_slist_append(headers, "charset: utf-8");

    curl_easy_setopt(curl, CURLOPT_URL, url);

    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); 
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonObj);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcrp/0.1");

    res = curl_easy_perform(curl);

サーバーはこれをリクエスト本文として受け取ります。

{ '{ "name" : "Pedro" , "age" : "22" }': '' }

私の質問は:

LibCurlは自動的にJsonリクエストを事前フォーマット/エンコードしていますか?

ちなみに、libCurlにはJSONオブジェクトをエンコードする方法がありますか?

本当にありがとう!

11

問題はヘッダーにある可能性があります。 curl_slistヘッダーを構成するときは、curl_slist_appendの出力をヘッダーに割り当てる必要があると思います。

struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, "charset: utf-8");
18
JustinB

まず、いくつかのことに注意しましょう。まず、Daniel Stenbergは正しいです(彼がコードを書いたことを考えると、彼がそうなることを願っています):libcurlはコードにデータを追加しません。このサンプルプログラムでこれを実証できます。これはあなたの例に似ていますが、いくつかの追加のセットアップ/分解コードがあります。これはコードのallです:ここには他に何もありません:

#include <curl/curl.h>

int main (int argc, char *argv[]) {
    CURL *curl;
    CURLcode res;

    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    if (curl == NULL) {
        return 128;
    }

    char* jsonObj = "{ \"name\" : \"Pedro\" , \"age\" : \"22\" }";

    struct curl_slist *headers = NULL;
    curl_slist_append(headers, "Accept: application/json");
    curl_slist_append(headers, "Content-Type: application/json");
    curl_slist_append(headers, "charset: utf-8");

    curl_easy_setopt(curl, CURLOPT_URL, "http://http2bin.org/put");

    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonObj);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcrp/0.1");

    res = curl_easy_perform(curl);

    curl_easy_cleanup(curl);
    curl_global_cleanup();
    return res;
}

Wiresharkでリクエストをキャプチャするパケットは、これが次のHTTPリクエストを発行することを示しています。

PUT /put HTTP/1.1
Host: http2bin.org
User-Agent: libcrp/0.1
Accept: */*
Content-Length: 35
Content-Type: application/x-www-form-urlencoded

{ "name" : "Pedro" , "age" : "22" }

ご覧のとおり、これはまさに送信を要求したJSONデータです。ここには追加のデータはなく、中括弧はありません。

これは、余分な中括弧がサーバーまたは中間ミドルボックスのいずれかによって追加されていることを意味します。私の推測では、サーバーはapplication/json本体以外の本体をに強制的に変換しようとしているため、サーバーがそれを追加していると思います。全身がひも。

サーバーがこれをJSON本体と見なさない理由は、ここで別の回答によってカプセル化されています。ヘッダーが適切に設定されていません。 curl_slist_appendreturnsheaders変数に割り当てる必要のある新しいstruct curl_slist *。つまり、次の4行を変更する必要があります。

struct curl_slist *headers = NULL;
curl_slist_append(headers, "Accept: application/json");
curl_slist_append(headers, "Content-Type: application/json");
curl_slist_append(headers, "charset: utf-8");

これらの4つに:

struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Accept: application/json");
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, "charset: utf-8");

これにより、JSONデータを送信していることをサーバーに納得させることができます。

将来的には、このような問題を解決するためにWiresharkに精通することをお勧めします。送信した実際のリクエストを確認すると非常に役立ちます。それができない場合は、CURLOPT_DEBUGFUNCTIONを使用して、curlが送信しているデータを取得して検証できます。

12
Lukasa

libcurlは正確に送信を要求したバイトを送信します。 JSONの知識はまったくありません。

より良い詳細については、@ Lukasaの優れたより精巧な回答を参照してください。

3
Daniel Stenberg

私も同様の問題を抱えていました。

Curlコマンドの-libcurlオプションを発見しました。それは大いに役立ちました!作業中のcurlコマンドの最後に追加するだけです。

結局、それは私がこのコードを作成するのを助けます:

  CURLcode ret;
  CURL *hnd;
  struct curl_slist *slist1;
  std::string jsonstr = "{\"username\":\"bob\",\"password\":\"12345\"}";

  slist1 = NULL;
  slist1 = curl_slist_append(slist1, "Content-Type: application/json");

  hnd = curl_easy_init();
  curl_easy_setopt(hnd, CURLOPT_URL, "http://u/r/l");
  curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
  curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, jsonstr.c_str());
  curl_easy_setopt(hnd, CURLOPT_USERAGENT, "curl/7.38.0");
  curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, slist1);
  curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
  curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
  curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);

  ret = curl_easy_perform(hnd);

  curl_easy_cleanup(hnd);
  hnd = NULL;
  curl_slist_free_all(slist1);
  slist1 = NULL;

ExpressはこのJSONを次のように受け取ります。

{ username: 'bob', password: '12345' }

これがお役に立てば幸いです。

2
Rahim Khoja

システムが正しく動作しているかどうかを理解するための重要な部分は、プログラムが実際にネットワークを介して送信しているものを確認することです。したがって、バイトストリームをサーバーに向ける(および/またはWiresharkを実行する)代わりに、バイトストリームをチェックする別の方法は、テストマシンの別のウィンドウで netcat インスタンスを実行することです。

nc -l 8080

コード(CURLOPT_URL)を「 http:// localhost:808 "」にポイントします。

あなたは打つ必要があります Control-Dncウィンドウで接続を終了して、curlが完了するようにしますが、事前にテストリターンテキストを入力することもできます。これは、テストに対する何らかの応答が必要な場合に役立ちます。

1
Pierz

Jsonパーサーを使用してnode-redにデータを投稿するときに、これと同じ問題が発生しました。
私にとっての解決策は、文字列をHTMLとして扱うことでした

#include <curl/curl.h>

int main (int argc, char *argv[]) {
    CURL *curl;
    CURLcode res;

    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    if (curl == NULL) {
        return 128;
    }
    struct curl_slist *headers = NULL;
    curl_slist_append(headers, "Content-Type: application/json");
    curl_easy_setopt(curl, CURLOPT_URL, #yourURL);
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "age=42&sex=male");

    res = curl_easy_perform(curl);

    curl_easy_cleanup(curl);
    curl_global_cleanup();
    return res;
}

誰かがそれが役立つことを願っています。

0
smroz