Quarkus JAX-RS Service Multipart Form Upload - «Не может использовать тип контента» - PullRequest
2 голосов
/ 05 февраля 2020

Я пишу сервис JAX-RS с использованием Quarkus. Мой сервис должен создать простой профиль пользователя с именем, датой рождения и т. Д. c. вместе с загрузкой файла изображения. Я пытаюсь сделать это как многочастную загрузку формы, но продолжаю сталкиваться с ошибками.

Мое определение сервиса выглядит примерно так ...

@Path("/profile")
@POST
@Consumes({MediaType.MULTIPART_FORM_DATA})
@Produces({ MediaType.APPLICATION_JSON })
public Response createStudentProfile(@MultipartForm ProfileFormBean profile);

Мой класс ProfileFormBean выглядит вот так ...

public class ProfileFormBean {

    @FormParam("firstName")
    @PartType(MediaType.TEXT_PLAIN)
    private String firstName = null;

    @FormParam("lastName")
    @PartType(MediaType.TEXT_PLAIN)
    private String lastName = null;

    @FormParam("profileImage")
    @PartType(MediaType.APPLICATION_OCTET_STREAM)
    private byte[] profileImage = null;

    ...
}

Насколько я могу судить, этого должно быть достаточно, чтобы мой сервис мог принимать загрузки файлов. Я пытаюсь проверить это с помощью curl, но когда я это делаю, я получаю исключение. Моя команда curl просто пытается загрузить сам файл, но не задает свойства имени / фамилии:

curl -vki -H "Authorization: Bearer ${ACCESS_TOKEN}" \
    -H 'Accepts: application/json' \
    -F 'profileImage=@profile_image.jpg' \
    http://localhost:8081/profile

Вывод этой команды выглядит следующим образом:

curl -vki -H "Authorization: Bearer ${ACCESS_TOKEN}" \
> -H 'Accepts: application/json' \
> -F 'profileImage=@profile_image.jpg' \
> http://localhost:8081/profile
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8081 (#0)
> POST /profile HTTP/1.1
> Host: localhost:8081
> User-Agent: curl/7.54.0
> Accept: */*
> Authorization: Bearer eyJhbGciOiJSUzI1N...
> Accepts: application/json
> Content-Length: 11701
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=------------------------49161ebc82f19511
> 
< HTTP/1.1 415 Unsupported Media Type
HTTP/1.1 415 Unsupported Media Type
< Content-Length: 0
Content-Length: 0

* HTTP error before end of send, stop sending
< 
* Closing connection 0

И на на сервере при запуске я вижу следующее исключение:

15:26:35 DEBUG [org.jboss.resteasy.resteasy_jaxrs.i18n] RESTEASY002305: Failed executing POST /profile: javax.ws.rs.NotSupportedException: RESTEASY003065: Cannot consume content type
    at org.jboss.resteasy.core.registry.SegmentNode.match(SegmentNode.java:453)
    at org.jboss.resteasy.core.registry.SegmentNode.match(SegmentNode.java:155)
    at org.jboss.resteasy.core.registry.RootNode.match(RootNode.java:58)
    at org.jboss.resteasy.core.registry.RootClassNode.match(RootClassNode.java:47)
    at org.jboss.resteasy.core.ResourceMethodRegistry.getResourceInvoker(ResourceMethodRegistry.java:482)
    at org.jboss.resteasy.core.SynchronousDispatcher.getInvoker(SynchronousDispatcher.java:323)
    at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:244)
    at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:153)
    at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:363)
    at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:156)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:238)
    at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:249)
    at io.quarkus.resteasy.runtime.ResteasyFilter.doFilter(ResteasyFilter.java:30)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
    at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:63)
    at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
    at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:133)
    at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
    at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:65)
    at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
    at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
    at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
    at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:270)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:59)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:116)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:113)
    at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
    at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
    at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$9$1$1.call(UndertowDeploymentRecorder.java:476)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:250)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:59)
    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:82)
    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:290)
    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:669)
    at io.quarkus.runtime.CleanableExecutor$CleaningRunnable.run(CleanableExecutor.java:224)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
    at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2011)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1535)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1395)
    at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
    at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
    at java.base/java.lang.Thread.run(Thread.java:830)
    at org.jboss.threads.JBossThread.run(JBossThread.java:479)

Я что-то упустил? На сайте Quarkus я нашел документацию по созданию клиентов REST с несколькими частями , но ничего для ресурсов на стороне сервера. Есть ли что-то еще, что мне нужно сделать, чтобы это работало?

1 Ответ

2 голосов
/ 06 февраля 2020

Проблема с

@FormParam("profileImage")
@PartType(MediaType.APPLICATION_OCTET_STREAM)
private byte[] profileImage = null;

Вы рекламируете, что единственным типом контента, который вы разрешаете для этой части, является application/octet-stream. Но вы отправляете изображение, и cURL не устанавливает для него тип содержимого изображения. Я бы предположил, что под капотом он устанавливает image/jpg. Вы можете вручную установить тип (и это исправит этот запрос)

profileImage=@profile_image.jpg;type=application/octet-stream

Но лично я бы просто удалил @PartType. Поскольку тип поля уже byte[], проблем с преобразованием не будет. Кроме того, многие клиенты не могут установить тип содержимого отдельных частей. Как и в браузере / JS клиенты будут по умолчанию устанавливать тип содержимого для файлов, как это делал cURL.

...