【问题标题】:Invalid unicode while sending a POST request to django with libcurl and jsoncpp使用 libcurl 和 jsoncpp 向 django 发送 POST 请求时 unicode 无效
【发布时间】:2016-11-17 19:20:45
【问题描述】:

我在 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

序列化器.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: %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-framework 需要一个 utf-8 编码的 json 字符串,
  • jsoncpp 以 utf-8 编码字符串,并且
  • libcurl 与编码无关,在字节级别处理数据。

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

谢谢!

【问题讨论】:

  • 通过strace或其他方式运行代码来捕获网络流量。您可能会发现您的某个假设是错误的,并且您正在发出 ISO-8859-1 编码数据。
  • 嗯。我在 Windows 10 上运行该应用程序,但很失望地发现 Wireshark 似乎无法捕获环回流量。你对我可以在 Windows 上使用的东西有什么建议吗?
  • 您不必将请求发送到同一个 Windows 框。将其发送到其他 Windows 或 Linux 机器。
  • Loopback 流量不会通过任何网络接口流出,因此 Wireshark 无法捕获流量。但是,如果您定义一个本地网络路由,将环回流量从网络设备(如 LAN 路由器)反弹出去,那么它可以捕获该流量(然后您可以过滤掉重复的数据包)。
  • 假设这是问题所在,我将如何解决? JSONParser 类似乎没有采用任何编码参数;强制 jsoncpp 将其编码为 utf-8 还是编写我自己的自定义解析器更容易?

标签: c++ django django-rest-framework libcurl jsoncpp


【解决方案1】:

根据CURLOPT_POSTFIELDS documentation

库不会复制指向的数据:因此,调用应用程序必须保留它,直到相关的传输完成。可以通过设置 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());
    

【讨论】:

  • 在我的情况下,我试图向节点 js http 服务器发出请求,但我得到了同样的错误,所以我尝试了这个解决方案,现在它很好;非常感谢!
猜你喜欢
  • 2015-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-20
  • 2011-07-15
  • 1970-01-01
  • 2021-12-21
  • 2019-09-29
相关资源
最近更新 更多