1
votes

'Erreur de désérialisation de l'objet du flux d'entité' après la soumission d'un multipart / form-data à l'aide de JavaScript / AJAX au backend JAX-RS

Je développe une application Web RESTful avec une interface JavaScript et un backend JAX-RS avec Netbeans 8.2 jusqu'à présent, tout fonctionne très bien. J'ai un formulaire dans lequel un utilisateur télécharge son image et je voudrais enregistrer cette image en tant que blob dans ma base de données (je connais les lacunes mais je voudrais procéder de cette façon). Veuillez consulter les extraits ci-dessous.

Premièrement, la demande POST lors de l'envoi du formulaire:

Severe:   javax.ws.rs.ProcessingException: Error deserializing object from entity stream.
javax.ws.rs.ProcessingException: Error deserializing object from entity stream.
    at org.glassfish.jersey.jsonb.internal.JsonBindingProvider.readFrom(JsonBindingProvider.java:101)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.invokeReadFrom(ReaderInterceptorExecutor.java:257)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:236)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:156)
    at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundReadFrom(MappableExceptionWrapperInterceptor.java:73)
    at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:156)
    at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1093)
    at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:874)
    at org.glassfish.jersey.server.ContainerRequest.readEntity(ContainerRequest.java:271)
    at org.glassfish.jersey.server.internal.inject.EntityParamValueParamProvider$EntityValueSupplier.apply(EntityParamValueParamProvider.java:98)
    at org.glassfish.jersey.server.internal.inject.EntityParamValueParamProvider$EntityValueSupplier.apply(EntityParamValueParamProvider.java:81)
    at org.glassfish.jersey.server.spi.internal.ParamValueFactoryWithSource.apply(ParamValueFactoryWithSource.java:75)
    at org.glassfish.jersey.server.spi.internal.ParameterValueHelper.getParameterValues(ParameterValueHelper.java:93)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$AbstractMethodParamInvoker.getParamValues(JavaResourceMethodDispatcherProvider.java:133)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:200)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:103)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:493)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:415)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:104)
    at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:277)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:272)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:268)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:316)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:298)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:268)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:289)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:256)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:704)
    at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:416)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:370)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:389)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:342)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:229)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1628)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:258)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:755)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:575)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:159)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:371)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:238)
    at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:516)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:213)
    at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:182)
    at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:156)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:218)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:95)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:260)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:177)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:109)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:88)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:53)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:524)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:89)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:94)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:33)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:114)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:569)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:549)
    at java.lang.Thread.run(Thread.java:745)
Caused by: javax.json.bind.JsonbException: Internal error: Event START_ARRAY not found. Last data: [EVENT: START_OBJECT KEY_NAME: candidatePicture]
    at org.eclipse.yasson.internal.JsonbRiParser.moveTo(JsonbRiParser.java:203)
    at org.eclipse.yasson.internal.serializer.AbstractArrayDeserializer.moveToFirst(AbstractArrayDeserializer.java:82)
    at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserializeInternal(AbstractContainerDeserializer.java:75)
    at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserialize(AbstractContainerDeserializer.java:61)
    at org.eclipse.yasson.internal.serializer.ObjectDeserializer.deserializeNext(ObjectDeserializer.java:165)
    at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserializeInternal(AbstractContainerDeserializer.java:85)
    at org.eclipse.yasson.internal.serializer.AbstractContainerDeserializer.deserialize(AbstractContainerDeserializer.java:61)
    at org.eclipse.yasson.internal.Unmarshaller.deserializeItem(Unmarshaller.java:62)
    at org.eclipse.yasson.internal.Unmarshaller.deserialize(Unmarshaller.java:52)
    at org.eclipse.yasson.internal.JsonBinding.deserialize(JsonBinding.java:45)
    at org.eclipse.yasson.internal.JsonBinding.fromJson(JsonBinding.java:85)
    at org.glassfish.jersey.jsonb.internal.JsonBindingProvider.readFrom(JsonBindingProvider.java:99)
    ... 60 more

Deuxièmement, le point de terminaison de la ressource:

 @POST
