61 votes

Comment récupérer les données brutes d'une requête POST à partir de HttpServletRequest en Java

Je suis en train d'essayer d'obtenir les données du post en Java. On dirait que cela devrait être une des choses les plus simples à faire, non? Je veux dire, HttpServletRequest.getParameter doit le faire, non? Alors comment obtenir les données brutes du post?

J'ai trouvé HttpServletRequest get JSON POST data et utilisé le code de Kdeveloper pour extraire les données du post à partir d'une requête. Ça fonctionne, mais il y a un hic : je ne peux obtenir ces données en post qu'une seule fois.

Voici la méthode que j'ai faite à partir du code de Kdeveloper:

public static String getPostData(HttpServletRequest req) {
    StringBuilder sb = new StringBuilder();
    try {
        BufferedReader reader = req.getReader();
        reader.mark(10000);

        String line;
        do {
            line = reader.readLine();
            sb.append(line).append("\n");
        } while (line != null);
        reader.reset();
        // do NOT close the reader here, or you won't be able to get the post data twice
    } catch(IOException e) {
        logger.warn("getPostData couldn't.. get the post data", e);  // This has happened if the request's reader is closed    
    }

    return sb.toString();
}

Auparavant, j'avais fermé le lecteur à la fin de cette méthode, mais cela provoquait des exceptions lorsque la méthode était exécutée plus d'une fois sur la même demande. Sans le fermer, aucune exception ne se produit, mais la méthode renvoie une chaîne vide.

Honnêtement, il devrait tout simplement y avoir une méthode exposée req.getPostData() - est-ce que personne n'a pensé que ce serait utile?

Alors comment puis-je écrire cette méthode de manière à ce qu'elle renvoie toujours les bonnes données post?

92voto

BalusC Points 498232

Le corps de la requête est disponible sous forme de flux de bytes par HttpServletRequest#getInputStream():

InputStream body = request.getInputStream();
// ...

Ou sous forme de flux de caractères par HttpServletRequest#getReader():

Reader body = request.getReader();
// ...

Notez que vous ne pouvez le lire qu'une seule fois. Le client ne va pas renvoyer la même requête plusieurs fois. L'appel à getParameter() et ainsi de suite le lira implicitement également. Si vous avez besoin de décomposer les paramètres plus tard, vous devez stocker le corps quelque part et le traiter vous-même.

3 votes

Donc votre réponse est qu'il n'y a aucun moyen de faire ce que je veux faire? Ce n'est pas une question que le client l'envoie plus d'une fois. La HttpServletRequest stocke clairement les données de publication internalement quelque part (comme vous pouvez toujours obtenir des paramètres de publication plusieurs fois). Je vous remercie pour la réponse, je veux juste comprendre pleinement si vous dites "c'est impossible" ou si vous réitérez simplement ce que j'ai déjà découvert.

14 votes

Lors du premier appel de getParameter(), la HttpServletRequest utilisera en interne getInputStream() pour lire et analyser le corps de la requête (c'est un flux de bytes d'une connexion réseau) et le stocker dans une map que vous pouvez obtenir en utilisant getParameterMap(). Après ce point, vous ne pouvez plus lire le corps de la requête avec getInputStream()/getReader(), car il a déjà été lu. Si vous précisez davantage l'exigence fonctionnelle derrière ce besoin, nous pourrions peut-être vous suggérer de meilleures façons de le réaliser.

0 votes

Nous avons un système de taille moyenne qui passe des objets HttpServletRequest partout, et nous avons eu des moyens moins précis d'afficher ce que sont les données postées (auparavant, nous utilisions une technique qui compare la map des paramètres à la chaîne de requête et suppose que la différence est des données postées). La plupart de ces choses sont des journaux internes, mais nous avons également un cas où un partenaire nous envoie du XML en tant que données postées. Dans ce cas, utiliser getReader pour obtenir les données postées parfois perturber notre journalisation, et parfois notre journalisation perturbera la gestion de la requête réelle.

7voto

Dano64 Points 154

Nous avons eu une situation où IE nous a forcés à poster en tant que text/plain, nous avons donc dû analyser manuellement les paramètres en utilisant getReader. Le servlet était utilisé pour le long polling, donc lorsque AsyncContext::dispatch était exécuté après un délai, il repostait littéralement la demande les mains vides.

Donc j'ai simplement stocké le post dans la demande lorsqu'il est apparu en premier en utilisant HttpServletRequest::setAttribute. La méthode getReader vide le tampon, où getParameter vide également le tampon mais stocke automatiquement les paramètres.

String input = null;

// nous devons stocker la chaîne, qui ne peut être lue qu'une seule fois, car lorsque le
// servlet réveille un AsyncContext, il repost la demande et revient ici les mains vides
if ((input = (String) request.getAttribute("com.xp.input")) == null) {
    StringBuilder buffer = new StringBuilder();
    BufferedReader reader = request.getReader();

    String line;
    while((line = reader.readLine()) != null){
        buffer.append(line);
    }
    // reqBytes = buffer.toString().getBytes();

    input = buffer.toString();
    request.setAttribute("com.xp.input", input);
}

if (input == null) {
    response.setContentType("text/plain");
    PrintWriter out = response.getWriter();
    out.print("{\"act\":\"fail\",\"msg\":\"invalid\"}");
}

0voto

David Cuervo Points 9

Cela a fonctionné pour moi : (remarquez que java 8 est requis)

String requestData = request.getReader().lines().collect(Collectors.joining());
UserJsonParser u = gson.fromJson(requestData, UserJsonParser.class);

UserJsonParse est une classe qui montre à gson comment analyser le format json.

La classe est comme ceci :

public class UserJsonParser {

    private String username;
    private String name;
    private String lastname;
    private String mail;
    private String pass1;
//puis mettre les setters et getters
}

la chaîne json qui est analysée est comme ceci :

$jsonData: {    "username": "testuser",    "pass1": "clave1234" }

Le reste des valeurs (mail, lastname, name) sont définies à null

0 votes

Je ne pense pas que cette réponse soit liée à la question posée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X