Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stringify models in formdata #111

Closed
konstantin-doncov opened this issue Feb 7, 2020 · 16 comments · Fixed by #116
Closed

Stringify models in formdata #111

konstantin-doncov opened this issue Feb 7, 2020 · 16 comments · Fixed by #116
Labels
enhancement New feature or request

Comments

@konstantin-doncov
Copy link

I want to send my model and files using @MultiPart(). My Spring server has the next endpoint:

@Transactional
@PostMapping("/test")
public void test(@Valid @RequestPart(value = "MyModel") MyModel myModel, @RequestPart(value = "listOfFiles") List<MultipartFile> listOfFiles,  @RequestPart(value = "oneFile") MultipartFile oneFile)

My working Retrofit code in Java:

@Multipart
@POST("/test")
Call<Void> test(@Part("MyModel") MyModel myModel, @Part List<MultipartBody.Part> listOfFiles, @Part MultipartBody.Part oneFile);

But I can't do the same with the Retrofit.dart:

@MultiPart()
@POST("/test")
Future<void> test(@Part("MyModel") MyModel myModel, @Part() List<File> listOfFiles, @Part() File oneFile);

Generated file:

@override
test(myModel, listOfFiles, oneFile) async {
  ArgumentError.checkNotNull(myModel, 'myModel');
  ArgumentError.checkNotNull(listOfFiles, 'listOfFiles');
  ArgumentError.checkNotNull(oneFile, 'oneFile');
  const _extra = <String, dynamic>{};
  final queryParameters = <String, dynamic>{};
  final _data = FormData.fromMap(<String, dynamic>{
    'MyModel': myModel,
    'listOfFiles': listOfFiles,
    'oneFile': MultipartFile.fromFileSync(oneFile.path,
        filename: oneFile.path.split(Platform.pathSeparator).last)
  });
  await _dio.request<void>('/test',
      queryParameters: queryParameters,
      options: RequestOptions(
          method: 'POST',
          headers: <String, dynamic>{},
          extra: _extra,
          baseUrl: baseUrl),
      data: _data);
  return Future.value(null);
}

Even if I don't send files, like this:

@MultiPart()
@POST("/test")
Future<void> test(@Part("MyModel") MyModel myModel);

The problem is that I get this exception:

DioError [DioErrorType.RESPONSE]: Http status error [415]
"error" -> "Unsupported Media Type"
"message" -> "Content type 'application/octet-stream' not supported"

So, how can I send my model and my files to the specified endpoint?

@trevorwang trevorwang added the enhancement New feature or request label Feb 8, 2020
@trevorwang
Copy link
Owner

Currently, only limited type can be supported. e.g. each property as parameter and single file

@trevorwang
Copy link
Owner

Will support your scenario in next release

@konstantin-doncov
Copy link
Author

@trevorwang ok, but what workaround can I use for this scenario now?

@trevorwang
Copy link
Owner

trevorwang commented Feb 9, 2020

This action that submit file with content is not recommended.
From my side I'd prefer to submit file in a separated request. And then submit all data with returned file URL in another one. You have to retry the request even only one filed was saved failed.

trevorwang added a commit that referenced this issue Feb 9, 2020
@konstantin-doncov
Copy link
Author

@trevorwang thank you for the new release.

  1. Unfortunately, I can’t use it yet:
    retrofit: ^1.2.1 returns:

Because project depends on retrofit ^1.2.1 which doesn't match any versions, version solving failed.

So, I tried to use git version:

  retrofit:
    git:
      url: git://github.com/trevorwang/retrofit.dart.git

It returns:

Could not find a file named "pubspec.yaml" in git://github.com/trevorwang/retrofit.dart.git 8bd0d86.

  1. I saw this version adds support for multiple files, but can I send my class as a payload like this:
    @Part("MyModel") MyModel myModel?

@trevorwang
Copy link
Owner

trevorwang commented Feb 10, 2020

It's retrofit_generator. You don't need to upgrade retrofit.

You can check the sample.dart to see more details.

@konstantin-doncov
Copy link
Author

@trevorwang @Part("MyModel") Map<String, dynamic> myModel and @Part("MyModel") MyModel myModel with the new retrofit_generator give:

DioError [DioErrorType.RESPONSE]: Http status error [400]
"message" -> "Required request part 'MyModel' is not present"

@trevorwang
Copy link
Owner

Did you try with dio directly? I am not familiar with spring, may it needs some special config before you use formdata.

@konstantin-doncov
Copy link
Author

@trevorwang no, I haven't tried dio. But I have already successfully used this code in Retrofit in Java:

@Multipart
@POST("/test")
Call<Void> test(@Part("MyModel") MyModel myModel);

with this Spring endpoint.

@trevorwang
Copy link
Owner

trevorwang commented Feb 11, 2020

Currently, when you put a map to a FormData, dio will post

   "form": {
    "map[key1]":xxxx,
   "map[key2]:xxxxx 
}

instead of

"form": {
    "map": ''value to string"
}

There're some BREAKING changes both on annotation and generator package if I keep the behavior with dio. Please use a string value parameter as a workaround. (passing the MyModel.toString() )

I will provide a solution lately.

@trevorwang trevorwang reopened this Feb 11, 2020
@trevorwang
Copy link
Owner

trevorwang commented Feb 11, 2020 via email

@konstantin-doncov
Copy link
Author

@trevorwang I upgraded my retrofit_generator to ^1.2.1+1 and tried @Part("MyModel") Map<String, dynamic> myModel with myModel.toJson() and just @Part("MyModel") MyModel myModel.
This generated key="MyModel[myField]" and value="my string value" in the data.fields[0].
And I still have

"Required request part 'MyModel' is not present"

@trevorwang
Copy link
Owner

trevorwang commented Feb 12, 2020

MyModel.toString()

@konstantin-doncov
Copy link
Author

@trevorwang unfortunately, I don't fully understand you. Do you want me to use the non-overridden toString() with @Part("MyModel") String myModel? If so, then it's does not take into account the fields, and I get an error 415 again. Also, I can override it, but what's the point to use Retrofit?

@trevorwang
Copy link
Owner

Have a try toJason().toString() to JsonEncode as a workaround.

The feature will be supported in the future release. I am thinking how to figure out it better.

@trevorwang trevorwang changed the title DioError: Http status error [415] with the @Part() Stringify models in formdata Feb 13, 2020
trevorwang added a commit that referenced this issue Feb 13, 2020
trevorwang added a commit that referenced this issue Feb 13, 2020
trevorwang added a commit that referenced this issue Feb 13, 2020
* feat: json stringify models in formdata

close #111

* chore: bump version to 1.2.2+2
@konstantin-doncov
Copy link
Author

@trevorwang unfortunately, I still have 415 error - please check this, I left here information about requests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants