如何在POST请求中设置多值字段? [英] How to set a field with multi-value into a Post request?
本文介绍了如何在POST请求中设置多值字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
此命令在Linux终端上运行正常:
curl -X POST "https://my-api.plantnet.org/v2/identify/all?api-key=11111111111111111111" -H "accept: application/json" -F "organs=flower" -F "organs=leaf" -F "images=@images/image_1.jpeg" -F "images=@images/image_2.jpeg"
如您所见,有两个多值字段organs
和images
,一个用于字符串对象,另一个用于文件对象。
我编写了以下代码:
static Future<T> postFilesAndGetJson<T>(String url, {List<MapEntry<String, String>> paths, List<MapEntry<String, String>> fields}) async {
var request = http.MultipartRequest('POST', Uri.parse(url));
if (paths != null && paths.isNotEmpty) {
paths.forEach((path) {
var file = File.fromUri(Uri.parse(path.value));
var multipartFile = http.MultipartFile.fromBytes(
path.key, file.readAsBytesSync(), filename: p.basename(file.path)
);
request.files.add(multipartFile);
});
}
if (fields != null && fields.isNotEmpty) {
request.fields.addEntries(fields);
}
return http.Response
.fromStream(await request.send())
.then((response) {
if (response.statusCode == 200) {
return jsonDecode(response.body) as T;
}
print('Status Code : ${response.statusCode}...');
return null;
});
}
当字段名不同时,它工作得很好,所以在这种情况下,它不工作,因为我得到状态代码400
(错误请求)。
request.fields
属性为Map<String, String>
,因此我无法(显然)将List<String>
设置为值。request.files
也有类似情况。
如何使用多值字段?
推荐答案
这些文件有重复的字段名称实际上是可以的。出现400错误可能是因为您发送了两个
images
,但只发送了一个organs
。因此,您需要解决的唯一问题就是发送多个同名的字段。没有更好的想法,可以复制原始的
MultipartRequest
,然后像MultipartListRequest
一样创建自己的类。然后将fields
从地图更改为列表(更改的行将被注释):
import 'dart:convert';
import 'dart:math';
import 'package:http/http.dart'; // CHANGED
import 'package:http/src/utils.dart'; // CHANGED
import 'package:http/src/boundary_characters.dart'; // CHANGED
final _newlineRegExp = RegExp(r'
|
|
');
class MultipartListRequest extends BaseRequest { // CHANGED
/// The total length of the multipart boundaries used when building the
/// request body.
///
/// According to http://tools.ietf.org/html/rfc1341.html, this can't be longer
/// than 70.
static const int _boundaryLength = 70;
static final Random _random = Random();
/// The form fields to send for this request.
final fields = <MapEntry<String, String>>[]; // CHANGED
/// The list of files to upload for this request.
final files = <MultipartFile>[];
MultipartListRequest(String method, Uri url) : super(method, url);
/// The total length of the request body, in bytes.
///
/// This is calculated from [fields] and [files] and cannot be set manually.
@override
int get contentLength {
var length = 0;
fields.forEach((field) { // CHANGED
final name = field.key; // CHANGED
final value = field.value; // CHANGED
length += '--'.length +
_boundaryLength +
'
'.length +
utf8.encode(_headerForField(name, value)).length +
utf8.encode(value).length +
'
'.length;
});
for (var file in files) {
length += '--'.length +
_boundaryLength +
'
'.length +
utf8.encode(_headerForFile(file)).length +
file.length +
'
'.length;
}
return length + '--'.length + _boundaryLength + '--
'.length;
}
@override
set contentLength(int? value) {
throw UnsupportedError('Cannot set the contentLength property of '
'multipart requests.');
}
/// Freezes all mutable fields and returns a single-subscription [ByteStream]
/// that will emit the request body.
@override
ByteStream finalize() {
// TODO: freeze fields and files
final boundary = _boundaryString();
headers['content-type'] = 'multipart/form-data; boundary=$boundary';
super.finalize();
return ByteStream(_finalize(boundary));
}
Stream<List<int>> _finalize(String boundary) async* {
const line = [13, 10]; //
final separator = utf8.encode('--$boundary
');
final close = utf8.encode('--$boundary--
');
for (var field in fields) { // CHANGED
yield separator;
yield utf8.encode(_headerForField(field.key, field.value));
yield utf8.encode(field.value);
yield line;
}
for (final file in files) {
yield separator;
yield utf8.encode(_headerForFile(file));
yield* file.finalize();
yield line;
}
yield close;
}
/// Returns the header string for a field.
///
/// The return value is guaranteed to contain only ASCII characters.
String _headerForField(String name, String value) {
var header =
'content-disposition: form-data; name="${_browserEncode(name)}"';
if (!isPlainAscii(value)) {
header = '$header
'
'content-type: text/plain; charset=utf-8
'
'content-transfer-encoding: binary';
}
return '$header
';
}
/// Returns the header string for a file.
///
/// The return value is guaranteed to contain only ASCII characters.
String _headerForFile(MultipartFile file) {
var header = 'content-type: ${file.contentType}
'
'content-disposition: form-data; name="${_browserEncode(file.field)}"';
if (file.filename != null) {
header = '$header; filename="${_browserEncode(file.filename!)}"';
}
return '$header
';
}
/// Encode [value] in the same way browsers do.
String _browserEncode(String value) =>
// http://tools.ietf.org/html/rfc2388 mandates some complex encodings for
// field names and file names, but in practice user agents seem not to
// follow this at all. Instead, they URL-encode `
`, `
`, and `
` as
// `
`; URL-encode `"`; and do nothing else (even for `%` or non-ASCII
// characters). We follow their behavior.
value.replaceAll(_newlineRegExp, '%0D%0A').replaceAll('"', '%22');
/// Returns a randomly-generated multipart boundary string
String _boundaryString() {
var prefix = 'dart-http-boundary-';
var list = List<int>.generate(
_boundaryLength - prefix.length,
(index) =>
boundaryCharacters[_random.nextInt(boundaryCharacters.length)],
growable: false);
return '$prefix${String.fromCharCodes(list)}';
}
}
(最好是子类,但那里很多有价值的东西都是私有的。)
- 然后在代码中使用
addAll
而不是addEntries
设置字段:
request.fields.addAll(fields);
我看到您已经向DARThttp
包提交了一个问题。这很好。
这篇关于如何在POST请求中设置多值字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文