使用libcurl和jsoncpp向django发送POST请求时,unicode无效



我在Django中使用REST -framework模块创建了一个REST端点;简单代码如下:

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)

我已经尝试过使用Firefox的RESTClient插件发送POST请求,并且可以验证它是按原样工作的。

然而,我的用例是我想在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: %sn",
            curl_easy_strerror(res));
        curl_easy_cleanup(curl);
    }
}

我从django服务器得到一个错误:

  File "C:Python27libsite-packagesrest_frameworkparsers.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

post请求不成功。我的理解是:

  • django-rest-framework期望一个utf-8编码的json字符串,
  • jsoncpp用utf-8编码字符串,
  • libcurl与编码无关,并且在字节级别处理数据。

所以我对此有点惊讶,不知道如何开始故障排除。有人能帮我弄清楚如何让我的c++应用程序和django应用程序一起工作吗?

谢谢!

根据CURLOPT_POSTFIELDS文档:

指向的数据不被库复制:因此,它必须由调用应用程序保存,直到相关的传输完成。可以通过设置CURLOPT_COPYPOSTFIELDS选项来改变这种行为(这样libcurl就会复制数据)。

临时 char*指针传递给CURLOPT_POSTFIELDS。这是因为Json::StyledWriter::write()返回一个临时 std::string,然后调用c_str()。当对curl_easy_setopt()的调用完成时,std::string被销毁,因此curl持有的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());
    

最新更新