Http Servlet demande de perdre params de corps POST après l'avoir lu une fois
Je suis en train d'essayer d'accéder à deux paramètres de la requête http dans un filtre de Servlet Java, rien de nouveau ici, mais a été surpris de constater que les paramètres ont déjà été consommés! De ce fait, il n'est plus disponible dans la chaîne de filtrage.
Il semble que cela se produit uniquement lorsque les paramètres est livré dans un POST du corps de la requête (la soumission d'un formulaire, par exemple).
Est-il un moyen pour lire les paramètres et de ne PAS les consommer?
Jusqu'à présent je n'ai trouvé que cette référence: Filtre de Servlet utilisant la demande.getParameter perd les données de Formulaire.
Merci!
- peut-être montrer un fragment de code de la façon dont vous le faites?
- Avez-vous getInputStream() ou getReader()? Semble qu'ils sont ceux qui vont interférer avec l'exécution de getParameter()
Vous devez vous connecter pour publier un commentaire.
Comme une parenthèse, une autre façon de résoudre ce problème est de ne pas utiliser le filtre de la chaîne et au lieu de construire votre propre intercepteur composant, peut-être l'aide d'aspects, qui peut fonctionner sur l'analyse du corps de la requête. Il sera probablement plus efficace que vous êtes seulement la conversion de la demande
InputStream
dans votre propre modèle d'objet une fois.Cependant, je pense que c'est raisonnable de vouloir lire le corps de la requête plus d'une fois en particulier que la demande se déplace à travers le filtre de la chaîne. I généralement utiliser des chaînes de filtres pour certaines opérations que je veux garder à la couche HTTP, découplée des composantes du service.
Comme suggéré par Va Hartung j'ai réalisé cela en étendant
HttpServletRequestWrapper
, la consommation de la demandeInputStream
et essentiellement de la mise en cache des octets.Maintenant le corps de la requête peut être lu plus d'une fois par emballage de la demande d'origine avant de la passer à travers le filtre de la chaîne:
Cette solution vous permettra également de lire le corps de la requête à plusieurs reprises par le
getParameterXXX
méthodes, car l'appel sous-jacent estgetInputStream()
, qui sera bien sûr lire la mise en cache demandeInputStream
.Modifier
Pour une version plus récente de
ServletInputStream
interface. Vous devez fournir de la mise en œuvre de quelques méthodes commeisReady
,setReadListener
etc. Consultez cette question comme prévu dans le commentaire ci-dessous.getInputStream
est appelé mon wrapper puisque c'est laServletRequest
exemple que je passe dans la chaîne de filtres. Si vous êtes encore dans le doute, lire le code source pourServletRequestWrapper
et laServletRequest
interface.isReady()
.isFinished()
etsetReadListener()
de non-blocage IO qui doit être mis en œuvre. Je pense à la ReadListener pourrait être laissé en blanc, mais vous ne savez pas quoi faire à ce sujetisFinished()
et/ouisReady()
.MultiReadHttpServletResponse
? J'ai essayé similaire àMultiReadHttpServletRequest
approche, mais pour quelque raison il ne met pas en cache.Je sais je suis en retard, mais cette question est toujours d'actualité pour moi et ce de manière post a été un des grands succès dans Google. Je vais de l'avant et l'après ma solution dans l'espoir que quelqu'un d'autre peut sauver quelques heures.
Dans mon cas, j'ai besoin d'enregistrer toutes les requêtes et les réponses à leur corps. À l'aide de Spring Framework, la réponse est en fait assez simple, il suffit d'utiliser ContentCachingRequestWrapper et ContentCachingResponseWrapper.
requestBody
etresponseBody
étaient des chaînes videschain.doFilter(request, response);
au lieu d'unchain.doFilter(requestWrapper, responseWrapper);
ContentCaching*Wrapper
classes ont le coûteux prix de la consommation du flux d'entrée de sorte que la "mise en cache" est fait par le biais de la méthodegetContentAsByteArray
mais cette classe n'est pas en cache le flux d'entrée qui pourrait être nécessaire par d'autres filtres dans la chaîne de filtre (ce qui est mon cas d'utilisation). À mon humble avis, ce n'est pas le comportement attendu d'une mise en cache du contenu de la classe, donc j'ai soulevé cette amélioration de printemps de l'équipe de jira.printemps.io/parcourir/SPR-16028AbstractRequestLoggingFilter
de Printemps, où la majeure partie du travail est déjà fait par le Printemps et vous avez seulement besoin de remplacer 1 ou 2 méthodes simples.spring-web-4.3.12.RELEASE
. Comme je l'ai vérifié la source, j'ai trouvé la variablecachedContent
est utilisé pour stocker les contenus les plus divers tels que les paramètres de la requête et de demande d'inputStream. Elle est vide si vous appelezgetContentAsByteArray()
uniquement. Pour obtenir le corps de la requête, vous devez appelergetInputStream()
. Mais encore une fois, cela va rendre l'inputStream pas disponible pour les autres filtres et le gestionnaire.Les réponses ci-dessus ont été très utiles, mais encore des problèmes dans mon expérience. Sur tomcat 7 servlet 3.0, le getParamter et getParamterValues également dû être remplacé. La solution comprend ici deux get-les paramètres de la requête et de la post-corps. Elle permet la prise en raw string facilement.
Comme les autres solutions, il utilise Apache commons-io et Lunettes de Goyave.
Dans cette solution, les getParameter* méthodes ne jetez pas IOException mais ils utilisent le super.getInputStream() (pour obtenir le corps) qui peut jeter IOException. Je l'attraper et de le jeter runtimeException. Il n'est pas si agréable.
decode(getPostBodyAsString(), result);
dansgetParameterMap()
? Qui crée un paramètre avec la clé = corps de la requête et de la valeur = null, ce qui est assez bizarre.super.getParameterMap()
dans votregetParameterMap
? Qui vous donnera une carte de<String, String[]>
de toute façon.Le seul moyen serait pour vous de consommer l'intégralité du flux d'entrée de vous-même dans le filtre, prenez ce que vous voulez d'elle, et puis créer un nouveau InputStream pour le contenu que vous lisez, et les mettre InputStream dans un ServletRequestWrapper (ou HttpServletRequestWrapper).
L'inconvénient est que vous aurez à analyser la charge utile de vous-même, la norme ne fait pas que de la capacité disponible pour vous.
Addenda --
Comme je l'ai dit, vous avez besoin de regarder HttpServletRequestWrapper.
Dans un filtre, vous continuez le long en appelant FilterChain.doFilter(request, response).
Trivial de filtres, de la demande et de la réponse sont les mêmes que ceux transmis au filtre. Qui n'a pas à être le cas. Vous pouvez les remplacer par celles de vos propres demandes et des réponses.
HttpServletRequestWrapper est spécialement conçu pour faciliter cela. Vous transmettre la demande d'origine, et puis vous pouvez intercepter tous les appels. Vous créer votre propre sous-classe de ce, et de remplacer la méthode getInputStream avec l'un de vos propres. Vous ne pouvez pas modifier le flux d'entrée de la demande initiale, de sorte qu'au lieu que vous avez ce wrapper et de retour de votre propre flux d'entrée.
Le cas le plus simple est de consommer les demandes initiales de flux d'entrée dans un tampon d'octets, de faire ce qui, de magie que vous voulez, puis créer un nouveau ByteArrayInputStream à partir de la mémoire tampon. C'est ce qui est retourné dans votre enveloppe, qui est passé à la FilterChain.méthode doFilter.
Vous aurez besoin à la sous-classe ServletInputStream et de faire un autre wrapper pour votre ByteArrayInputStream, mais ce n'est pas une grosse affaire soit.
Moi aussi j'avais le même problème et je crois que le code ci-dessous est plus simple, et il est travaillé pour moi,
dans le filtre de classe java,
S'il vous plaît laissez-moi savoir si vous avez des questions
C'est donc essentiellement Lathy de la réponse, MAIS mis à jour pour les nouvelles exigences pour ServletInputStream.
À savoir (pour ServletInputStream), on a à mettre en œuvre:
C'est le édités Lathy l'objet
et quelque part (??) J'ai trouvé ce qui est une première classe de la classe et qui traite de la "extra" les méthodes.
En fin de compte, j'essayais juste de le journal de la demande. Et la-dessus frankensteined ensemble des morceaux m'ont aidé à créer le ci-dessous.
Veuillez prendre ce code avec un grain de sel.
Le PLUS important "test" est si un POSTE travaille avec une charge utile. C'est ce qui va exposer la "double lecture" questions de.
pseudo exemple de code
Vous pouvez remplacer "MyCustomObject" avec ole plaine "Objet" si vous voulez juste tester.
Cette réponse est frankensteined de plusieurs forces d'opérations spéciales des postes et des exemples..mais il a fallu un certain temps pour tirer tous ensemble, donc j'espère que ça aide un futur lecteur.
Veuillez upvote Lathy réponse avant le mien. Je n'aurais pas pu arrivé jusqu'ici sans elle.
Ci-dessous est l'une ou plusieurs des exceptions que je suis tout en travaillant cela.
Ressemble à certains des endroits que j'ai "emprunté" de sont ici:
http://slackspace.de/articles/log-request-body-with-spring-boot/
https://github.com/c0nscience/spring-web-logging/blob/master/src/main/java/org/zalando/springframework/web/logging/LoggingFilter.java
https://howtodoinjava.com/servlets/httpservletrequestwrapper-example-read-request-body/
https://www.oodlestechnologies.com/blogs/How-to-create-duplicate-object-of-httpServletRequest-object
https://github.com/c0nscience/spring-web-logging/blob/master/src/main/java/org/zalando/springframework/web/logging/LoggingFilter.java
Printemps a un support intégré avec un
AbstractRequestLoggingFilter
:Malheureusement, vous ne serez pas en mesure de lire de la charge directement sur la demande, mais le message de la Chaîne de paramètre inclure la charge utile, de sorte que vous pouvez récupérer à partir de là, comme suit:
String body = message.substring(message.indexOf("{"), message.lastIndexOf("]"));
Juste d'écraser des
getInputStream()
n'a pas fonctionné dans mon cas. Mon serveur de mise en œuvre semble pour analyser les paramètres sans appel de cette méthode. Je n'ai pas trouvé d'autre moyen, mais re-mettre en œuvre les quatre getParameter* méthodes. Voici le code degetParameterMap
(Client Http Apache et Google Goyave bibliothèque utilisée):Si vous avez le contrôle sur la demande, vous pouvez définir le type de contenu à binaire/octet-stream. Cela permet de requête pour les paramètres sans consommer le flux d'entrée.
Cependant, cela peut être spécifique à certains serveurs d'applications. J'ai testé uniquement tomcat, jetty semble se comporter de la même façon selon https://stackoverflow.com/a/11434646/957103.
La méthode getContentAsByteArray() du Ressort de la classe ContentCachingRequestWrapper lit le corps plusieurs fois, mais les méthodes getInputStream() et getReader (de la même classe, ne lisez pas le corps plusieurs fois:
"Cette classe met en cache le corps de la requête par la consommation de la InputStream. Si nous lisons la InputStream dans l'un des filtres, puis d'autres filtres dans le filtre de la chaîne ne peut pas lire plus. En raison de cette limitation, cette classe n'est pas adapté à toutes les situations."
Dans mon cas plus général de la solution qui a résolu ce problème a été d'ajouter à la suite de trois classes de mon Ressort de démarrage du projet (et les dépendances nécessaires à la pom fichier):
CachedBodyHttpServletRequest.java:
CachedBodyServletInputStream.java:
ContentCachingFilter.java:
J'ai également ajouté les dépendances suivantes pour pom:
Un tuturial et le code source complet est disponible ici:
https://www.baeldung.com/spring-reading-httpservletrequest-multiple-times
Tout d'abord nous ne devons pas lire les paramètres à l'intérieur du filtre. Habituellement, les en-têtes sont lus dans le filtre pour faire quelques tâches d'authentification. Cela dit, peut-on lire HttpRequest corps complètement dans le Filtre ou l'Intercepteur à l'aide de la CharStreams:
Cela n'affecte pas les lectures subséquentes à tous.
request.getReader()
sera de retour un lecteur qui contient une chaîne vide par la suite sur le lit.vous pouvez utiliser le filtre de servlet de la chaîne, mais au lieu d'utiliser celui d'origine, vous pouvez créer votre propre demande yourownrequests s'étend HttpServletRequestWrapper.