2016-11-17 117 views
0

我使用rest-framework模塊在Django中創建了REST端點;簡單的代碼放在如下:使用libcurl和jsoncpp向django發送POST請求時發送無效的unicode

models.py

class Data(models.Model): 
    name = models.CharField(max_length=256) 
    description = models.TextField() 

    def __str__(self): 
     return self.name 

serializers.py

class DataSerializer(serializers.ModelSerializer): 
    class Meta: 
     model = Data 
     fields = ('name', 'description') 

views.py

def data_list(request): 
    """ 
    List all data. 
    """ 
    if request.method == 'GET': 
     categories = Data.objects.all() 
     serializer = DataSerializer(categories, many=True) 
     return JSONResponse(serializer.data) 

    elif request.method == 'POST': 
     data = JSONParser().parse(request) 
     serializer = DataSerializer(data=data) 
     if serializer.is_valid(): 
      serializer.save() 
      return JSONResponse(serializer.data, status=201) 
     return JSONResponse(serializer.errors, status=400) 

我試圖發送使用POST請求用於Firefox的RESTClient插件,並且可以驗證它是否按原樣工作。

但是,我的用例是我想在C++應用程序中使用libcurl寫入數據庫。

如果我使用jsoncpp創建一個JSON對象,然後使用libcurl的做一個POST請求,如下:

void main() { 
    Json::Value submitted_data; 
    submitted_data["name"] = "data id"; 
    submitted_data["description"] = "data description"; 
    Json::StyledWriter writer; 

    CURL *curl; 
    CURLcode res; 

    curl_global_init(CURL_GLOBAL_ALL); 

    curl = curl_easy_init(); 
    if (curl) { 
     curl_easy_setopt(curl, CURLOPT_URL, "http://127.0.0.1:8000/data/"); 
     struct curl_slist *headers = NULL; 
     headers = curl_slist_append(headers, "Content-Type: application/json; charset=UTF-8"); 
     curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); 
     curl_easy_setopt(curl, CURLOPT_POST, 1); 
     curl_easy_setopt(curl, CURLOPT_POSTFIELDS, writer.write(submitted_data).c_str()); 

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

     curl_easy_cleanup(curl); 
    } 
} 

我得到一個錯誤,從Django的服務器:

File "C:\Python27\lib\site-packages\rest_framework\parsers.py", line 67, in parse 
    raise ParseError('JSON parse error - %s' % six.text_type(exc)) 
ParseError: JSON parse error - 'utf8' codec can't decode byte 0xdd in position 0: invalid continuation byte 

並且發佈請求不成功。我的理解是:

  • Django的REST的架構需要一個UTF-8編碼的JSON字符串,
  • jsoncpp以UTF-8編碼字符串,
  • 的libcurl是不可知的關於編碼的,並處理數據,在字節級別。

所以我對此有點驚訝,不知道如何開始故障排除。有人能幫我弄清楚如何讓我的C++應用程序和django應用程序一起工作嗎?

謝謝!

+0

通過'strace'或其他方式運行代碼來捕獲網絡流量。你可能會發現你的一個假設是錯誤的,並且你正在發佈ISO-8859-1編碼數據。 –

+0

嗯。我在Windows 10上運行該應用程序,並且很失望地發現Wireshark似乎無法捕獲環迴流量。你有什麼建議可以在Windows上使用嗎? –

+0

您不必將請求發送到同一Windows窗口。將它發送到其他Windows或Linux機器。 –

回答

2

CURLOPT_POSTFIELDS documentation

數據指出,不被複制庫:作爲一個後果,必須由調用應用程序,直到相關的傳輸完成保存。通過設置CURLOPT_COPYPOSTFIELDS選項,可以更改此行爲(所以libcurl會複製數據)。

你傳遞一個臨時char*指針CURLOPT_POSTFIELDS。這是因爲Json::StyledWriter::write()返回臨時std::string您然後打電話c_str()。當curl_easy_setopt()的調用完成時,該std::string被破壞,因此捲曲所持有的char*指針不再有效。 Curl最終從釋放的內存中傳輸垃圾數據。這是未定義的行爲,你很幸運,你的代碼不會完全崩潰。

所以,你需要:

  1. 保留std::string在一個局部變量,直到curl_easy_perform()完成:

    std::string json = writer.write(submitted_data); 
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str()); 
    
  2. 使用CURLOPT_COPYPOSTFIELDS代替CURLOPT_POSTFIELDS

    curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, writer.write(submitted_data).c_str()); 
    
+0

這似乎解決了它;非常感謝! –

+0

在我的情況下,我試圖向一個節點js http服務器發出請求,但我得到了同樣的錯誤,所以我嘗試了這個解決方案,現在它很好;非常感謝! –