request method

Future request(
  1. String requestUrl,
  2. String method, {
  3. String? body,
  4. Map<String, List<String>>? queryParams,
  5. Media? uploadMedia,
  6. UploadOptions? uploadOptions,
  7. DownloadOptions? downloadOptions = client_requests.DownloadOptions.metadata,
})

Sends a HTTPRequest using method (usually GET or POST) to requestUrl using the specified queryParams. Optionally include a body and/or uploadMedia in the request.

If uploadMedia was specified downloadOptions must be client_requests.DownloadOptions.Metadata or null.

If downloadOptions is client_requests.DownloadOptions.Metadata the result will be decoded as JSON.

If downloadOptions is null the result will be a Future completing with null.

Otherwise the result will be downloaded as a client_requests.Media

Implementation

Future request(
  String requestUrl,
  String method, {
  String? body,
  Map<String, List<String>>? queryParams,
  client_requests.Media? uploadMedia,
  client_requests.UploadOptions? uploadOptions,
  client_requests.DownloadOptions? downloadOptions =
      client_requests.DownloadOptions.metadata,
}) async {
  if (uploadMedia != null &&
      downloadOptions != client_requests.DownloadOptions.metadata) {
    throw ArgumentError(
      'When uploading a [Media] you cannot download a '
      '[Media] at the same time!',
    );
  }
  client_requests.ByteRange? downloadRange;
  if (downloadOptions is client_requests.PartialDownloadOptions &&
      !downloadOptions.isFullDownload) {
    downloadRange = downloadOptions.range;
  }
  queryParams = queryParams?.cast<String, List<String>>();

  var response = await _request(requestUrl, method, body, queryParams,
      uploadMedia, uploadOptions, downloadOptions, downloadRange);

  response = await validateResponse(response);

  if (downloadOptions == null) {
    // If no download options are given, the response is of no interest
    // and we will drain the stream.
    return response.stream.drain();
  } else if (downloadOptions == client_requests.DownloadOptions.metadata) {
    // Downloading JSON Metadata
    final stringStream = _decodeStreamAsText(response);
    if (stringStream == null) {
      throw client_requests.ApiRequestError(
        'Unable to read response with content-type '
        "${response.headers['content-type']}.",
      );
    }

    final bodyString = await stringStream.join();
    if (bodyString.isEmpty) return null;
    return json.decode(bodyString);
  }

  // Downloading Media.
  final contentType = response.headers['content-type'];
  if (contentType == null) {
    throw client_requests.ApiRequestError(
        "No 'content-type' header in media response.");
  }

  int? contentLength;
  if (response.headers['content-length'] != null) {
    contentLength = int.tryParse(response.headers['content-length']!);
  }

  if (downloadRange != null) {
    if (contentLength != downloadRange.length) {
      throw client_requests.ApiRequestError(
        'Content length of response does not match requested range length.',
      );
    }

    if (!isWeb) {
      // TODO(kevmoo) on the web, should check access-control-expose-headers
      // but this is easy for now
      final contentRange = response.headers[_contentRangeHeader];
      final expected = 'bytes ${downloadRange.start}-${downloadRange.end}/';
      if (contentRange == null || !contentRange.startsWith(expected)) {
        throw client_requests.ApiRequestError(
          'Attempting partial '
          "download but got invalid '$_contentRangeHeader' header "
          '(was: $contentRange, expected: $expected).',
        );
      }
    }
  }

  return client_requests.Media(response.stream, contentLength,
      contentType: contentType);
}