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

RESTEasy Reactive: multipart form improvements #27526

Merged
merged 1 commit into from
Oct 21, 2022

Conversation

FroMage
Copy link
Member

@FroMage FroMage commented Aug 26, 2022

Fixes #22205

  • Made @BeanParam and @MultipartForm optional (any container class with fields using the @Rest* annotations or their JAX-RS equivalents will be auto-detected as a bean param
  • Merged the @MultipartForm special handling into the @BeanParam code, so that you can now have any request field in that container class (@RestCookie and @RestForm for example)
  • Support multipart parameters in method parameters (no need for containing class anymore)
  • Auto-detect multipart based on the presence of FileUpload, File, Path (for the server) and byte[], Multi<Byte> (for the client), removing need for explicit @Consumes
  • Multipart fields default to text/plain for @PartType except for FileUpload, File, Path (for the server) and byte[], Multi<Byte> (for the client) which default to application/octet-stream
  • Multipart fields that are text/plain go through the ParamConverter route instead of the MessageBodyReader/Writer one.
  • Multipart fields that are neither text/plain nor FileUpload, File, Path (for the server) and byte[], Multi<Byte> (for the client) go through the MessageBodyReader/Writer route.
  • Deprecated @MultipartForm which is never useful anymore. Would deprecate @BeanParam too if I could.
  • Replaced the awkward @RestForm List<FileUpload> uploads pattern which was special-cased to obtain all uploads with the clearer pattern @RestForm(FileUpload.ALL) List<FileUpload> uploads. Note that this is a breaking change.
  • Updated the docs.

I had to touch some bits that were also touched by #27474 due to parallel work.
I also saw some special support for getters/setters in multipart forms that may have fallen by the side due to bean param probably not dealing with them, but I'm not sure. I'm also not sure it's useful.

@quarkus-bot

This comment has been minimized.

@geoand
Copy link
Contributor

geoand commented Aug 26, 2022

@Sgitario you might also be interested in reviewing this

@quarkus-bot

This comment has been minimized.

@quarkus-bot

This comment has been minimized.

Copy link
Contributor

@Sgitario Sgitario left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apart from the comments in the review, I'm not a big fan of doing @RestForm(FileUpload.ALL) to load a list of FileUpload List<FileUpload>.

Also, if possible and only if it's easy, we could move the converters into the common module, so it's reused by both the client and the server.

The rest of the changes look good to me.

@@ -744,35 +744,42 @@ REST Client Reactive support multipart messages.
REST Client Reactive allows sending data as multipart forms. This way you can for example
send files efficiently.

To send data as a multipart form, you need to create a class that would encapsulate all the fields
to be sent, e.g.
To send data as a multipart form, you can just use the regular `@RestForm` annotations:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should mention the regular JAX-RS annotation @FormParam here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess… In the server guide we avoid mentioning these annotations to not confuse users.

docs/src/main/asciidoc/rest-client-reactive.adoc Outdated Show resolved Hide resolved
@@ -809,7 +830,7 @@ Then, create an interface method that corresponds to the call and make it return
@GET
@Produces(MediaType.MULTIPART_FORM_DATA)
@Path("/get-file")
FormDto data sendMultipart();
FormDto data receiveMultipart();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're sending a multipart object, not receiving, right ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, from the PoV of the client, we're receiving one, we're not sending anything, especially not from a GET request.

docs/src/main/asciidoc/resteasy-reactive.adoc Outdated Show resolved Hide resolved
@FroMage
Copy link
Member Author

FroMage commented Aug 26, 2022

Apart from the comments in the review, I'm not a big fan of doing @RestForm(FileUpload.ALL) to load a list of FileUpload List<FileUpload>.

Well, consider that the semantics of @RestForm List<FileUpload> foo for every other case is to default to the foo name (name of the annotated field/parameter), but only in the case of multipart, that default was dropped in favour of requiring the user to duplicate the parameter name @RestForm("foo") List<FileUpload> foo, but again only for the special List<FileUpload> type. This sort of special-case is super harmful to users, counter-intuitive and really bad practice.

The new way of doing his, by explicitely overriding the name to a meaningful constant FileUpload.ALL (which is the * string, which is not a valid parameter name otherwise) is self-describing as well as consistent with all other usages of @RestForm.

Also, if possible and only if it's easy, we could move the converters into the common module, so it's reused by both the client and the server.

The server doesn't actually need the primitive converters, because it will generate them if they're required. The client should do the same, possibly by sharing the server code which generates the converters, but that's not for this issue.

@quarkus-bot

This comment has been minimized.

@geoand
Copy link
Contributor

geoand commented Aug 29, 2022

FWIW, the test failures do seem related

@FroMage
Copy link
Member Author

FroMage commented Aug 29, 2022

Damn, look like I broke quite a few TCK tests :(

@FroMage
Copy link
Member Author

FroMage commented Aug 29, 2022

MultipartResourceTest runs locally. Do you happen to know how I can run the MultipartResourceIT one?

@geoand
Copy link
Contributor

geoand commented Aug 29, 2022

mvn verify -Dstart-containers -Dtest-containers

will build the native binary and run the tests.
If you already have the native binary built, you can save a bunch of time by doing:

mvn failsafe:integration-test -Dstart-containers -Dtest-containers -Dnative -Dit.test=MultipartResourceIT

@FroMage
Copy link
Member Author

FroMage commented Aug 29, 2022

Thanks.

@FroMage
Copy link
Member Author

FroMage commented Aug 29, 2022

I was able to fix all the test issues, except the native image one. It's throwing a 500, I guess I need to find out how to make it log the cause.

@FroMage
Copy link
Member Author

FroMage commented Aug 29, 2022

It's a reflection issue, apparently.

@FroMage
Copy link
Member Author

FroMage commented Aug 30, 2022

OK, the tests should pass now.

BTW @geoand I couldn't find any way to make RR print the exceptions, no setting, no docs about it, and nothing in the code that leads me to think we even log exceptions before we map them. Is that true? If yes, shouldn't we add a setting or log them on error level or something?

@FroMage
Copy link
Member Author

FroMage commented Aug 30, 2022

Guys, I'd love a review on this, in particular about the docs and changes, and an opinion on the impact of the breaking change regarding obtaining all FileUpload regardless of their names.

But can we not merge this until I get back from PTO at the end of september? Otherwise I won't be able to help with any bugs.

@geoand
Copy link
Contributor

geoand commented Aug 30, 2022

But can we not merge this until I get back from PTO at the end of september? Otherwise I won't be able to help with any bugs

Sure yeah. @Sgitario let's not make any multipart changes until this is in, because it will cause @FroMage a lot of unnecessary headaches.

@geoand
Copy link
Contributor

geoand commented Aug 30, 2022

BTW @geoand I couldn't find any way to make RR print the exceptions, no setting, no docs about it, and nothing in the code that leads me to think we even log exceptions before we map them. Is that true? If yes, shouldn't we add a setting or log them on error level or something?

I think you are probably right...

@FroMage FroMage marked this pull request as draft August 30, 2022 08:43
@FroMage
Copy link
Member Author

FroMage commented Aug 30, 2022

Turning to draft to avoid premature merging

@quarkus-bot

This comment has been minimized.

@FroMage
Copy link
Member Author

FroMage commented Oct 13, 2022

😥

igorregis pushed a commit to igorregis/quarkus that referenced this pull request Oct 16, 2022
The failure showed up in a few PRs
(like quarkusio#28429 and quarkusio#27526)
igorregis pushed a commit to igorregis/quarkus that referenced this pull request Oct 16, 2022
The failure showed up in a few PRs
(like quarkusio#28429 and quarkusio#27526)
igorregis pushed a commit to igorregis/quarkus that referenced this pull request Oct 16, 2022
The failure showed up in a few PRs
(like quarkusio#28429 and quarkusio#27526)
igorregis pushed a commit to igorregis/quarkus that referenced this pull request Oct 17, 2022
The failure showed up in a few PRs
(like quarkusio#28429 and quarkusio#27526)
@geoand
Copy link
Contributor

geoand commented Oct 17, 2022

@gsmet are you okay with this one?

We really need to get this in because it's causing frequent conflicts (@FroMage can you address the latest one please?).

@FroMage
Copy link
Member Author

FroMage commented Oct 17, 2022

Done.

@geoand
Copy link
Contributor

geoand commented Oct 17, 2022

TY

@quarkus-bot

This comment has been minimized.

@FroMage
Copy link
Member Author

FroMage commented Oct 20, 2022

This test failure looks unrelated. Can we merge this @gsmet and move on with our lives? :)

- Merged support into @BeanParam handling
- Deprecated @MultipartForm
- Auto-detect @BeanParam classes, annotation now optional
- Use ParamConverter instead of MessageBodyReader for plain text
  multiparts
- Auto-default to Accept: urlencoded or multipart depending on types
  of @FormParam present
- Support multipart form elements as endpoint parameters and fields
- Breaking: need @restform(FileUpload.ALL) for getting all uploads
@FroMage
Copy link
Member Author

FroMage commented Oct 21, 2022

Squashed into a single commit.

@geoand geoand dismissed gsmet’s stale review October 21, 2022 08:19

issue addressed

@geoand geoand added triage/waiting-for-ci Ready to merge when CI successfully finishes release/noteworthy-feature labels Oct 21, 2022
@quarkus-bot
Copy link

quarkus-bot bot commented Oct 21, 2022

Failing Jobs - Building 03833f8

Status Name Step Failures Logs Raw logs
✔️ JVM Tests - JDK 11
JVM Tests - JDK 17 Build ⚠️ Check → Logs Raw logs
JVM Tests - JDK 17 MacOS M1 Build Failures Logs Raw logs
✔️ JVM Tests - JDK 18

Full information is available in the Build summary check run.

Failures

⚙️ JVM Tests - JDK 17 MacOS M1 #

- Failing: integration-tests/mongodb-devservices 

📦 integration-tests/mongodb-devservices

io.quarkus.it.mongodb.BookResourceTest.health line 45 - More details - Source on GitHub

java.net.SocketTimeoutException: Read timed out
	at java.base/sun.nio.ch.NioSocketImpl.timedRead(NioSocketImpl.java:283)
	at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:309)

io.quarkus.it.mongodb.BookResourceTest.testReactiveClients line 39 - More details - Source on GitHub

java.net.SocketTimeoutException: Read timed out
	at java.base/sun.nio.ch.NioSocketImpl.timedRead(NioSocketImpl.java:283)
	at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:309)

io.quarkus.it.mongodb.BookResourceTest.testBlockingClient line 34 - More details - Source on GitHub

java.net.SocketTimeoutException: Read timed out
	at java.base/sun.nio.ch.NioSocketImpl.timedRead(NioSocketImpl.java:283)
	at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:309)

io.quarkus.it.mongodb.BookResourceWithParameterInjectionTest.testInjectedClient line 31 - More details - Source on GitHub

java.net.SocketTimeoutException: Read timed out
	at java.base/sun.nio.ch.NioSocketImpl.timedRead(NioSocketImpl.java:283)
	at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:309)

io.quarkus.it.mongodb.OtherProfileBookResourceTest.testBlockingClient line 14 - More details - Source on GitHub

java.net.SocketTimeoutException: Read timed out
	at java.base/sun.nio.ch.NioSocketImpl.timedRead(NioSocketImpl.java:283)
	at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:309)

@geoand geoand merged commit e50ae5d into quarkusio:main Oct 21, 2022
@quarkus-bot quarkus-bot bot added this to the 2.14 - main milestone Oct 21, 2022
@quarkus-bot quarkus-bot bot added kind/enhancement New feature or request and removed triage/waiting-for-ci Ready to merge when CI successfully finishes labels Oct 21, 2022
@FroMage
Copy link
Member Author

FroMage commented Oct 24, 2022

Updated the migration guide too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

RESTEasy Reactive: multipart form improvement
4 participants