Skip to content

Commit

Permalink
馃殤 Fix FormData encoding (#1741) (#1747)
Browse files Browse the repository at this point in the history
  • Loading branch information
kuhnroyal committed Mar 17, 2023
1 parent 9c30e9a commit f9c923b
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 73 deletions.
1 change: 1 addition & 0 deletions dio/CHANGELOG.md
Expand Up @@ -3,6 +3,7 @@
## Unreleased

- Imply `List<Map>` as JSON content in `ImplyContentTypeInterceptor`.
- Fix `FormData` encoding for collections and objects.

## 5.0.2

Expand Down
7 changes: 5 additions & 2 deletions dio/lib/src/utils.dart
Expand Up @@ -38,8 +38,11 @@ String encodeMap(
}) {
final urlData = StringBuffer('');
bool first = true;
final leftBracket = isQuery ? '[' : '%5B';
final rightBracket = isQuery ? ']' : '%5D';
// URL Query parameters are generally encoded but not their
// index or nested names in square brackets.
// When [encode] is false, for example for [FormData], nothing is encoded.
final leftBracket = isQuery || !encode ? '[' : '%5B';
final rightBracket = isQuery || !encode ? ']' : '%5D';
final encodeComponent = encode ? Uri.encodeQueryComponent : (e) => e;
Object? maybeEncode(Object? value) {
if (!isQuery || value == null || value is! String) {
Expand Down
162 changes: 91 additions & 71 deletions dio/test/formdata_test.dart
Expand Up @@ -6,85 +6,105 @@ import 'package:dio/dio.dart';
import 'package:test/test.dart';

void main() async {
test('FormData', () async {
final fm = FormData.fromMap({
'name': 'wendux',
'age': 25,
'path': '/鍥剧墖绌洪棿/鍦板潃',
'file': MultipartFile.fromString(
'hello world.',
headers: {
'test': <String>['a']
},
),
'files': [
await MultipartFile.fromFile(
'test/mock/_testfile',
filename: '1.txt',
headers: {
'test': <String>['b']
},
),
MultipartFile.fromFileSync(
'test/mock/_testfile',
filename: '2.txt',
group(FormData, () {
test('complex', () async {
final fm = FormData.fromMap({
'name': 'wendux',
'age': 25,
'path': '/鍥剧墖绌洪棿/鍦板潃',
'file': MultipartFile.fromString(
'hello world.',
headers: {
'test': <String>['c']
'test': <String>['a']
},
),
]
});
final fmStr = await fm.readAsBytes();
final f = File('test/mock/_formdata');
String content = f.readAsStringSync();
content = content.replaceAll('--dio-boundary-3788753558', fm.boundary);
String actual = utf8.decode(fmStr, allowMalformed: true);
'files': [
await MultipartFile.fromFile(
'test/mock/_testfile',
filename: '1.txt',
headers: {
'test': <String>['b']
},
),
MultipartFile.fromFileSync(
'test/mock/_testfile',
filename: '2.txt',
headers: {
'test': <String>['c']
},
),
]
});
final fmStr = await fm.readAsBytes();
final f = File('test/mock/_formdata');
String content = f.readAsStringSync();
content = content.replaceAll('--dio-boundary-3788753558', fm.boundary);
String actual = utf8.decode(fmStr, allowMalformed: true);

actual = actual.replaceAll('\r\n', '\n');
content = content.replaceAll('\r\n', '\n');
actual = actual.replaceAll('\r\n', '\n');
content = content.replaceAll('\r\n', '\n');

expect(actual, content);
expect(fm.readAsBytes(), throwsA(const TypeMatcher<StateError>()));
expect(actual, content);
expect(fm.readAsBytes(), throwsA(const TypeMatcher<StateError>()));

final fm1 = FormData();
fm1.fields.add(MapEntry('name', 'wendux'));
fm1.fields.add(MapEntry('age', '25'));
fm1.fields.add(MapEntry('path', '/鍥剧墖绌洪棿/鍦板潃'));
fm1.files.add(
MapEntry(
'file',
MultipartFile.fromString(
'hello world.',
headers: {
'test': <String>['a']
},
final fm1 = FormData();
fm1.fields.add(MapEntry('name', 'wendux'));
fm1.fields.add(MapEntry('age', '25'));
fm1.fields.add(MapEntry('path', '/鍥剧墖绌洪棿/鍦板潃'));
fm1.files.add(
MapEntry(
'file',
MultipartFile.fromString(
'hello world.',
headers: {
'test': <String>['a']
},
),
),
),
);
fm1.files.add(
MapEntry(
'files',
await MultipartFile.fromFile(
'test/mock/_testfile',
filename: '1.txt',
headers: {
'test': <String>['b'],
},
);
fm1.files.add(
MapEntry(
'files',
await MultipartFile.fromFile(
'test/mock/_testfile',
filename: '1.txt',
headers: {
'test': <String>['b'],
},
),
),
),
);
fm1.files.add(
MapEntry(
'files',
await MultipartFile.fromFile(
'test/mock/_testfile',
filename: '2.txt',
headers: {
'test': <String>['c'],
},
);
fm1.files.add(
MapEntry(
'files',
await MultipartFile.fromFile(
'test/mock/_testfile',
filename: '2.txt',
headers: {
'test': <String>['c'],
},
),
),
),
);
expect(fmStr.length, fm1.length);
);
expect(fmStr.length, fm1.length);
});

test('encodes maps correctly', () async {
final fd = FormData.fromMap({
'items': [
{'name': 'foo', 'value': 1},
{'name': 'bar', 'value': 2},
],
});

final data = await fd.readAsBytes();
final result = utf8.decode(data, allowMalformed: true);

expect(result, contains('name="items[0][name]"'));
expect(result, contains('name="items[0][value]"'));

expect(result, contains('name="items[1][name]"'));
expect(result, contains('name="items[1][value]"'));
});
});
}

0 comments on commit f9c923b

Please sign in to comment.