From 8a00f8d51834cf2efa2c761289d9f0f99957bebe Mon Sep 17 00:00:00 2001 From: Alex Li Date: Tue, 7 Mar 2023 07:04:31 +0800 Subject: [PATCH] Improve exceptions and documents for `FormData` and `MultipartFile` (#1714) Help issues like #482 to understand what happened exactly. ### New Pull Request Checklist - [x] I have read the [Documentation](https://pub.dev/documentation/dio/latest/) - [x] I have searched for a similar pull request in the [project](https://github.com/cfug/dio/pulls) and found none - [x] I have updated this branch with the latest `main` branch to avoid conflicts (via merge from master or rebase) - [ ] I have added the required tests to prove the fix/feature I'm adding - [x] I have updated the documentation (if necessary) - [x] I have run the tests without failures - [ ] I have updated the `CHANGELOG.md` in the corresponding package ### Additional context and info (if any) As an improvement with comments and docs, it doesn't require a version change. --------- Signed-off-by: Alex Li Co-authored-by: Peter Leibiger --- dio/README-ZH.md | 23 ++++++++++++++++++++++- dio/README.md | 25 +++++++++++++++++++++++-- dio/lib/src/form_data.dart | 6 +++++- dio/lib/src/multipart_file.dart | 6 +++++- 4 files changed, 55 insertions(+), 5 deletions(-) diff --git a/dio/README-ZH.md b/dio/README-ZH.md index 5980da952..994b8e4a6 100644 --- a/dio/README-ZH.md +++ b/dio/README-ZH.md @@ -36,6 +36,7 @@ dio 是一个强大的 Dart HTTP 请求库,支持全局配置、Restful API、 * [使用 application/x-www-form-urlencoded 编码](#使用-applicationx-www-form-urlencoded-编码) * [发送 FormData](#发送-formdata) * [多文件上传](#多文件上传) + * [复用 `FormData` 和 `MultipartFile`](#复用-formdata-和-multipartfile) * [转换器](#转换器) * [在 Flutter 中进行设置](#在-flutter-中进行设置) * [其它示例](#其它示例) @@ -569,7 +570,7 @@ final formData = FormData.fromMap({ final response = await dio.post('/info', data: formData); ``` -> 注意: 只有 post 方法支持发送 FormData. +> 通常情况下只有 POST 方法支持发送 FormData。 这里有一个完整的 [示例](../example/lib/formdata.dart)。 @@ -604,6 +605,26 @@ formData.files.addAll([ ]); ``` +### 复用 `FormData` 和 `MultipartFile` + +如果你在重复调用的请求中使用 `FormData` 或者 `MultipartFile`,确保你每次使用的都是新实例。 +常见的错误做法是将 `FormData` 赋值给一个共享变量,在每次请求中都使用这个变量。 +这样的操作会加大 **无法序列化** 的错误出现的可能性。 +你可以像以下的代码一样编写你的请求以避免出现这样的错误: +```dart +Future _repeatedlyRequest() async { + Future createFormData() async { + return FormData.fromMap({ + 'name': 'dio', + 'date': DateTime.now().toIso8601String(), + 'file': await MultipartFile.fromFile('./text.txt',filename: 'upload.txt'), + }); + } + + await dio.post('some-url', data: await createFormData()); +} +``` + ## 转换器 转换器 `Transformer` 用于对请求数据和响应数据进行编解码处理。 diff --git a/dio/README.md b/dio/README.md index bf61a8cd3..4fd627e25 100644 --- a/dio/README.md +++ b/dio/README.md @@ -37,6 +37,7 @@ timeout, and custom adapters etc. * [Using application/x-www-form-urlencoded format](#using-applicationx-www-form-urlencoded-format) * [Sending FormData](#sending-formdata) * [Multiple files upload](#multiple-files-upload) + * [Reuse `FormData`s and `MultipartFile`s](#reuse-formdata-s-and-multipartfile-s) * [Transformer](#transformer) * [In Flutter](#in-flutter) * [Other example](#other-example) @@ -596,12 +597,12 @@ and it supports uploading files. final formData = FormData.fromMap({ 'name': 'dio', 'date': DateTime.now().toIso8601String(), - 'file': await MultipartFile.fromFile('./text.txt',filename: 'upload.txt') + 'file': await MultipartFile.fromFile('./text.txt',filename: 'upload.txt'), }); final response = await dio.post('/info', data: formData); ``` -Note: `FormData` is only supported in POST methods. +> `FormData` is supported with the POST method typically. There is a complete example [here](../example/lib/formdata.dart). @@ -639,6 +640,26 @@ formData.files.addAll([ ]); ``` +### Reuse `FormData`s and `MultipartFile`s + +You should make a new `FormData` or `MultipartFile` every time in repeated requests. +A typical wrong behavior is setting the `FormData` as a variable and using it in every request. +It can be easy for the *Cannot finalize* exceptions to occur. +To avoid that, write your requests like the below code: +```dart +Future _repeatedlyRequest() async { + Future createFormData() async { + return FormData.fromMap({ + 'name': 'dio', + 'date': DateTime.now().toIso8601String(), + 'file': await MultipartFile.fromFile('./text.txt',filename: 'upload.txt'), + }); + } + + await dio.post('some-url', data: await createFormData()); +} +``` + ## Transformer `Transformer` allows changes to the request/response data diff --git a/dio/lib/src/form_data.dart b/dio/lib/src/form_data.dart index b5ac7d47f..aec59cf29 100644 --- a/dio/lib/src/form_data.dart +++ b/dio/lib/src/form_data.dart @@ -139,7 +139,11 @@ class FormData { Stream> finalize() { if (isFinalized) { - throw StateError('Already finalized.'); + throw StateError( + 'The FormData has already been finalized. ' + 'This typically means you are using ' + 'the same FormData in repeated requests.', + ); } _isFinalized = true; final controller = StreamController>(sync: false); diff --git a/dio/lib/src/multipart_file.dart b/dio/lib/src/multipart_file.dart index bcea8daf7..85fa3e6c0 100644 --- a/dio/lib/src/multipart_file.dart +++ b/dio/lib/src/multipart_file.dart @@ -131,7 +131,11 @@ class MultipartFile { Stream> finalize() { if (isFinalized) { - throw StateError("Can't finalize a finalized MultipartFile."); + throw StateError( + 'The MultipartFile has already been finalized. ' + 'This typically means you are using ' + 'the same MultipartFile in repeated requests.', + ); } _isFinalized = true; return _stream;