dgs-framework
dgs-framework copied to clipboard
bug: File upload doesn't work with Spring WebFlux
Expected behavior
According to DGS document and GraphQL multipart request specification, file upload should work as expected.
Actual behavior
com.netflix.graphql.dgs.webflux.handlers.DefaultDgsWebfluxHttpHandler
will treat form-data requests as application/json
and use com.fasterxml.jackson.databind.ObjectMapper
to parse the requests. Then the framework will throw the following exception:
Error has been observed at the following site(s):
*_______________________________________Mono.map ⇢ at com.netflix.graphql.dgs.webflux.handlers.DefaultDgsWebfluxHttpHandler.graphql(DefaultDgsWebfluxHttpHandler.kt:35)
|_ Mono.flatMap ⇢ at com.netflix.graphql.dgs.webflux.handlers.DefaultDgsWebfluxHttpHandler.graphql(DefaultDgsWebfluxHttpHandler.kt:48)
|_ Mono.flatMap ⇢ at com.netflix.graphql.dgs.webflux.handlers.DefaultDgsWebfluxHttpHandler.graphql(DefaultDgsWebfluxHttpHandler.kt:61)
*___________________________________Mono.flatMap ⇢ at org.springframework.cloud.sleuth.instrument.web.TraceHandlerFunction.handle(TraceHandlerFunction.java:54)
|_ Mono.doFinally ⇢ at org.springframework.cloud.sleuth.instrument.web.TraceHandlerFunction.handle(TraceHandlerFunction.java:54)
|_ Mono.map ⇢ at org.springframework.web.reactive.function.server.support.HandlerFunctionAdapter.handle(HandlerFunctionAdapter.java:62)
*___________________________________Mono.flatMap ⇢ at org.springframework.web.reactive.DispatcherHandler.handle(DispatcherHandler.java:153)
|_ Mono.flatMap ⇢ at org.springframework.web.reactive.DispatcherHandler.handle(DispatcherHandler.java:154)
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
*______________________________________Mono.then ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.onAuthenticationSuccess(AuthenticationWebFilter.java:135)
*___________________________________Mono.flatMap ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.authenticate(AuthenticationWebFilter.java:124)
|_ Mono.doOnError ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.authenticate(AuthenticationWebFilter.java:126)
*___________________________________Mono.flatMap ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.filter(AuthenticationWebFilter.java:114)
|_ Mono.onErrorResume ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.filter(AuthenticationWebFilter.java:115)
|_ checkpoint ⇢ com.hohomalls.web.filter.AuthenticationFilter [DefaultWebFilterChain]
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
*_____________________________Mono.switchIfEmpty ⇢ at org.springframework.security.web.server.authorization.AuthorizationWebFilter.filter(AuthorizationWebFilter.java:55)
|_ checkpoint ⇢ org.springframework.security.web.server.authorization.AuthorizationWebFilter [DefaultWebFilterChain]
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
|_ Mono.onErrorResume ⇢ at org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter.filter(ExceptionTranslationWebFilter.java:58)
|_ checkpoint ⇢ org.springframework.security.web.server.authorization.ExceptionTranslationWebFilter [DefaultWebFilterChain]
*__Operators$MultiSubscriptionSubscriber.onError ⇢ at org.springframework.cloud.sleuth.instrument.reactor.ScopePassingSpanSubscriber.onError(ScopePassingSpanSubscriber.java:96)
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
*___________________________________Mono.flatMap ⇢ at org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter.filter(ServerRequestCacheWebFilter.java:39)
|_ checkpoint ⇢ org.springframework.security.web.server.savedrequest.ServerRequestCacheWebFilter [DefaultWebFilterChain]
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
|_ checkpoint ⇢ org.springframework.security.web.server.context.SecurityContextServerWebExchangeWebFilter [DefaultWebFilterChain]
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
*______________________________________Mono.then ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.onAuthenticationSuccess(AuthenticationWebFilter.java:135)
*___________________________________Mono.flatMap ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.authenticate(AuthenticationWebFilter.java:124)
|_ Mono.doOnError ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.authenticate(AuthenticationWebFilter.java:126)
*___________________________________Mono.flatMap ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.filter(AuthenticationWebFilter.java:114)
|_ Mono.onErrorResume ⇢ at org.springframework.security.web.server.authentication.AuthenticationWebFilter.filter(AuthenticationWebFilter.java:115)
|_ checkpoint ⇢ com.hohomalls.web.filter.AuthenticationFilter [DefaultWebFilterChain]
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
|_ checkpoint ⇢ org.springframework.security.web.server.context.ReactorContextWebFilter [DefaultWebFilterChain]
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
|_ checkpoint ⇢ org.springframework.security.config.web.server.ServerHttpSecurity$ServerWebExchangeReactorContextWebFilter [DefaultWebFilterChain]
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
*___________________________________Mono.flatMap ⇢ at org.springframework.security.web.server.WebFilterChainProxy.filter(WebFilterChainProxy.java:56)
|_ checkpoint ⇢ org.springframework.security.web.server.WebFilterChainProxy [DefaultWebFilterChain]
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
|_ checkpoint ⇢ org.springframework.cloud.sleuth.instrument.web.TraceWebFilter [DefaultWebFilterChain]
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
|_ Mono.doOnEach ⇢ at org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter.filter(MetricsWebFilter.java:87)
|_ Mono.doOnCancel ⇢ at org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter.filter(MetricsWebFilter.java:88)
*_________________________Mono.transformDeferred ⇢ at org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter.filter(MetricsWebFilter.java:82)
|_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
|_ checkpoint ⇢ com.hohomalls.web.filter.CorsFilter [DefaultWebFilterChain]
*_____________________________________Mono.defer ⇢ at org.springframework.web.server.handler.DefaultWebFilterChain.filter(DefaultWebFilterChain.java:119)
|_ Mono.onErrorResume ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler.handle(ExceptionHandlingWebHandler.java:77)
*_____________________________________Mono.error ⇢ at org.springframework.web.server.handler.ExceptionHandlingWebHandler$CheckpointInsertingHandler.handle(ExceptionHandlingWebHandler.java:98)
|_ checkpoint ⇢ HTTP POST "/graphql" [ExceptionHandlingWebHandler]
Original Stack Trace:
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:2391) ~[jackson-core-2.13.0.jar:2.13.0]
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:735) ~[jackson-core-2.13.0.jar:2.13.0]
at com.fasterxml.jackson.core.base.ParserMinimalBase.reportUnexpectedNumberChar(ParserMinimalBase.java:557) ~[jackson-core-2.13.0.jar:2.13.0]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._handleInvalidNumberStart(ReaderBasedJsonParser.java:1718) ~[jackson-core-2.13.0.jar:2.13.0]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser._parseNegNumber(ReaderBasedJsonParser.java:1467) ~[jackson-core-2.13.0.jar:2.13.0]
at com.fasterxml.jackson.core.json.ReaderBasedJsonParser.nextToken(ReaderBasedJsonParser.java:784) ~[jackson-core-2.13.0.jar:2.13.0]
at com.fasterxml.jackson.databind.ObjectMapper._initForReading(ObjectMapper.java:4762) ~[jackson-databind-2.13.0.jar:2.13.0]
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4668) ~[jackson-databind-2.13.0.jar:2.13.0]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3630) ~[jackson-databind-2.13.0.jar:2.13.0]
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3613) ~[jackson-databind-2.13.0.jar:2.13.0]
at com.netflix.graphql.dgs.webflux.handlers.DefaultDgsWebfluxHttpHandler.graphql$lambda-0(DefaultDgsWebfluxHttpHandler.kt:79) ~[graphql-dgs-spring-webflux-autoconfigure-4.9.14.jar:4.9.14]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:113) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:127) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:295) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1816) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:159) ~[reactor-core-3.4.12.jar:3.4.12]
at org.springframework.cloud.sleuth.instrument.reactor.ScopePassingSpanSubscriber.onComplete(ScopePassingSpanSubscriber.java:103) ~[spring-cloud-sleuth-instrumentation-3.1.0.jar:3.1.0]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onComplete(FluxMapFuseable.java:150) ~[reactor-core-3.4.12.jar:3.4.12]
at org.springframework.cloud.sleuth.instrument.reactor.ScopePassingSpanSubscriber.onComplete(ScopePassingSpanSubscriber.java:103) ~[spring-cloud-sleuth-instrumentation-3.1.0.jar:3.1.0]
at reactor.core.publisher.FluxPeekFuseable$PeekFuseableSubscriber.onComplete(FluxPeekFuseable.java:277) ~[reactor-core-3.4.12.jar:3.4.12]
at org.springframework.cloud.sleuth.instrument.reactor.ScopePassingSpanSubscriber.onComplete(ScopePassingSpanSubscriber.java:103) ~[spring-cloud-sleuth-instrumentation-3.1.0.jar:3.1.0]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:142) ~[reactor-core-3.4.12.jar:3.4.12]
at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:400) ~[reactor-netty-core-1.0.13.jar:1.0.13]
at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:419) ~[reactor-netty-core-1.0.13.jar:1.0.13]
at reactor.netty.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:590) ~[reactor-netty-http-1.0.13.jar:1.0.13]
at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:93) ~[reactor-netty-core-1.0.13.jar:1.0.13]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at reactor.netty.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:264) ~[reactor-netty-http-1.0.13.jar:1.0.13]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:324) ~[netty-codec-4.1.70.Final.jar:4.1.70.Final]
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:296) ~[netty-codec-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) ~[netty-transport-4.1.70.Final.jar:4.1.70.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986) ~[netty-common-4.1.70.Final.jar:4.1.70.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.70.Final.jar:4.1.70.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.70.Final.jar:4.1.70.Final]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
Steps to reproduce
I have used cURL, Postman and Altair GraphQL Client to test the file upload without luck.
- Graphql schema snippet or check it on mutations.graphqls:
scalar Upload
type Mutation {
# Upload a file and return the URL on the server
uploadFile(file: Upload!, rootDir: String!, subDir: String!): String
}
- Java method snippet or check it on FileDataFetcher.java:
@DgsData(parentType = "Mutation")
public Mono<String> uploadFile(DataFetchingEnvironment env) {
FileDataFetcher.log.info("Received a request to upload a file");
}
- Use cURL to upload the file:
curl http://localhost:8080/graphql \
-F operations='{"query":"mutation ($file: Upload!) {\n uploadFile(file: $file, rootDir: \"rootDir\", subDir: \"subDir\")\n}","variables":{"file":null},"operationName":null}' \
-F map='{ "0": ["variables.file"] }' \
-F [email protected]
- Error responses in cURL, Postman and Altair GraphQL Client:
{
"errors": [
{
"message": "Unexpected character ('-' (code 45)) in numeric value: expected digit (0-9) to follow minus sign, for valid numeric value\n at [Source: (String)\"------WebKitFormBoundaryq8A4KbH60pKaWgNR\r\nContent-Disposition: form-data; name=\"operations\"\r\n\r\n{\"query\":\"mutation ($file: Upload!) {\\n uploadFile(file: $file, rootDir: \\\"rootDir\\\", subDir: \\\"subDir\\\")\\n}\",\"variables\":{\"file\":null},\"operationName\":null}\r\n------WebKitFormBoundaryq8A4KbH60pKaWgNR\r\nContent-Disposition: form-data; name=\"map\"\r\n\r\n{\"0\":[\"variables.file\"]}\r\n------WebKitFormBoundaryq8A4KbH60pKaWgNR\r\nContent-Disposition: form-data; name=\"0\"; filename=\"movie.txt\"\r\nContent-Type: text/plain\r\"[truncated 60 chars]; line: 1, column: 3]",
"path": null,
"locations": [
"/graphql"
],
"extensions": {
"errorType": "INTERNAL",
"origin": "APP",
"debugInfo": {
"exceptionId": "625c0be7-4a5f-4a92-b2fc-6f8d45447ad9",
"exceptionName": "JsonParseException"
}
}
}
],
"status": 500
}
Thanks for reporting the issue @shiyouping.
Thanks, we do not support File uploads with webflux yet.
On Mon, Jan 17, 2022 at 12:11 PM Bernardo Gomez Palacio < @.***> wrote:
Thanks for reporting the issue @shiyouping https://github.com/shiyouping .
— Reply to this email directly, view it on GitHub https://github.com/Netflix/dgs-framework/issues/819#issuecomment-1014856346, or unsubscribe https://github.com/notifications/unsubscribe-auth/AJ5JPXLEEMMBET5AMWYPBDDUWRZXRANCNFSM5LTYPEUA . You are receiving this because you are subscribed to this thread.Message ID: @.***>
Any schedule to support it? Thanks in advance. @srinivasankavitha @berngp
We do not plan on adding support in the next few months at least, since WebFlux is not used internally. You are also the first to request this feature. We do welcome contributions from folks willing to help out with this.
On Fri, Jan 21, 2022 at 12:23 AM Shi Youping @.***> wrote:
Any schedule to support it? Thanks in advance.
— Reply to this email directly, view it on GitHub https://github.com/Netflix/dgs-framework/issues/819#issuecomment-1018284109, or unsubscribe https://github.com/notifications/unsubscribe-auth/AJ5JPXMT34YNS74RYUHZKOTUXEJZDANCNFSM5LTYPEUA . You are receiving this because you commented.Message ID: @.***>
I am having the same issue Would be great if this is supported in the future Thanks
Is there any way of doing a temporal fix or some hack in order to still use webflux but just use spring web mvc in the file uploads or I must have all the project with spring mvc? Because right now I am forced to use Base64 as inputs in my file uploads and this is not really well performant.
@zanonena You can still use Spring Webflux to upload the files without dgs-framework. Here is a sample code https://github.com/shiyouping/hohomalls/blob/master/server/hohomalls-web/src/main/java/com/hohomalls/web/controller/FileController.java
@zanonena You can still use Spring Webflux to upload the files without dgs-framework. Here is a sample code https://github.com/shiyouping/hohomalls/blob/master/server/hohomalls-web/src/main/java/com/hohomalls/web/controller/FileController.java
Thanks! but I must use GraphQL
I must admit that DGS library is well designed and I have never had any problems using it in WebFlux mode. That is, until I had to implement file uploads. The lack of file uploads in WebFlux is not documented, so I was very disappointed when I found this issue open.
I need file uploads, so I came up with workaround. This is essentially custom HTTP handler bean with few classes operating on Part
class instead of MultipartFile
. I'm not familiar with Kotlin unfortunately, so I can't make a PR at the moment. For those who may find it handy I'm leaving a link to my Java hack.
It would be great if maintainers implemented file uploads in WebFlux flavor!
Pretty, pretty please with sugar on top :wink:
@bartebor - Thanks for posting your solution for the workaround. Unfortunately, this feature is unlikely to be prioritized anytime soon due to internal conflicting requests. We do not use Webflux within Netflix yet. We are open to contributions though, so if anyone if interested, we would welcome the support!
Any chance this will be added soon or has been added?
Unfortunately this is not a priority for us since we do not use the webflux stack internally, and therefore unlikely we will support this in the future for webflux. We'll update our documentation to reflect this.
On Wed, Nov 8, 2023 at 6:29 PM hwhh @.***> wrote:
Any chance this will be added soon or has been added?
— Reply to this email directly, view it on GitHub https://github.com/Netflix/dgs-framework/issues/819#issuecomment-1803066427, or unsubscribe https://github.com/notifications/unsubscribe-auth/AJ5JPXLYDA7USO32EF56ZELYDQ5YPAVCNFSM5LTYPEUKU5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBQGMYDMNRUGI3Q . You are receiving this because you were mentioned.Message ID: @.***>
Hello.
Let me first say that I really like the DGS framework. It's great to work with. However I'm disappointed to find out that you're not considering handling this. This seems like a must have for anyone who wants to use DGS and works with webflux (which is probably an increasing number of users).
☝🏼In any case it would be great if you could add this limitation to the file upload documentation because it's not obvious where the issue comes from at first and it's only once I included "multipart" in my search terms that I found this issue.
Although I do understand why Netflix chooses not to do this, I'm wondering about what this would actually cost from its point of view considering @bartebor seems to have provided some code that would serve as a base for this and that the dev teams working on this are probably used to the code and can do that in a timely fashion. Is the cost that great ? It feels that it would be of great value for the community at least. Considering you would still have to take the time to review the code of any new contributor and that this new contributor would have to go through the process of understanding how the DGS code works, isn't there any amount of value in doing this ?
If I knew where to start I might actually be that contributor but I'm not sure what I would be getting into and I can't afford that right now. Maybe in a couple weeks once I've set up a workaround and delivered a first version of my project. But we all know how that generally goes.
I won't hide it, I hope you change you mind on the subject. 😁
Thanks again for all the work you're doing. It's much appreciated. 🫶🏼
As a side effect of some bigger news coming soon, this will actually be supported soon.
Awesome. Meanwhile if anyone is looking for a Kotlin version of the hack initially provided by @bartebor You'll find it here: https://gist.github.com/lthoulon-locala/02efaf339d9f6b8795bc48f425926efe
I reworked this based on the original Netflix files so that the code is as close as possible to theirs.