|
前言:
response是服務端對客戶端請求的一個響應,其中封裝了響應頭、狀態碼、內容(也就是最終要在瀏覽器上顯示的HTML代碼或者其他數據格式)等,
服務端在把response提交到客戶端之前,會使用一個緩沖區,並向該緩沖區內寫入響應頭和狀態碼,然后將所有內容flush(flush包含兩個步驟:先將緩沖區內容發送至客戶端,然后將緩沖區清空)。這就標志着該次響應已經committed(提交)。對於當前頁面中已經committed(提交)的response,就不能再使用這個response向緩沖區寫任何東西 (注:以為JSP中,response是一個JSP頁面的內置對象,所以同一個頁面中的response.XXX()是同一個response的不同方法,只要其中一個已經導致了committed,那么其它類似方式的調用都會導致 IllegalStateException異常)。
對於異常一:
問題描述:
在執行一段代碼后,最后執行request.getRequestDispatcher(跳轉頁面URL).forward(resquest,response)進行頁面跳轉時,報了此錯
問題原因:
1.在forwar之前,response就已經commit到瀏覽器端了。
導致response commit的原因包括:forward, redirect, flushBuffer
2.前兩個原因都是指在forward之前就已經進行了頁面跳轉動作(forward, redirect)。
至於flushBuffer,每一個JSP頁面都有一個緩沖區,默認的緩沖區大小為8KB,如果緩沖區被占滿的話,web服務器就會自動將response 進行commit,然后清空緩沖區(即flush),所以再進行頁面跳轉的時候就會報response已經被commit了。
內置對象out相關方法
| public abstract void clear() throws java.io.IOException | 清除緩沖區中的內容。如果緩沖區已經被刷新,clear()方法將拋出IOException異常 |
| public abstract void clearBuffer() throws java.io.IOException | 清除緩沖區中的當前內容。這個方法和clear()方法的區別是,如果緩沖區已經被刷新,這個方法不會拋出IOException異常 |
| public abstract void close() throws java.io.IOException | 刷新緩沖區,關閉輸出流。注意,我們在編寫JSP頁面時,不需要顯式地去調用這個方法,因為在JSP容器所生成的代碼中會自動包含對close()方法的調用。 |
| public abstract void flush() throws java.io.IOException | 刷新緩沖區,兩個步驟:1,提交response 2.清空緩沖區 |
| public int getBufferSize() | 獲得緩沖區大小,同response.getBufferSize()相同 |
| public abstract int getRemaining() | 獲得緩沖區中未使用的字節數 |
| public boolean isAutoFlush() | 判斷out對象是否是自動刷新 <%@ page autoFlush="true" %> <%--Default--%> |
內置對象response相關方法
| response.isCommitted() | 確認response是否已經committed |
| response.flushbuffer(); | 同out.flush相同 |
解決方法:
增大緩沖區大小:
<%@ page buffer="10kb" %> |
參考:http://jorton468.blog.163.com/blog/static/72588135201102441617287/
附加:
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
問題描述:
錯誤類型大致為以下幾種:
java.lang.IllegalStateException:Cannot forward a response that is already committed
IllegalStateException:response already commited
IllegalStateException:getOutputStream() has already been called for this request
…………
錯誤原因:
該異常表示,當前對客戶端的響應已經結束,不能在響應已經結束(或說消亡)后再向
客戶端(實際上是緩沖區)輸出任何內容。
具體分析:
首先解釋下flush(),我們知道在使用讀寫流的時候數據先被讀入內存這個緩沖區中,
然后再寫入文件,但是當數據讀完時不代表數據已經寫入文件完畢,因為可能還有
一部分仍未寫入文件而留在內存中,這時調用flush()方法就會把緩沖區的數據強行
清空輸出,因此flush()的作用就是保證緩存清空輸出。
response是服務端對客戶端請求的一個響應,其中封裝了響應頭、狀態碼、內容等,
服務端在把response提交到客戶端之前,會向緩沖區內寫入響應頭和狀態碼,然后
將所有內容flush。這就標志着該次響應已經committed(提交)。對於當前頁面中
已經committed(提交)的response,就不能再使用這個response向緩沖區寫任何東西
(注:同一個頁面中的response.XXX()是同一個response的不同方法,只要其中一個
已經導致了committed,那么其它類似方式的調用都會導致 IllegalStateException異常)。
【注意】能夠導致響應已經committed的操作包括:forward, redirect, flushBuffer。
JDK API:
①
flushBuffer
public void flushBuffer()throws IOException
- Forces any content in the buffer to be written to the client. A call to this method automatically commits the response, meaning the status code and headers will be written.
sendRedirect
public void sendRedirect(String location)throws IOException
-
Sends a temporary redirect response to the client using the specified redirect location URL. This method can accept relative URLs; the servlet container must convert the relative URL to an absolute URL before sending the response to the client. If the location is relative without a leading '/' the container interprets it as relative to the current request URI. If the location is relative with a leading '/' the container interprets it as relative to the servlet container root.
If the response has already been committed, this method throws an IllegalStateException. After using this method, the response should be considered to be committed and should not be written to.
③
forward
public void forward(ServletRequest request,ServletResponse response)
throws ServletException,IOException
-
Forwards a request from a servlet to another resource (servlet, JSP file, or HTML file) on the server. This method allows one servlet to do preliminary processing of a request and another resource to generate the response.
For a
RequestDispatcherobtained viagetRequestDispatcher(), theServletRequestobject has its path elements and parameters adjusted to match the path of the target resource.forwardshould be called before the response has been committed to the client (before response body output has been flushed). If the response already has been committed, this method throws anIllegalStateException. Uncommitted output in the response buffer is automatically cleared before the forward.The request and response parameters must be either the same objects as were passed to the calling servlet's service method or be subclasses of the
ServletRequestWrapperorServletResponseWrapperclasses that wrap them.
備 注:
注:在一次響應commit之前,所有的內容輸出都將寫入servlet引擎的緩沖區(tomcat或
weblogic的內容空間), 而在commit之后,上一次response向緩沖區寫入的內容,將清空。
由於servlet在沒有設置單線程的情況下(使用Single-Threaded Model,servlet實現
SingleThreadModel接口,jsp使用<%@ page isThreadSafe="false" %>),是多線程的,所以
上面所說的緩沖區,都將是該response所屬的線程私有的內存空間。有了這個概念,
將可以分析碰到的關於servlet多線程的很多問題。
如果不能確認response是否已經committed. 可以調用response.isCommitted()來判斷。
導致這個錯誤最普遍的原因是,jsp有編譯錯誤。
常見解決辦法:
①在response.sendRedirect()方法后加return語句即可,如下:
response.sendRedirect("login.jsp");
return;
②檢查提交的url是否有誤。
③如果你的頁面中用了清緩存代碼response.flushbuffer();又用到了response.sendRedirect(url);
你可以把response.flushbuffer();去掉,或者用JS的window.location.href="url";來做轉向。
④如果你用了OutputStream,而web容器生成的servlet代碼中有out.write(””),這個和JSP中調用的
response.getOutputStream()沖突。out.write()這個是字符流,而response.getOutputStream()
是字節流,你不能在同一個頁面中調用多個輸出流。無論先調用哪一個,在調用第二個時都會拋出
IllegalStateException,因為在jsp中,out變量是通過response.getWriter得到的。在多個使用了
outputStream的<%%>語句之間不能有空格及多余的字符。也就是頁面中除了使用了
outputStream的<%%>之外不能有空格或其它任何字符,在之內的語句可以有空格及回車。
在JSP頁面做輸出的時候有兩種方式.一是通過JspWriter,另一個是通過
OutputStream,但二者互相排斥.如果並存的話就會報告以上異常.
在不得不使用OutputStream的時候.我們必須要把JspWriter舍棄掉了。找到
請求異常的頁面所對應的Servlet..把其中所有使用JspWriter的語句全部去掉.
或者是到你的JSP文件里把動態輸出的代碼注釋掉.這里注意換行和空格制表符均
為JspWriter輸出.應該一起去掉.保存文件重新啟動服務器你會發現上述異常
消失了。
由於jsp container在處理完成請求后會調用releasePageContet方法釋放
所用的PageContext object,並且同時調用getWriter方法,由於getWriter方法
與在jsp頁面中使用流相關的getOutputStream方法沖突,所以會造成這種異常,
解決辦法是:只需要在jsp頁面的最后加上兩條語句:
out.clear();
out=pageContext.pushBody();
即可(其中out,pageContext均為jsp內置對象!) 。
