使用 libcurl 和 jsoncpp 向 django 发送 POST 请求时无效的 unicode
Invalid unicode while sending a POST request to django with libcurl and jsoncpp
我已经使用 rest-framework 模块在 Django 中创建了一个 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: %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
并且 post 请求未成功。我的理解是:
- django-rest-framework 需要一个 utf-8 编码的 json 字符串,
- jsoncpp 以 utf-8 编码字符串,
- libcurl 与编码无关并在字节级别处理数据。
所以我对此感到有点惊讶,并且不确定如何开始进行故障排除。谁能帮我弄清楚如何让我的 C++ 应用程序和 Django 应用程序协同工作?
谢谢!
根据 CURLOPT_POSTFIELDS
documentation:
The data pointed to is NOT copied by the library: as a consequence, it must be preserved by the calling application until the associated transfer finishes. This behaviour can be changed (so libcurl does copy the data) by setting the CURLOPT_COPYPOSTFIELDS
option.
您正在将 临时 char*
指针传递给 CURLOPT_POSTFIELDS
。这是因为 Json::StyledWriter::write()
returns a temporary std::string
然后你调用 c_str()
。当对 curl_easy_setopt()
的调用完成时,std::string
被销毁,因此 curl 持有的 char*
指针不再有效。 Curl 最终从释放的内存中传输垃圾数据。这是未定义的行为,你很幸运你的代码没有完全崩溃。
因此,您需要:
在局部变量中保留 std::string
直到 curl_easy_perform()
完成:
std::string json = writer.write(submitted_data);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str());
使用CURLOPT_COPYPOSTFIELDS
代替CURLOPT_POSTFIELDS
:
curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, writer.write(submitted_data).c_str());
我已经使用 rest-framework 模块在 Django 中创建了一个 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: %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
并且 post 请求未成功。我的理解是:
- django-rest-framework 需要一个 utf-8 编码的 json 字符串,
- jsoncpp 以 utf-8 编码字符串,
- libcurl 与编码无关并在字节级别处理数据。
所以我对此感到有点惊讶,并且不确定如何开始进行故障排除。谁能帮我弄清楚如何让我的 C++ 应用程序和 Django 应用程序协同工作?
谢谢!
根据 CURLOPT_POSTFIELDS
documentation:
The data pointed to is NOT copied by the library: as a consequence, it must be preserved by the calling application until the associated transfer finishes. This behaviour can be changed (so libcurl does copy the data) by setting the
CURLOPT_COPYPOSTFIELDS
option.
您正在将 临时 char*
指针传递给 CURLOPT_POSTFIELDS
。这是因为 Json::StyledWriter::write()
returns a temporary std::string
然后你调用 c_str()
。当对 curl_easy_setopt()
的调用完成时,std::string
被销毁,因此 curl 持有的 char*
指针不再有效。 Curl 最终从释放的内存中传输垃圾数据。这是未定义的行为,你很幸运你的代码没有完全崩溃。
因此,您需要:
在局部变量中保留
std::string
直到curl_easy_perform()
完成:std::string json = writer.write(submitted_data); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json.c_str());
使用
CURLOPT_COPYPOSTFIELDS
代替CURLOPT_POSTFIELDS
:curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, writer.write(submitted_data).c_str());