2016-11-17 88 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服务器发出请求,但我得到了同样的错误,所以我尝试了这个解决方案,现在它很好;非常感谢! –