@Path("{ipaddress}/{electionid}")
@ApiOperation(value = "Create new ballot item", notes = "This can only be done by logged in users.")
@ApiResponses(value = {
    @ApiResponse(code = 400, message = "Invalid ballot item Input"),
    @ApiResponse(code = 200, message = "Ballot item created")})
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public Response add(
        @ApiParam(value = "The ipaddress of the ballot item to be created", required = true) @PathParam("ipaddress") String ip,
        @ApiParam(value = "The election's id", required = true) @PathParam("electionid") String electionid,
        @ApiParam(value = "The ballot item that needs to be added", required = true) BallotItem newBallot) {

    Users curUser = UsersService.find(1l); 
    Election election = electionService.find(Long.parseLong(electionid));
    newBallot.setElectionID(election);
    ballotsService.create(newBallot);
    operationsService.create(new Operation(curUser, "Created a new ballot item", "Candidate name: " + newBallot.getCandidateName() + " and Election ID: " + electionid, new Date(), ip));
    return Response.ok(newBallot).build();
}


3 commentaires

Il semble qu'un tableau soit attendu en tant que valeur de candidatePicture, mais il n'y a qu'une valeur de texte dans les données JSON. Quelle est la valeur de e.srcElement.result? Pouvez-vous partager le code de BallotItem?


@OndroMih voici la valeur de e.srcElement.result drive.google. com / file / d / 1t_qIojtR3YUPXDlAbtf01AGrodjvnfCv /…


@OndroMih Et voici la classe de BallotItem drive.google.com/drive/folders/…


3 Réponses :


1
votes

le problème est que votre classe BallotItem contient le champ byte [] candidatePicture , que la liaison JSON ne sait pas définir par défaut à partir du JSON entrant. Le JSON contient juste une chaîne avec des données binaires (je ne sais pas si cela est encodé avec base64). Un serveur Jakarta EE tente d'enregistrer la chaîne dans un tableau d'octets et échoue.

Cela peut être un bogue dans votre serveur Jakarta EE ou cela peut ne pas être spécifié. La spécification JSON-Binding http://json-b.net/ ne mentionne pas comment cela devrait fonctionner . Il spécifie uniquement qu'un tableau d'octets est converti en JSON en utilisant la stratégie "byte" par défaut, mais aucune mention sur la façon dont les données binaires sont converties à partir de JSON.

Je suggère d'ajouter un désérialiseur personnalisé à votre champ candidatePicture dans BallotItem avec JsonbTypeDeserializer . Cela devrait fonctionner mais nécessite quelques lignes de code à implémenter.


2 commentaires

Merci pour cette explication détaillée; cela a beaucoup de sens. Je vais essayer avec JsonbTypeDeserializer et vous faire savoir que cela fonctionne.


Je n'ai trouvé aucun cas d'utilisation de JsonbTypeDeserializer sur Internet. On dirait que personne n'a jamais eu ce problème?



3
votes

JSONB prend en charge la désérialisation des tableaux d'octets. Le problème est votre cas d'utilisation particulier.

Par défaut, l'encodage BYTE est utilisé. Dans ce cas, votre propriété candidatePicture doit être un tableau et ressembler à ceci:

"candidatePicture":"WWFzc29uIGlzIHBlcmZlY3Qh"

Je vois que ce n'est pas votre cas.

Si l'encodage BASE_64 est utilisé, ce qui est un peu standard, vous devriez dire au moteur JSONB de l'utiliser dans une configuration personnalisée:

JsonbConfig config = new JsonbConfig()
                .withBinaryDataStrategy(BinaryDataStrategy.BASE_64);
Jsonb jsonb = JsonbBuilder.newBuilder()
                .withConfig(config)
                .build();
jsonb.fromJson(...);

Dans ce cas, votre propriété devrait être un BASE_64 chaîne encodée et ressemble à ceci:

"candidatePicture":[89,97,115,115,111,110,32,105,115,32,112,101,114,102,101,99,116,33]

Je suppose que c'est votre cas, mais votre chaîne ne ressemble pas à l'encodage BASE_64. Essayez-le, si cela ne fonctionne pas, vérifiez quel encodage vous utilisez pour encoder vos données binaires.


1 commentaires

Merci @Dmitry pour votre réponse. S'il vous plaît, j'ai besoin de quelques éclaircissements. La propriété candidatePicture doit être un tableau de quoi? entiers? De plus, j'ai utilisé BASE_64 une fois, mais le formulaire a mis beaucoup de temps à se soumettre. J'ai donc décidé de convertir l'image blob en texte. Supposons que je décide d'utiliser le codage BASE_64, où puis-je dire au moteur JSONB d'utiliser BASE_64 dans une configuration personnalisée?



1
votes

Assurez-vous que le constructeur est par défaut, le constructeur vide est déclaré public et non par défaut


0 commentaires