Si dovrebbe chiamare .close () su HttpServletResponse.getOutputStream () /. GetWriter ()?


96

In Java Servlet, è possibile accedere al corpo della risposta tramite response.getOutputStream()o response.getWriter(). Si dovrebbe ricorrere .close()a questo OutputStreamdopo che è stato scritto?

Da un lato c'è l'esortazione Blochiana a chiudere sempre OutputStreams. D'altra parte, non credo che in questo caso ci sia una risorsa sottostante che deve essere chiusa. L'apertura / chiusura dei socket è gestita a livello HTTP, per consentire cose come connessioni persistenti e simili.


Non sei invitato a indovinare se esiste una risorsa sottostante da chiudere. Se l' implementatore pensa così, o meglio lo sa, fornirà un close()che non fa nulla. Quello che dovresti fare è chiudere ogni risorsa chiudibile.
Marchese di Lorne

1
Anche se il tuo codice non l'ha aperto? Non credo proprio ...
Steven Huwig

Risposte:


93

Normalmente non dovresti chiudere lo stream. Il contenitore servlet chiuderà automaticamente il flusso al termine dell'esecuzione del servlet come parte del ciclo di vita della richiesta servlet.

Ad esempio, se chiudessi lo stream, non sarebbe disponibile se implementassi un filtro .

Detto questo, se lo chiudi non succederà nulla di male finché non provi a usarlo di nuovo.

EDIT: un altro collegamento di filtro

EDIT2: adrian.tarau è corretto in quanto se vuoi modificare la risposta dopo che il servlet ha fatto la sua cosa dovresti creare un wrapper che estenda HttpServletResponseWrapper e bufferizzare l'output. Questo per evitare che l'output vada direttamente al client ma consente anche di proteggere se il servlet chiude il flusso, come da questo estratto (enfasi mia):

Un filtro che modifica una risposta deve in genere acquisire la risposta prima che venga restituita al client. Il modo per farlo è passare al servlet che genera la risposta un flusso sostitutivo. Il flusso sostitutivo impedisce al servlet di chiudere il flusso di risposta originale quando viene completato e consente al filtro di modificare la risposta del servlet.

Articolo

Si può dedurre da quell'articolo ufficiale di Sun che chiudere il OutputStreamda un servlet è qualcosa che è normale, ma non è obbligatorio.


2
Questo è corretto. Una cosa da notare è che in alcuni casi potrebbe essere necessario svuotare il flusso, e questo è perfettamente consentito.
Toluju

1
C'è un altro effetto collaterale della chiusura dello scrittore. Inoltre, non sarà possibile impostare il codice di stato tramite response.setStatus dopo che è stato chiuso.
che javara

1
Segui questo consiglio. Ti farà risparmiare molto dolore. Neanche flush () a meno che tu non sappia perché lo stai facendo: dovresti lasciare che il contenitore gestisca il buffering.
Hal50000

75

La regola generale è questa: se hai aperto lo stream, dovresti chiuderlo. Se non l'hai fatto, non dovresti. Assicurati che il codice sia simmetrico.

Nel caso di HttpServletResponse, è un po 'meno chiaro, poiché non è ovvio se la chiamata getOutputStream()è un'operazione che apre il flusso. Il Javadoc dice solo che " Returns a ServletOutputStream"; allo stesso modo per getWriter(). In ogni caso, ciò che è chiaro è che HttpServletResponse"possiede" lo stream / writer e che esso (o il contenitore) è responsabile della sua chiusura.

Quindi, per rispondere alla tua domanda, no, in questo caso non dovresti chiudere lo stream. Il contenitore deve farlo e, se ci entri prima, rischi di introdurre piccoli bug nella tua applicazione.


Sono d'accordo con questa risposta, potresti anche voler controllare il ServletResponse.flushBuffer () vedi: docs.oracle.com/javaee/1.4/api/javax/servlet/…
cyber-monk

14
"se hai aperto il flusso, dovresti chiuderlo. Se non l'hai fatto, non dovresti" --- ben detto
Srikanth Reddy Lingala

2
Sembra le frasi affisse nel consiglio scolastico "se lo apri, chiudilo. Se lo accendi, spegnilo. Se lo sblocchi, chiudilo a chiave. [...]"
Ricardo

