send method

Future send(
  1. String path, {
  2. String method = "GET",
  3. Map<String, String> headers = const {},
  4. Map<String, dynamic> query = const {},
  5. Map<String, dynamic> body = const {},
  6. List<MultipartFile> files = const [],
})

Sends a single HTTP request built with the current client configuration and the provided options.

All response errors are normalized and wrapped in ClientException.

Implementation

Future<dynamic> send(
  String path, {
  String method = "GET",
  Map<String, String> headers = const {},
  Map<String, dynamic> query = const {},
  Map<String, dynamic> body = const {},
  List<http.MultipartFile> files = const [],
}) async {
  http.BaseRequest request;

  final url = buildUrl(path, query);

  if (files.isEmpty) {
    request = _jsonRequest(method, url, headers: headers, body: body);
  } else {
    request = _multipartRequest(
      method,
      url,
      headers: headers,
      body: body,
      files: files,
    );
  }

  if (!headers.containsKey("Authorization") && authStore.isValid) {
    request.headers["Authorization"] = authStore.token;
  }

  if (!headers.containsKey("Accept-Language")) {
    request.headers["Accept-Language"] = lang;
  }

  // ensures that keepalive on web is disabled for now
  //
  // it is ignored anyway when using the default http.Cient on web
  // and it causing issues with the alternative fetch_client package
  // (see https://github.com/Zekfad/fetch_client/issues/6#issuecomment-1615936365)
  if (isWeb) {
    request.persistentConnection = false;
  }

  final requestClient = httpClientFactory();

  try {
    final response = await requestClient.send(request);
    final responseStr = await response.stream.bytesToString();

    dynamic responseData;
    try {
      responseData = responseStr.isNotEmpty ? jsonDecode(responseStr) : null;
    } catch (_) {
      // custom non-json response
      responseData = responseStr;
    }

    if (response.statusCode >= 400) {
      throw ClientException(
        url: url,
        statusCode: response.statusCode,
        response: responseData is Map<String, dynamic> ? responseData : {},
      );
    }

    return responseData;
  } catch (e) {
    // PocketBase API exception
    if (e is ClientException) {
      rethrow;
    }

    // http client exception (eg. connection abort)
    if (e is http.ClientException) {
      throw ClientException(
        url: e.uri,
        originalError: e,
        // @todo will need to be redefined once cancellation support is added
        isAbort: true,
      );
    }

    // anything else
    throw ClientException(url: url, originalError: e);
  } finally {
    requestClient.close();
  }
}