如何在POST请求中设置多值字段? [英] How to set a field with multi-value into a Post request?

查看:23
本文介绍了如何在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"

如您所见,有两个多值字段organsimages,一个用于字符串对象,另一个用于文件对象。

我编写了以下代码:

  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也有类似情况。

如何使用多值字段?

推荐答案

  1. 这些文件有重复的字段名称实际上是可以的。出现400错误可能是因为您发送了两个images,但只发送了一个organs。因此,您需要解决的唯一问题就是发送多个同名的字段。

  2. 没有更好的想法,可以复制原始的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)}';
  }
}

(最好是子类,但那里很多有价值的东西都是私有的。)

  1. 然后在代码中使用addAll而不是addEntries设置字段:
request.fields.addAll(fields);
我看到您已经向DARThttp包提交了一个问题。这很好。

这篇关于如何在POST请求中设置多值字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