2015-04-27 20 views
7

Ho un progetto Spring MVC in cui sto utilizzando i consigli del controller per gestire gli errori generati nei controller. Tuttavia, voglio anche mostrare una bella pagina di errore se si verifica un errore nei file JSP (anche se questo non dovrebbe accadere davvero!). Quindi ho aggiunto quanto segue al fascicolo di mio progetto web.xml:Pagina di errore JSP aggiunta all'output precedente

<error-page> 
    <error-code>500</error-code> 
    <location>/WEB-INF/views/application/error/view-error.jsp</location> 
</error-page> 

<error-page> 
    <exception-type>java.lang.Exception</exception-type> 
    <location>/WEB-INF/views/application/error/view-error.jsp</location> 
</error-page> 

Se io attivare un errore in JSTL di proposito, il contenuto di view-error.jsp è resa bene. Tuttavia, il contenuto è aggiunto all'output del file JSP in cui si è verificato l'errore. Ad esempio, se si verifica un errore entro display-users.jsp alla riga 50, il risultato è che l'output generato prima dell'errore (riga 1-50) viene anteposto al contenuto in view-error.jsp.

Questo è molto indesiderabile in quanto genera una pagina di errore dall'aspetto originale. E dato che non posso dire dove verrà lanciata un'eccezione (se potessi, correggerei l'errore), allora è molto probabile che l'utente veda un aspetto negativo.

Suppongo che sia perché l'output è già nel buffer e potrebbe essere già stato inviato al client? C'è un modo per sistemarlo, o forse un approccio alternativo? Grazie!

risposta

1

Questo è un problema con JSP di grandi dimensioni che genera HTML di grandi dimensioni, con codice java scriptlet mescolato ovunque. Non appena sono stati scritti abbastanza dati, il server esegue il commit delle intestazioni (le invia al client) e invia l'inizio della pagina. In quel momento, non è più possibile eseguire il rollback di nulla per recuperare i dati che sono già stati ricevuti (e eventualmente visualizzati) dal browser.

Questo è uno dei motivi per cui lo scriplet non è raccomandato, e se è davvero necessario mettere un po 'di intelligenza nella JSP, dovrebbe essere all'inizio della pagina prima che tutto venga effettivamente inviato al browser. Ma idealmente, tutto avrebbe dovuto essere calcolato in anticipo in un servlet e preparato i dati inseriti negli attributi di richiesta. In questo modo il JSP dovrebbe contenere solo semplici tag condizionali o di loop oltre all'output HTML e richiedere la resa degli attributi. Tutto ciò con pochi rischi per generare un'eccezione.

+0

L'OP non indica che sta facendo uso di scriptlet. Tutto quello che ha menzionato è JSTL, quindi potrebbe essere solo un semplice ' GriffeyDog

+0

@GriffeyDog Hai ragione, la mia risposta è probabilmente troppo generica. Ma gli errori in '' o '

+0

Sei corretto, tuttavia, non sto usando gli scriptlet. Uso esclusivamente JSTL. La maggior parte di ciò che accade nei miei file JSP è la ripetizione dei dati e semplici controlli condizionali, il tutto con JSTL. Sfortunatamente, alcuni JSP hanno cose leggermente più complicate e in questo momento il refactoring non ha avuto la priorità. Ecco perché mi piacerebbe avere questa "rete di sicurezza". La dimensione della pagina intera senza errori è 9 KB, e anche se faccio scattare l'errore nella parte superiore, ciò accade ancora, quindi non penso che sia causato dalla quantità di HTML. Grazie per la risposta! – Andy0708

1

Sembra che l'OutputStream di HttpServletResponse venga scritto prima che il JSP di enitre finisca il rendering.

Questo idealmente dovrebbe essere controllabile dalla proprietà "autoflush". https://tomcat.apache.org/tomcat-5.5-doc/jspapi/javax/servlet/jsp/JspWriter.html

Ma solo nel caso in cui non è risolvibile con questo:

Si potrebbe intercettare tutto ciò che scritto HttpServletResponse utilizzando l'approccio HttpServletResponseWrapper.

L'idea generale è che si crea un filtro e che il filtro passerà un "wrapper di risposta" agli strati sottostanti. Questo wrapper di risposta contiene un riferimento all'istanza di risposta reale. Qualsiasi cosa venga scritta nella risposta, può essere quindi manipolata dal wrapper di risposta e quindi inviata all'istanza di risposta reale.

Quindi, per il tuo caso, è possibile aggiungere tutti i dati in un oggetto StringBuilder e, quando i controlli tornano al filtro, il filtro può stampare l'intero StringBuilder sul OutputStream della risposta reale.

Ecco un esempio che intercetta qualsiasi cosa servlet, ecc.scrivere e poi invia la versione GZipped di che al Browser:

http://tutorials.jenkov.com/java-servlets/gzip-servlet-filter.html

+0

Oppure ... si potrebbe fare in modo che view-error.jsp esegua determinate azioni se la risposta è già stata commessa. http://docs.oracle.com/javaee/5/api/javax/servlet/ServletResponse.html#isCommitted%28%29 Forse si esegue un javascript sul browser per reindirizzare ad una pagina di errore – Mecon

+0

Grazie mille per la tua risposta - cercherò questo! – Andy0708

0

stato lì, fatto questo. Ecco una soluzione rapida e sporca fino a quando non è possibile riprogettazione.

1) Inserire tutto il codice JSTL che genera l'output in un nuovo JSP - chiamiamolo display-users-view.jsp (chiamarlo come si desidera).

2) Importare display-users-view.jsp dalla pagina display-users.jsp tramite < c: import >, ma assicurarsi di eseguire il dump del contenuto su una var (!). ad esempio:

<c:import url="display-users-view.jsp" var="output"/> 

3) Come passo finale sul display-users.jsp, scaricare l'output sullo schermo con un semplice:

${output} 

Ora, se l'errore viene generato prima che il $ { output} .. nessun danno, nessun fallo perché non hai ancora prodotto nulla nel browser. Se non ci sono errori, $ {output} scaricherà l'HTML generato nel display-users-view.jsp.

Nota, utilizzando c: import non è necessario passare alcun parametro querystring o modulo inviato a display-users.jsp perché saranno ancora disponibili in display-users-view.jsp.

+0

È davvero una soluzione un po 'sporca, ma in realtà è abbastanza logico. Terrò questo a mente se non trovassi una soluzione più "pulita". :-) Grazie per la tua risposta, lo apprezzo! – Andy0708

+0

Un'altra cosa che dovresti considerare è che c: forloop può generare una tonnellata di spazio bianco, che causa un commit del buffer prematuro. A volte è necessario solo il c: forloop che genera una tonnellata di dati, ma non il resto del JSTL per impedire il commit del buffer. – alfreema

Problemi correlati