Flutter http MultipartRequest字段不能是汉字?
Flutter http MultipartRequest field can not be Chinese Charaters?
我尝试 post 将表单发送到服务器,代码如下:
ar request = new http.MultipartRequest("POST", _uri);
request.fields['user_acc'] = _userAcc;
// this issue should be solve
request.fields['user_nick_name'] = '中文名字';
request.fields['user_password'] = _password;
但是服务器端的user_nick_name字段总是空的,注意是总是,但是我把它改成英文服务器可以接收。我在postman上测试,服务器可以正确获取中文,所以这个问题是MultipartRequest issue。
我的问题是:为什么 Dart 或 Flutter 团队对这个如此重要的基础库如此粗心? 他们甚至没有考虑过这个简单的问题。我在 github 上提出了一个问题,但得到了 no-one 的回应,我认为团队已经完成了。所以我在这里问开发社区,无论如何如何解决这个问题?
[更新]
正如好心人的建议,我现在更新我的golang服务器,如果其他人遇到这个问题,你可能也不会回答和建议。
func HandleUserRegister(context *gin.Context) {
userAcc := context.PostForm("user_acc")
userAvatar := context.PostForm("user_avatar")
userNickName := context.PostForm("user_nick_name")
userPassword := context.PostForm("user_password")
userPhone := context.PostForm("user_phone")
userEmail := context.PostForm("user_email")
userGender := context.PostForm("user_gender")
userSign := context.PostForm("user_sign")
userType := context.PostForm("user_type")
userTypeInt, _ := strconv.Atoi(userType)
log.Infof("userAcc: %s, userNickName: %s, userPassword: %s", userAcc, userNickName, userPassword)}
这个是基于gin的,这个函数就是api求解器。如果有人想帮忙,请帮我解决一下。
好的!我现在更新问题,因为它真的很奇怪!。我做了那些测试:
- Post multiform 通过 Flutter 到 Django 服务器,正确接收中文文件;
- Post multiform data via Postman,golang(gin)服务器正确获取中文;
- Post通过Flutter到golang(gin)服务器获取多格式数据中文字段为null;
有关更多详细信息,我从我的服务器记录 headers postman(正常)和 flutter(异常):
Post人:
request header: map[Content-Type:[multipart/form-data; boundary=--------------------------022341683711652813100488] Postman-Token:[855646d7-5bea-4b8f-b8df-81366226cd49] User-Agent:[PostmanRuntime/7.1.1] Content-Length:[422] Connection:[keep-alive] Cache-Control:[no-cache] Accept:[*/*] Accept-Encoding:[gzip, deflate]]
颤动:
request header: map[User-Agent:[Dart/2.0 (dart:io)] Content-Type:[multipart/form-data; boundary=dart-http-boundary-.XUeYeqXpg4Yfyh8QhH1T5JB4zi_f3WxX9t7Taxhw91EFqhyki4] Accept-Encoding:[gzip] Content-Length:[574]]
有没有人能注意到其中的区别并告诉我如何更改它使服务器可以接收汉字?
我通过发布到 httpbin 进行了测试,响应表明字符发布正确:
"user_nick_name":"\u4e2d\u6587\u540d\u5b57"
我尝试使用 Flutter 的稳定版 v1 SDK 和 v2 SDK。问题可能出在服务器上吗?您是否尝试过使用 Fiddler 之类的工具来捕获实际发送的内容?
编辑: 我的猜测是您的服务器端代码没有正确读取数据作为 MultipartForm 数据(例如,您应该使用 ParseMultipartForm
并从 MultipartForm
).
@DannyTuppeny 是正确的。这是服务器问题。
当要求将 non-ASCII 字段包含到 multi-part 请求中时,Dart 库使用二进制 content-transfer-encoding.
正确地包装了它
String _headerForField(String name, String value) {
var header =
'content-disposition: form-data; name="${_browserEncode(name)}"';
if (!isPlainAscii(value)) {
header = '$header\r\n'
'content-type: text/plain; charset=utf-8\r\n'
'content-transfer-encoding: binary';
}
return '$header\r\n\r\n';
}
(邮递员不会,只是发送没有任何 headers 的 utf8 编码字符串。)
Dart/ASCII 看起来像这样:
--dart-http-boundary-HjDS88CmQicdgd8VaHSwPqJK8iR4H6rTG3LovSZy-QXGpU7pAB0
content-disposition: form-data; name="test"
stackover
--dart-http-boundary-HjDS88CmQicdgd8VaHSwPqJK8iR4H6rTG3LovSZy-QXGpU7pAB0
Dart/non-ASCII 看起来像这样:
First boundary: --dart-http-boundary-58NU6u6_Fo22xjH8H7yPCtKuoKgB+A8+RTJ82iIK1gs3nnGMLlp\r\n
Encapsulated multipart part: (text/plain)
content-disposition: form-data; name="test"\r\n
content-type: text/plain; charset=utf-8\r\n
content-transfer-encoding: binary\r\n\r\n
Line-based text data: text/plain
405667505557
Boundary: \r\n--dart-http-boundary-58NU6u6_Fo22xjH8H7yPCtKuoKgB+A8+RTJ82iIK1gs3nnGMLlp\r\n
所以问题是服务器无法从封装中解包值。
编辑
这是我昨天捕获的 Postman 踪迹。它是 multi-form,但未能添加 content-type-encoding header,尽管该字段是 non-ASCII。
MIME Multipart Media Encapsulation, Type: multipart/form-data, Boundary: "--------------------------595246000077585285134204"
[Type: multipart/form-data]
First boundary: ----------------------------595246000077585285134204\r\n
Encapsulated multipart part:
Content-Disposition: form-data; name="name"\r\n\r\n
Data (12 bytes)
0000 e4 b8 ad e6 96 87 e5 90 8d e5 ad 97 ............
Data: e4b8ade69687e5908de5ad97
[Length: 12]
Last boundary: \r\n----------------------------595246000077585285134204--\r\n
问题似乎出在 multipart
的 formdata.go
部分。 Go 假定任何带有 Content-Type
header 的多部分都是一个文件(而不是一个字段)。但是,知道这一点后,您可以按如下方式更改服务器代码:
func main() {
r := gin.Default()
r.POST("/sotest", func(c *gin.Context) {
formValue := c.PostForm("form_value")
if formValue == "" {
formFile, _ := c.FormFile("form_value")
file, _ := formFile.Open()
b1 := make([]byte, formFile.Size)
file.Read(b1)
formValue = string(b1)
}
c.JSON(200, gin.H{
"status": "posted",
"formValue": formValue,
})
})
r.Run() // listen and serve on 0.0.0.0:8080
}
当你检测到 PostForm
returns 为空字符串时,你知道 Go 已经将该字段视为文件,在这种情况下你可以打开并读取 'file' 和将其解码为我们所知道的 utf-8 字符串。显然,您可以将 "try as PostForm and if that's empty, try as FormFile" 测试封装到一个函数中。
如果您不想在服务器上测试空字符串,您可以将您的 Dart 结束代码更改为始终使用 utf-8 编码甚至 non-ascii 个字符串
request.files.add(
new http.MultipartFile.fromBytes(
'some_form_value_name',
utf8.encode('the string value'),
contentType: new MediaType('text', 'plain', {'charset': 'utf-8'}),
),
);
并使用 Open/Read/string 方法在服务器上读取它们。
我现在已经解决了这个问题。感谢理查德和丹尼的帮助。
1。这样做的原因
不管发生什么,但这真的不仅仅是一个方面的问题,我们不能说它是 Flutter 或 Go wrong。但是组合,Flutter + Go 服务器可能会遇到这个问题。后面的原因我还是没确定,但一定是脑袋不对(post人能做到);
2。解决方案
我们不仅要知道为什么,还要知道如何解决。这是我的工作:
不要使用官方http包。使用 dio
,这是 Dart 的扩展包。 link: https://pub.dartlang.org/packages/dio
它更简洁易用,所以我的代码变成:
FormData _formData = new FormData.from({
"user_acc": _userAcc,
"user_nick_name": _userNickName,
'user_password': _password,
});
Dio dio = new Dio();
Response response = await dio.post(usersUrl, data: _formData);
print(response.data);
我不会post none-英文单词现在:
INFO[0668] userAcc: ww, userNickName: 小鹿叮叮婴儿湿巾手口专用80抽湿纸巾婴儿湿巾婴儿100抽带盖批发【原价】34.90元【券后】9.9元【省】25元【复制此信息打开手机淘宝即可查看并下单】¥Tnsx0E77pFs¥【必买理由】新品预售80抽*3仙女联盟,更多优惠fd.loliloli.pro , userPassword: ww
INFO[0671] 用户存在。
我尝试 post 将表单发送到服务器,代码如下:
ar request = new http.MultipartRequest("POST", _uri);
request.fields['user_acc'] = _userAcc;
// this issue should be solve
request.fields['user_nick_name'] = '中文名字';
request.fields['user_password'] = _password;
但是服务器端的user_nick_name字段总是空的,注意是总是,但是我把它改成英文服务器可以接收。我在postman上测试,服务器可以正确获取中文,所以这个问题是MultipartRequest issue。
我的问题是:为什么 Dart 或 Flutter 团队对这个如此重要的基础库如此粗心? 他们甚至没有考虑过这个简单的问题。我在 github 上提出了一个问题,但得到了 no-one 的回应,我认为团队已经完成了。所以我在这里问开发社区,无论如何如何解决这个问题?
[更新] 正如好心人的建议,我现在更新我的golang服务器,如果其他人遇到这个问题,你可能也不会回答和建议。
func HandleUserRegister(context *gin.Context) {
userAcc := context.PostForm("user_acc")
userAvatar := context.PostForm("user_avatar")
userNickName := context.PostForm("user_nick_name")
userPassword := context.PostForm("user_password")
userPhone := context.PostForm("user_phone")
userEmail := context.PostForm("user_email")
userGender := context.PostForm("user_gender")
userSign := context.PostForm("user_sign")
userType := context.PostForm("user_type")
userTypeInt, _ := strconv.Atoi(userType)
log.Infof("userAcc: %s, userNickName: %s, userPassword: %s", userAcc, userNickName, userPassword)}
这个是基于gin的,这个函数就是api求解器。如果有人想帮忙,请帮我解决一下。
好的!我现在更新问题,因为它真的很奇怪!。我做了那些测试:
- Post multiform 通过 Flutter 到 Django 服务器,正确接收中文文件;
- Post multiform data via Postman,golang(gin)服务器正确获取中文;
- Post通过Flutter到golang(gin)服务器获取多格式数据中文字段为null;
有关更多详细信息,我从我的服务器记录 headers postman(正常)和 flutter(异常):
Post人:
request header: map[Content-Type:[multipart/form-data; boundary=--------------------------022341683711652813100488] Postman-Token:[855646d7-5bea-4b8f-b8df-81366226cd49] User-Agent:[PostmanRuntime/7.1.1] Content-Length:[422] Connection:[keep-alive] Cache-Control:[no-cache] Accept:[*/*] Accept-Encoding:[gzip, deflate]]
颤动:
request header: map[User-Agent:[Dart/2.0 (dart:io)] Content-Type:[multipart/form-data; boundary=dart-http-boundary-.XUeYeqXpg4Yfyh8QhH1T5JB4zi_f3WxX9t7Taxhw91EFqhyki4] Accept-Encoding:[gzip] Content-Length:[574]]
有没有人能注意到其中的区别并告诉我如何更改它使服务器可以接收汉字?
我通过发布到 httpbin 进行了测试,响应表明字符发布正确:
"user_nick_name":"\u4e2d\u6587\u540d\u5b57"
我尝试使用 Flutter 的稳定版 v1 SDK 和 v2 SDK。问题可能出在服务器上吗?您是否尝试过使用 Fiddler 之类的工具来捕获实际发送的内容?
编辑: 我的猜测是您的服务器端代码没有正确读取数据作为 MultipartForm 数据(例如,您应该使用 ParseMultipartForm
并从 MultipartForm
).
@DannyTuppeny 是正确的。这是服务器问题。
当要求将 non-ASCII 字段包含到 multi-part 请求中时,Dart 库使用二进制 content-transfer-encoding.
正确地包装了它 String _headerForField(String name, String value) {
var header =
'content-disposition: form-data; name="${_browserEncode(name)}"';
if (!isPlainAscii(value)) {
header = '$header\r\n'
'content-type: text/plain; charset=utf-8\r\n'
'content-transfer-encoding: binary';
}
return '$header\r\n\r\n';
}
(邮递员不会,只是发送没有任何 headers 的 utf8 编码字符串。)
Dart/ASCII 看起来像这样:
--dart-http-boundary-HjDS88CmQicdgd8VaHSwPqJK8iR4H6rTG3LovSZy-QXGpU7pAB0
content-disposition: form-data; name="test"
stackover
--dart-http-boundary-HjDS88CmQicdgd8VaHSwPqJK8iR4H6rTG3LovSZy-QXGpU7pAB0
Dart/non-ASCII 看起来像这样:
First boundary: --dart-http-boundary-58NU6u6_Fo22xjH8H7yPCtKuoKgB+A8+RTJ82iIK1gs3nnGMLlp\r\n
Encapsulated multipart part: (text/plain)
content-disposition: form-data; name="test"\r\n
content-type: text/plain; charset=utf-8\r\n
content-transfer-encoding: binary\r\n\r\n
Line-based text data: text/plain
405667505557
Boundary: \r\n--dart-http-boundary-58NU6u6_Fo22xjH8H7yPCtKuoKgB+A8+RTJ82iIK1gs3nnGMLlp\r\n
所以问题是服务器无法从封装中解包值。
编辑 这是我昨天捕获的 Postman 踪迹。它是 multi-form,但未能添加 content-type-encoding header,尽管该字段是 non-ASCII。
MIME Multipart Media Encapsulation, Type: multipart/form-data, Boundary: "--------------------------595246000077585285134204"
[Type: multipart/form-data]
First boundary: ----------------------------595246000077585285134204\r\n
Encapsulated multipart part:
Content-Disposition: form-data; name="name"\r\n\r\n
Data (12 bytes)
0000 e4 b8 ad e6 96 87 e5 90 8d e5 ad 97 ............
Data: e4b8ade69687e5908de5ad97
[Length: 12]
Last boundary: \r\n----------------------------595246000077585285134204--\r\n
问题似乎出在 multipart
的 formdata.go
部分。 Go 假定任何带有 Content-Type
header 的多部分都是一个文件(而不是一个字段)。但是,知道这一点后,您可以按如下方式更改服务器代码:
func main() {
r := gin.Default()
r.POST("/sotest", func(c *gin.Context) {
formValue := c.PostForm("form_value")
if formValue == "" {
formFile, _ := c.FormFile("form_value")
file, _ := formFile.Open()
b1 := make([]byte, formFile.Size)
file.Read(b1)
formValue = string(b1)
}
c.JSON(200, gin.H{
"status": "posted",
"formValue": formValue,
})
})
r.Run() // listen and serve on 0.0.0.0:8080
}
当你检测到 PostForm
returns 为空字符串时,你知道 Go 已经将该字段视为文件,在这种情况下你可以打开并读取 'file' 和将其解码为我们所知道的 utf-8 字符串。显然,您可以将 "try as PostForm and if that's empty, try as FormFile" 测试封装到一个函数中。
如果您不想在服务器上测试空字符串,您可以将您的 Dart 结束代码更改为始终使用 utf-8 编码甚至 non-ascii 个字符串
request.files.add(
new http.MultipartFile.fromBytes(
'some_form_value_name',
utf8.encode('the string value'),
contentType: new MediaType('text', 'plain', {'charset': 'utf-8'}),
),
);
并使用 Open/Read/string 方法在服务器上读取它们。
我现在已经解决了这个问题。感谢理查德和丹尼的帮助。
1。这样做的原因
不管发生什么,但这真的不仅仅是一个方面的问题,我们不能说它是 Flutter 或 Go wrong。但是组合,Flutter + Go 服务器可能会遇到这个问题。后面的原因我还是没确定,但一定是脑袋不对(post人能做到);
2。解决方案
我们不仅要知道为什么,还要知道如何解决。这是我的工作:
不要使用官方http包。使用 dio
,这是 Dart 的扩展包。 link: https://pub.dartlang.org/packages/dio
它更简洁易用,所以我的代码变成:
FormData _formData = new FormData.from({
"user_acc": _userAcc,
"user_nick_name": _userNickName,
'user_password': _password,
});
Dio dio = new Dio();
Response response = await dio.post(usersUrl, data: _formData);
print(response.data);
我不会post none-英文单词现在:
INFO[0668] userAcc: ww, userNickName: 小鹿叮叮婴儿湿巾手口专用80抽湿纸巾婴儿湿巾婴儿100抽带盖批发【原价】34.90元【券后】9.9元【省】25元【复制此信息打开手机淘宝即可查看并下单】¥Tnsx0E77pFs¥【必买理由】新品预售80抽*3仙女联盟,更多优惠fd.loliloli.pro , userPassword: ww
INFO[0671] 用户存在。