Iv ha notato che uso il flusso all'inizio del mio codice e mai più, il client attende l'esecuzione dell'intero servlet, ma quando chiamo close()quando ho finito con il flusso, il client ritorna immediatamente e il resto del servlet continua l'esecuzione. Questo non rende quindi la risposta un po 'più relativa? al contrario di un sì o no definitivo
BiGGZ

"se hai aperto lo stream, dovresti chiuderlo. Se non l'hai fatto, non dovresti. Assicurati che il codice sia simmetrico." - Quindi cosa succede se crei un altro flusso che avvolge questo flusso? Difficile rimanere simmetrici in questo caso poiché la chiamata di chiusura del flusso esterno in genere chiuderà quello annidato.
steinybot

5

Se non v'è alcuna possibilità che il filtro potrebbe essere chiamato su una risorsa 'incluso', si dovrebbe assolutamente non vicino al ruscello. Ciò causerà il fallimento della risorsa inclusa con un'eccezione "flusso chiuso".


Grazie per aver aggiunto questo commento. Abbastanza esilarante, sono ancora alle prese con il problema - solo ora ho notato che il modello servlet di NetBeans include il codice per chiudere il flusso di output ...
Steven Huwig

4

Dovresti chiudere lo stream, il codice è più pulito poiché invoca getOutputStream () e lo stream non ti viene passato come parametro, quando di solito lo usi e non provi a chiuderlo. L'API Servlet non afferma che se il flusso di output può essere chiuso o non deve essere chiuso, in questo caso puoi chiudere in sicurezza il flusso, qualsiasi contenitore là fuori si occupa di chiudere il flusso se non è stato chiuso dal servlet.

Ecco il metodo close () in Jetty, chiudono il flusso se non viene chiuso.

public void close() throws IOException
    {
        if (_closed)
            return;

        if (!isIncluding() && !_generator.isCommitted())
            commitResponse(HttpGenerator.LAST);
        else
            flushResponse();

        super.close();
    }

Inoltre, come sviluppatore di un filtro non dovresti presumere che OutputStream non sia chiuso, dovresti sempre passare un altro OutputStream se vuoi modificare il contenuto dopo che il servlet ha svolto il suo lavoro.

EDIT: Chiudo sempre lo stream e non ho avuto problemi con Tomcat / Jetty. Non penso che dovresti avere problemi con qualsiasi contenitore, vecchio o nuovo.


4
"Dovresti chiudere lo stream, il codice è più pulito ..." - Per me, il codice infarcito di .close () sembra meno pulito del codice senza, soprattutto se .close () non è necessario - che è ciò che questa domanda è tentando di determinare.
Steven Huwig

1
sì, ma la bellezza viene dopo :) Comunque, poiché l'API non è chiara, preferirei chiuderla, il codice sembra coerente, una volta richiesto un OutputStream dovresti chiuderlo a meno che l'API non dica "non chiuderlo".
adrian.tarau

Non credo che chiudere il flusso di output nel mio codice sia una buona pratica. Questo è il lavoro del container.
JimHawkins,

get * non crea lo stream, non è il produttore. Non dovresti chiuderlo, il contenitore deve farlo.
dmatej

3

Un altro argomento contro la chiusura del file OutputStream. Guarda questo servlet. Genera un'eccezione. L'eccezione è mappata in web.xml a un errore JSP:

package ser;

import java.io.*;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;

@WebServlet(name = "Erroneous", urlPatterns = {"/Erroneous"})
public class Erroneous extends HttpServlet {

  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.setContentType("text/html;charset=UTF-8");
    PrintWriter out = resp.getWriter();
    try {
      throw new IOException("An error");
    } finally {
//      out.close();
    }
  }
}

Il file web.xml contiene:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <error-page>
        <exception-type>java.io.IOException</exception-type>
        <location>/error.jsp</location>
    </error-page>
</web-app>

E il file error.jsp:

<%@page contentType="text/html" pageEncoding="UTF-8" isErrorPage="true"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Error Page</title>
    </head>
    <body>
        <h1><%= exception.getMessage()%></h1>
    </body>
</html>

Quando carichi /Erroneousnel browser, vedi la pagina di errore che mostra "Un errore". Ma se annulli il commento della out.close()riga nel servlet sopra, ridistribuisci l'applicazione e ricarichi /Erroneousnon vedrai nulla nel browser. Non ho idea di cosa stia realmente accadendo, ma immagino che ciò out.close()impedisca la gestione degli errori.

Testato con Tomcat 7.0.50, Java EE 6 utilizzando Netbeans 7.4.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.