3
votes

État HTTP 406 - Non acceptable [Diffusion de données volumineuses depuis le backend à l'aide de spring 4.3.x + Java 8]

J'utilise Spring mvc 4.3.x, java 8, Tomcat 7

CODE:

@Controller
public class StreamRecordsController {

    @RequestMapping(value = "/streamrecords", method = RequestMethod.GET, consumes = MediaType.ALL_VALUE, 
            produces = "application/octet-stream")
    @ResponseBody
    public ResponseEntity<StreamingResponseBody> export() throws FileNotFoundException {
        File file = new File("C:\\Users\\Ankur\\sample.pdf");
        StreamingResponseBody responseBody = outputStream -> {
            Files.copy(file.toPath(), outputStream);
        };
        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=generic_file_name.pdf")
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(responseBody);
    }
}

EXCEPTION:

 entrez la description de l'image ici

INSTANTANÉ POSTMAN

 entrez la description de l'image ici QUESTION:

Que me manque-t-il ici?


3 commentaires

Il dit que le client (par exemple le navigateur) s'attend à ce qu'une sorte de contenu X (via l'en-tête Accept , par exemple application / json ) soit envoyé par le serveur, mais le serveur ne fournit pas un tel contenu sur un point final donné (par exemple, ne produit que du XML).


Essayez sans aucun en-tête Accept dans le facteur.


@Antoniossss Vous êtes un génie. Je vous remercie


5 Réponses :


1
votes

Il semble qu'il vous manque 2 choses:

Tout d'abord, votre code renvoie MediaType.APPLICATION_OCTET_STREAM comme type de contenu. Ce serait bien si vous disiez à Spring que la méthode export () produit ce type. Vous pouvez le faire avec l'attribut produit de @RequestMapping .

Deuxièmement, votre navigateur ne demande pas APPLICATION_OCTET_STREAM - vous pouvez le voir par la valeur d'en-tête Accept . APPLICATION_OCTET_STREAM correspond à application / octet-stream - votre demande du navigateur devra l'inclure dans la valeur d'en-tête Accept , afin que Spring puisse reconnaissez, quelle méthode doit être appelée dans votre contrôleur.

Modifier: après avoir résolu le problème, jetez un œil sur l'annotation @RestController qui peut être utilisée à la place de @Controller - vous n'aurez pas à ajouter Annotation @ResponseBody , car elle sera incluse par défaut. Regardez également @GetMapping qui est une superposition pour l'annotation @RequestMapping pour les méthodes HTTP GET.


2 commentaires

Merci beaucoup pour votre temps et vos commentaires. Je vous tiendrai au courant sous peu.


La sortie est toujours la même, veuillez trouver le code mis à jour ci-dessus.



1
votes

406 Not Acceptable

La ressource identifiée par la requête est uniquement capable de générer des entités de réponse dont les caractéristiques de contenu ne sont pas acceptables selon les en-têtes d'acceptation envoyés dans la requête.

Essayez d'utiliser l'annotation Produces dans votre méthode de contrôleur:

@Produces({MediaType.APPLICATION_JSON})


1 commentaires

La sortie est toujours la même, veuillez trouver le code mis à jour ci-dessus.



1
votes

Ce qui vous arrive est peut-être un bogue Spring MVC. Avec les navigateurs et cela varie également selon les navigateurs - Accepter l'en-tête dans la demande est obligatoire avec des valeurs correctes. Vous pouvez avoir un comportement différent dans un navigateur différent et votre code peut fonctionner dans une interface utilisateur swagger.

Selon votre capture d'écran, votre en-tête Accept ne correspond pas au type de réponse, il doit être * / * car votre code est spécial dans le sens où vous ne prenez aucune entrée qui soit un scénario rare dans le monde REST.

Je suggère donc d'ajouter des consommations dans votre cartographie comme ci-dessous et de voir si cela fonctionne,

@RequestMapping (value = "/ streamrecords", method = RequestMethod.GET, consumes = MediaType.ALL_VALUE)

et assurez-vous que l'en-tête Accept dans la requête porte la valeur - * / * .


10 commentaires

Merci Sabir, Je vais vous mettre à jour sous peu à ce sujet .... Permettez-moi de vérifier les changements ci-dessus.


La sortie est toujours la même, veuillez trouver le code mis à jour ci-dessus.


Quel est votre client Rest? Du code JavaScript / Angular, etc. ou autre chose? Essayez de définir la valeur d'en-tête Accept sur * / * dans votre client, car elle doit être définie dans l'en-tête de la demande.


My Rest Client est un facteur et un navigateur Web. Si je me souviens bien, j'ai essayé * / * ... les choses n'ont pas fonctionné


Swagger lance ceci - curl -X GET "http: // localhost: 7001 / streamrecords" -H "accept: * / *" et cela fonctionne. Votre navigateur a-t-il montré que la valeur de l'en-tête Accepter était * / * après que votre code ait changé ou que la valeur soit restée la même que celle indiquée dans la capture d'écran initiale?


Swagger me donne la possibilité de télécharger le fichier et c'est la même chose dans Postman si je définis Accept = * / * , c'est-à-dire que le statut HTTP est de 200 pour les deux clients. Si la valeur d'en-tête Accept reste la même même après avoir explicitement défini cette valeur d'en-tête, vous devez publier ce code et les captures d'écran du facteur.


Avez-vous utilisé Spring Boot? Si oui, je n'utilise pas Spring Boot ici. C'est du pur Spring MVC 4.x


Oui, je suis sur la version Spring Boot - 1.5.2.RELEASE .


Merci pour la réponse, pouvez-vous me dire comment pouvons-nous y parvenir sans utiliser Spring Boot.


Dans ce cas, il peut s'agir de bibliothèques json manquantes / incompatibles - ce n'est que le point de départ. Je suggère de télécharger votre code complet quelque part et de fournir un lien.



1
votes

Cette erreur indique que le client (par exemple, le navigateur) s'attend à ce qu'une sorte de contenu X (via l'en-tête Accept par exemple, application / json) soit envoyé par le serveur, mais le serveur ne fournit pas ce contenu sur un point final donné (par exemple, ne produit que du XML) .

Dans votre cas, vous "acceptez" plusieurs formats, mais aucun d'entre eux n'est application / octet-stream et c'est ce que vous avez déclaré que le serveur renverra dans @RequestMapping ( produit une annotation = "application / octet-stream") . Incluez-le dans votre navigateur d'en-tête Accept .


1 commentaires

Merci pour la réponse, pouvez-vous me dire comment inclure l'en-tête Accepter dans le navigateur Chrome?



1
votes

Enfin, j'ai pu régler ce problème, ce qui m'aide maintenant à diffuser d'énormes données du backend vers le frontend en utilisant Spring 4.3.x comme mentionné dans mon article. Voici les points pour vous guider pour exécuter le programme avec succès.

La procédure ci-dessous est si efficace que vous pouvez même paginer d'énormes données à l'arrière (peut être mise en veille prolongée, pilote Mongo-java, pilote java cassandra, etc.) et continuez à diffuser les données à moins que votre opération de base de données ne soit terminée. Dans certains domaines comme la fabrication, l'assurance, la logistique, etc., vous avez besoin d'un tel utilitaire où l'utilisateur final attend d'énormes données silencieuses du serveur sous la forme de CSV, JSON, etc. pour analyser les données brutes.

  1. Ajoutez une autre annotation @EnableWebMvc au-dessus de votre classe de contrôleur.

  2. Lorsque vous ajoutez l'annotation ci-dessus, le code se cassera lors de l'exécution, vous pouvez voir dans catalina.log , cette erreur: java.lang.NoClassDefFoundError: com / plus rapidexml / jackson / core / util / DefaultIndenter

  3. Pour résoudre ce problème, vous devrez ajouter ci-dessous la dépendance jar dans votre pom.xml

    package com.emg.server.controller.rest;
    
    import java.io.File;
    import java.nio.file.Files;
    
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.MediaType;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
    
    @Controller
    @EnableWebMvc
    public class StreamRecordsController {
    
        @RequestMapping(value = "/streamrecords", method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
        @ResponseBody
        public ResponseEntity<StreamingResponseBody> export() {
             File file = new File("C:\\Users\\Ankur\\sample.pdf");
                StreamingResponseBody responseBody = outputStream -> {
                    Files.copy(file.toPath(), outputStream);
                };
                return ResponseEntity.ok()
                        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=generic_file_name.pdf")
                        .contentType(MediaType.APPLICATION_OCTET_STREAM)
                        .body(responseBody);
            }
    }
    
  4. Maintenant, ajoutez true dans web.xml sous tag comme exemple ci-dessous,

    package com.emg.server.controller.rest;
    
    import java.io.IOException;
    import java.io.OutputStream;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
    
    @Controller
    @EnableWebMvc
    public class StreamRecordsController {
    
        @RequestMapping(value = "/streamrecords")
        @ResponseBody
        public StreamingResponseBody export() {
            return new StreamingResponseBody() {
                @Override
                public void writeTo (OutputStream out) throws IOException {
                    for (int i = 0; i < 1000; i++) {
                        out.write((Integer.toString(i) + " - ")
                                            .getBytes());
                        out.flush();
                        try {
                            Thread.sleep(5);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
        }
    }
    

Vous trouverez ci-dessous le code pour le téléchargement de FILE Streaming et la prise en charge du flux de données.

CODE DE FLUX DE DONNÉES:

<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
</servlet>


0 commentaires