-
Notifications
You must be signed in to change notification settings - Fork 122
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
[418] multipart/form-data support #948
Conversation
@andymc12 Thank you for contributing this! Unfortunately I am busy with JavaLand organization recently, so the next two weeks I won't have time to actually review this code. Sorry for that. I pretend to have a look at it after JavaLand! :-) |
Thanks, Andy, yes, we had approx. 1500 online attendees and a quite shiny lineup of notable speakers. Despite all hopes, I am still busy today and tomorrow, so if things run well, will look into this PR on Sunday. Stay tuned! :-) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm done with an initial preview. Kudos, this is great work! But I have some questions, see inlined. Also I wonder why we force the use of streams? I understand that streams have benefits, by why not also allows parameter conversion or using Entity
classes?
examples/src/main/java/jaxrs/examples/multipart/MultipartClient.java
Outdated
Show resolved
Hide resolved
examples/src/main/java/jaxrs/examples/multipart/MultipartClient.java
Outdated
Show resolved
Hide resolved
jaxrs-spec/src/main/asciidoc/chapters/resources/_declaring_method_capabilities.adoc
Outdated
Show resolved
Hide resolved
examples/src/main/java/jaxrs/examples/multipart/MultipartClient.java
Outdated
Show resolved
Hide resolved
jaxrs-spec/src/main/asciidoc/chapters/resources/_declaring_method_capabilities.adoc
Outdated
Show resolved
Hide resolved
jaxrs-spec/src/main/asciidoc/chapters/resources/_declaring_method_capabilities.adoc
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andymc12 Thank you so much for working on this. See my comment in the source.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andymc12 Thanks a lot for integrating the latest changes. This looks awesome. I'm basically +1 for this, but added one comment. It would be great to hear your thoughts.
jaxrs-spec/src/main/asciidoc/chapters/resources/_declaring_method_capabilities.adoc
Outdated
Show resolved
Hide resolved
annotation corresponds to the name of the part. The parameter type may be | ||
a `jakarta.ws.rs.ext.Part`, a `java.io.InputStream`, a `String`, or any type | ||
that can be converted to using a registered `MessageBodyReader` or | ||
`ParamConverter`. Here is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry. One last question.
The API docs of Part#getContent(Class|GenericType)
only mentions MessageBodyReader
, but here we allow ParamConverter
as well.
I'm wondering if ParamConverter
makes sense here. If we allow both, we will most likely have to define which one of the two mechanisms is preferred over the other. And ParamConverter
uses a String
as the input and in the next paragraph we explicitly warn about String
being not optimal as it requires to read the full entity into the heap.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My thinking here was that ParamConverter
providers are already responsible for converting Strings to parameter types. The only real difference here (with @FormParam
) is that we have an InputStream
instead of a a String
- but since we allow the implementation to convert the InputStream
to a String
for @FormParam(...) String
, it seemed intuitive (to me, at least :) ) that the implementation should first check that it can convert to the parameter type using registered MBRs, and if it finds none, then it could check to see if a ParamConverter
is registered for the parameter type. If so, then the implementation would convert the part's InputStream
to String
and then invoke the ParamConverter
.
For Part#getContent(...)
, I don't think it makes sense to use ParamConverter
because at this point, it's not a parameter; the parameter was Part
. A MBR does make sense because it is reading the message body of the individual part to the specified class or GenericType
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, I appreciate the thoroughness of your review. I'd certainly rather spend time up front to get it right rather than trying to patch something that was broken from the start. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@andymc12 Thanks for your explanation and sorry for the delayed reply.
After thinking about this more, it somehow makes sense to allow ParamConverter
in case of @FormParam
, as it is also used for standard @FormParam
cases. However, I also think it could be a bit weird for the user that @FormParam
and getContent
may behave differently.
So basically we have three options:
@FormParam
uses the MBR and falls back to aParamConverter
(if available), butgetContent()
only uses the MBR.- Both
@FormParam
andgetContent()
use the MBR and fall back toParamConverter
. - Both
@FormParam
andgetContent()
only use the MBR.
To be honest, I'm not sure yet which option I personally like most.
@eclipse-ee4j/ee4j-jaxrs-committers Other opinions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation of ParamConverter
says it applies to parameters injected via (among other annotations) @FormParam
.:
Defines a contract for a delegate responsible for converting between a String form of a message parameter value and the corresponding custom Java type T. Conversion of message parameter values injected via
@PathParam
,@QueryParam
,@MatrixParam
,@FormParam
,@CookieParam
and@HeaderParam
is supported.
I am not aware of an existing API method that would utilize converters. Therefore to maintain consistency, I'd suggest staying with option 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@FormParam
uses the MBR and falls back to aParamConverter
(if available), butgetContent()
only uses the MBR.- Both
@FormParam
andgetContent()
use the MBR and fall back toParamConverter
.- Both
@FormParam
andgetContent()
only use the MBR.
getContent()
is not intended for parameters, so it shouldn't use ParamConverter
.
My preference would be 1.
Actually after some thinking I wonder why we want @FormParam
to use MBR, actually, as it is a parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my view @FormParam
on types other than EntityPart
is basically a convenient way to parse and access part data.
So instead of doing this:
public Response someMethod( List<EntityPart> parts ) {
JsonObject data = parts.stream()
.filter( p -> p.getId().equals("file") )
.findFirst()
.map( p -> p.getContent( JsonObject.class ) )
.orElse( null );
// ...
}
or instead of this:
public Response someMethod( @FormParam("file") EntityPart part ) {
JsonObject data = part.getContent( JsonObject.class );
// ...
}
You can simply do this:
public Response someMethod( @FormParam("file") JsonObject data ) {
// ...
}
And therefore it would make sense that using @FormParam
on some specific type T
is the same as calling EntityPart.getContent(T.class)
.
However, I agree that this may be problematic given the originally intended usage of ParamConverter
and MessageBodyReader
.
@andymc12 |
Speaking in terms of Java, it would be |
I see that I like Markus's idea of |
Another idea would be to actually use |
What if we also left in the ability to automatically convert to
and
This should avoid the confusion with ParamConverters, but still allow some of the more common use cases - like writing a part to disk or reading it directly as a String.
That should be fine. The only difference between your 2nd block and 3rd is one line of code. I can live with that! :-) |
This seems like a reasonable solution at this time; getting the param converters in the mix seems reasonable but also adds additional complexity. |
Signed-off-by: Andy McCright <j.andrew.mccright@gmail.com>
- "ex:" to "e.g." - Using modern java.nio.file examples instead of java.io Signed-off-by: Andy McCright <j.andrew.mccright@gmail.com>
- Change "entityStream" to "content" - Indicate who must close content streams - Update examples to use new method name / close behavior Signed-off-by: Andy McCright <j.andrew.mccright@gmail.com>
Also, add `withFileName` convenience method and document the encoding charset to use when converting a part's content. Signed-off-by: Andy McCright <j.andrew.mccright@gmail.com>
Signed-off-by: Andy McCright <j.andrew.mccright@gmail.com>
Signed-off-by: Andy McCright <j.andrew.mccright@gmail.com>
Signed-off-by: Andy McCright <j.andrew.mccright@gmail.com>
- Moves `EntityPart` from `ext` to `core` package - Adds new `content` methods to `EntityPart.Builder` that use MBWs - Cleanup of references to old `Part` name updated to `EntityPart` Signed-off-by: Andy McCright <j.andrew.mccright@gmail.com>
Signed-off-by: Andy McCright <j.andrew.mccright@gmail.com>
Signed-off-by: Andy McCright <j.andrew.mccright@gmail.com>
eb54a83
to
b5319b0
Compare
The latest commit should address @chkal's question - basically allowing code blocks 1 & 2, but not 3. It also allows @spericas @chkal @mkarg @pdudits @jansupol @deki - thanks for all of the discussion and reviews! Can I ask that you take another look and approve? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
@andymc12 Thanks a lot for all the adjustments. :-)
Hi, I have a simple remark: can't |
Not directly, but you can specify headers when building an
or
|
@andymc12 Great feature! Thank you for driving this! |
Fixes #418
Includes new API, spec text and some simple examples.