110 votes

La requête Http Servlet perd les paramètres du corps POST après l'avoir lu une fois.

J'essaie d'accéder à deux paramètres de requête http dans un filtre Java Servlet, rien de nouveau ici, mais j'ai é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 filtres.

Il semble que cela ne se produise que lorsque les paramètres se trouvent dans le corps d'une requête POST (un formulaire à soumettre, par exemple).

Existe-t-il un moyen de lire les paramètres et de NE PAS les consommer ?

Jusqu'à présent, je n'ai trouvé que cette référence : Filtre Servlet utilisant request.getParameter perd les données du formulaire .

Merci !

1voto

La méthode getContentAsByteArray() de la classe Spring ContentCachingRequestWrapper lit le corps plusieurs fois, mais les méthodes getInputStream() et getReader() de la même classe ne lisent pas le corps plusieurs fois :

"Cette classe met en cache le corps de la requête en consommant l'InputStream. Si nous lisons l'InputStream dans l'un des filtres, les autres filtres suivants de la chaîne de filtres ne peuvent plus le lire. En raison de cette limitation, cette classe n'est pas adaptée à toutes les situations."

Dans mon cas, la solution plus générale qui a résolu ce problème a été d'ajouter les trois classes suivantes à mon projet Spring boot (et les dépendances requises au fichier pom) :

CachedBodyHttpServletRequest.java :

public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {

    private byte[] cachedBody;

    public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
        super(request);
        InputStream requestInputStream = request.getInputStream();
        this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new CachedBodyServletInputStream(this.cachedBody);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        // Create a reader from cachedContent
        // and return it
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody);
        return new BufferedReader(new InputStreamReader(byteArrayInputStream));
    }
}

CachedBodyServletInputStream.java :

public class CachedBodyServletInputStream extends ServletInputStream {

    private InputStream cachedBodyInputStream;

    public CachedBodyServletInputStream(byte[] cachedBody) {
        this.cachedBodyInputStream = new ByteArrayInputStream(cachedBody);
    }

    @Override
    public boolean isFinished() {
        try {
            return cachedBodyInputStream.available() == 0;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public boolean isReady() {
        return true;
    }

    @Override
    public void setReadListener(ReadListener readListener) {
        throw new UnsupportedOperationException();
    }

    @Override
    public int read() throws IOException {
        return cachedBodyInputStream.read();
    }
}

ContentCachingFilter.java :

@Order(value = Ordered.HIGHEST_PRECEDENCE)
@Component
@WebFilter(filterName = "ContentCachingFilter", urlPatterns = "/*")
public class ContentCachingFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        System.out.println("IN  ContentCachingFilter ");
        CachedBodyHttpServletRequest cachedBodyHttpServletRequest = new CachedBodyHttpServletRequest(httpServletRequest);
        filterChain.doFilter(cachedBodyHttpServletRequest, httpServletResponse);
    }
}

J'ai également ajouté les dépendances suivantes à pom :

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.0</version>
</dependency>

Un tutoriel et le code source complet sont disponibles ici : https://www.baeldung.com/spring-reading-httpservletrequest-multiple-times

0voto

GKislin Points 254

Regardez (ou utilisez) le filtre Spring AbstractRequestLoggingFilter.

0voto

Olivier.Roger Points 2094

Si vous avez le contrôle de la requête, vous pouvez définir le type de contenu comme suit flux binaire/octet . Cela permet d'interroger les paramètres sans consommer le flux d'entrée.

Toutefois, cela peut être spécifique à certains serveurs d'application. Je n'ai testé que tomcat, jetty semble se comporter de la même manière selon https://stackoverflow.com/a/11434646/957103 .

-1voto

Zhengwei Zhan Points 1

Vous pouvez utiliser la chaîne de filtres des servlets, mais au lieu d'utiliser celle d'origine, vous pouvez créer votre propre requête : yourownrequests extends HttpServletRequestWrapper.

-3voto

ashoka Points 177

Tout d'abord, nous ne devons pas lire les paramètres dans le filtre. Habituellement, les en-têtes sont lus dans le filtre pour effectuer quelques tâches d'authentification. Ceci dit, on peut lire le corps de HttpRequest complètement dans le filtre ou l'intercepteur en utilisant les CharStreams :

String body = com.google.common.io.CharStreams.toString(request.getReader());

Cela n'affecte en rien les lectures suivantes.

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