dio icon indicating copy to clipboard operation
dio copied to clipboard

Prevent encoding query parameters

Open xeinebiu opened this issue 2 years ago • 2 comments

New Issue Checklist

  • [x] I have searched for a similar issue in the project and found none
dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.4
  json_annotation: ^4.4.0
  dio: ^4.0.4

Issue Description and Steps

https://github.com/flutterchina/dio/blob/develop/dio/lib/src/options.dart#L545

The above code always does the encoding of query parameters by using + instead of spaces. The issue is, some servers do not understand the +, instead they use %20.

Tried to manually encode the query paramater value but then it ends up to duplicated encoding.

  • How to prevent encoding or provive a Query Parameter encoder to support this case ?
// Current (Term=Hello World)
baseUrl/search?term=HELLO+WORLD

Expected (Term=Hello World)
// baseUrl/search?term=HELLO%20WORLD

xeinebiu avatar Mar 16 '22 18:03 xeinebiu

Currently using the below workaround.

class EncodingInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    if (options.queryParameters.isEmpty) {
      super.onRequest(options, handler);
      return;
    }

    final queryParams = _getQueryParams(options.queryParameters);
    handler.next(
      options.copyWith(
        path: _getNormalizedUrl(options.path, queryParams),
        queryParameters: Map.from({}),
      ),
    );
  }

  String _getNormalizedUrl(String baseUrl, String queryParams) {
    if (baseUrl.contains("?")) {
      return baseUrl + "&$queryParams";
    } else {
      return baseUrl + "?$queryParams";
    }
  }

  String _getQueryParams(Map<String, dynamic> map) {
    String result = "";
    map.forEach((key, value) {
      result += "$key=${Uri.encodeComponent(value)}&";
    });
    return result;
  }
}

https://github.com/flutterchina/dio/blob/develop/dio/lib/src/transformer.dart#L45 Can be fixed by using Uri.encodeComponent(value) instead.

xeinebiu avatar Mar 16 '22 20:03 xeinebiu

Currently using the below workaround.

class EncodingInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    if (options.queryParameters.isEmpty) {
      super.onRequest(options, handler);
      return;
    }

    final queryParams = _getQueryParams(options.queryParameters);
    handler.next(
      options.copyWith(
        path: _getNormalizedUrl(options.path, queryParams),
        queryParameters: Map.from({}),
      ),
    );
  }

  String _getNormalizedUrl(String baseUrl, String queryParams) {
    if (baseUrl.contains("?")) {
      return baseUrl + "&$queryParams";
    } else {
      return baseUrl + "?$queryParams";
    }
  }

  String _getQueryParams(Map<String, dynamic> map) {
    String result = "";
    map.forEach((key, value) {
      result += "$key=${Uri.encodeComponent(value)}&";
    });
    return result;
  }
}

https://github.com/flutterchina/dio/blob/develop/dio/lib/src/transformer.dart#L45 Can be fixed by using Uri.encodeComponent(value) instead.

Thanks, it works with me

minhdangoz avatar Sep 16 '22 07:09 minhdangoz

Currently using the below workaround.

class EncodingInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    if (options.queryParameters.isEmpty) {
      super.onRequest(options, handler);
      return;
    }

    final queryParams = _getQueryParams(options.queryParameters);
    handler.next(
      options.copyWith(
        path: _getNormalizedUrl(options.path, queryParams),
        queryParameters: Map.from({}),
      ),
    );
  }

  String _getNormalizedUrl(String baseUrl, String queryParams) {
    if (baseUrl.contains("?")) {
      return baseUrl + "&$queryParams";
    } else {
      return baseUrl + "?$queryParams";
    }
  }

  String _getQueryParams(Map<String, dynamic> map) {
    String result = "";
    map.forEach((key, value) {
      result += "$key=${Uri.encodeComponent(value)}&";
    });
    return result;
  }
}

https://github.com/flutterchina/dio/blob/develop/dio/lib/src/transformer.dart#L45 Can be fixed by using Uri.encodeComponent(value) instead.

I suggest you this change to avoid errors on the qp's types sent:

String _getQueryParams(Map<String, dynamic> map) {
    String result = "";
    map.forEach((key, value) {
      result += value.runtimeType == String
          ? "$key=${Uri.encodeComponent(value)}&"
          : "$key=$value&";
    });
    return result;
  }

mb180594b avatar Nov 24 '22 08:11 mb180594b

Hi everyone. According to Uri.encodeQueryComponent that is used in Transformer.urlEncodeMap, it's the right choice of the API. But I also want to test if I can provide another solution to change the code base:

Try to replace: https://github.com/flutterchina/dio/blob/9040bde4a07ffe4e201e4ec014242a16eb69d926/dio/lib/src/options.dart#L545-L550

To this:

// Normalize the url.
return Uri.parse(url)
    .replace(queryParameters: queryParameters)
    .normalizePath();

This will drop the usage of the ListFormat, but please let me know if it works.

AlexV525 avatar Dec 15 '22 16:12 AlexV525

This is completed because... there's a way to avoid encoding query parameters without the above workaround?

The above workaround doesn't work for me. The parameters still gets encoded. Aka I want to send a query param like so:

?param=auth0|someId

But even if I explicitly overwrite the above without encoding I still get

?param=auth0%7CsomeId

Which is pretty annoying. I have no other interceptors implemented / connected.

lucavenir avatar Jan 18 '24 18:01 lucavenir