8.1 http應答頭概述
web服務器的http應答一般由以下幾項構成:一個狀態(tài)行,一個或多個應答頭,一個空行,內(nèi)容文檔。設置http應答頭往往和設置狀態(tài)行中的狀態(tài)代碼結合起來。例如,有好幾個表示“文檔位置已經(jīng)改變”的狀態(tài)代碼都伴隨著一個location頭,而401(unauthorized)狀態(tài)代碼則必須伴隨一個www-authenticate頭。
然而,即使在沒有設置特殊含義的狀態(tài)代碼時,指定應答頭也是很有用的。應答頭可以用來完成:設置cookie,指定修改日期,指示瀏覽器按照指定的間隔刷新頁面,聲明文檔的長度以便利用持久http連接,……等等許多其他任務。
設置應答頭最常用的方法是httpservletresponse的setheader,該方法有兩個參數(shù),分別表示應答頭的名字和值。和設置狀態(tài)代碼相似,設置應答頭應該在發(fā)送任何文檔內(nèi)容之前進行。
setdateheader方法和setintheadr方法專門用來設置包含日期和整數(shù)值的應答頭,前者避免了把java時間轉換為gmt時間字符串的麻煩,后者則避免了把整數(shù)轉換為字符串的麻煩。
httpservletresponse還提供了許多設置常見應答頭的簡便方法,如下所示:
setcontenttype:設置content-type頭。大多數(shù)servlet都要用到這個方法。
setcontentlength:設置content-length頭。對于支持持久http連接的瀏覽器來說,這個函數(shù)是很有用的。
addcookie:設置一個cookie(servlet api中沒有setcookie方法,因為應答往往包含多個set-cookie頭)。
另外,如上節(jié)介紹,sendredirect方法設置狀態(tài)代碼302時也會設置location頭。
8.2 常見應答頭及其含義
有關http頭詳細和完整的說明,請參見http://www.w3.org/protocols/規(guī)范。
應答頭 說明
allow 服務器支持哪些請求方法(如get、post等)。
content-encoding 文檔的編碼(encode)方法。只有在解碼之后才可以得到content-type頭指定的內(nèi)容類型。利用gzip壓縮文檔能夠顯著地減少html文檔的下載時間。java的gzipoutputstream可以很方便地進行gzip壓縮,但只有unix上的netscape和windows上的ie 4、ie 5才支持它。因此,servlet應該通過查看accept-encoding頭(即request.getheader("accept-encoding"))檢查瀏覽器是否支持gzip,為支持gzip的瀏覽器返回經(jīng)gzip壓縮的html頁面,為其他瀏覽器返回普通頁面。
content-length 表示內(nèi)容長度。只有當瀏覽器使用持久http連接時才需要這個數(shù)據(jù)。如果你想要利用持久連接的優(yōu)勢,可以把輸出文檔寫入bytearrayoutputstram,完成后查看其大小,然后把該值放入content-length頭,最后通過bytearraystream.writeto(response.getoutputstream()發(fā)送內(nèi)容。
content-type 表示后面的文檔屬于什么mime類型。servlet默認為text/plain,但通常需要顯式地指定為text/html。由于經(jīng)常要設置content-type,因此httpservletresponse提供了一個專用的方法setcontenttyep。
date 當前的gmt時間。你可以用setdateheader來設置這個頭以避免轉換時間格式的麻煩。
expires 應該在什么時候認為文檔已經(jīng)過期,從而不再緩存它?
last-modified 文檔的最后改動時間。客戶可以通過if-modified-since請求頭提供一個日期,該請求將被視為一個條件get,只有改動時間遲于指定時間的文檔才會返回,否則返回一個304(not modified)狀態(tài)。last-modified也可用setdateheader方法來設置。
location 表示客戶應當?shù)侥睦锶ヌ崛∥臋n。location通常不是直接設置的,而是通過httpservletresponse的sendredirect方法,該方法同時設置狀態(tài)代碼為302。
refresh 表示瀏覽器應該在多少時間之后刷新文檔,以秒計。除了刷新當前文檔之外,你還可以通過setheader("refresh", "5; url=http://host/path")讓瀏覽器讀取指定的頁面。
注意這種功能通常是通過設置html頁面head區(qū)的<meta http-equiv="refresh" content="5;url=http://host/path">實現(xiàn),這是因為,自動刷新或重定向對于那些不能使用cgi或servlet的html編寫者十分重要。但是,對于servlet來說,直接設置refresh頭更加方便。
注意refresh的意義是“n秒之后刷新本頁面或訪問指定頁面”,而不是“每隔n秒刷新本頁面或訪問指定頁面”。因此,連續(xù)刷新要求每次都發(fā)送一個refresh頭,而發(fā)送204狀態(tài)代碼則可以阻止瀏覽器繼續(xù)刷新,不管是使用refresh頭還是<meta http-equiv="refresh" ...>。
注意refresh頭不屬于http 1.1正式規(guī)范的一部分,而是一個擴展,但netscape和ie都支持它。
server 服務器名字。servlet一般不設置這個值,而是由web服務器自己設置。
set-cookie 設置和頁面關聯(lián)的cookie。servlet不應使用response.setheader("set-cookie", ...),而是應使用httpservletresponse提供的專用方法addcookie。參見下文有關cookie設置的討論。
www-authenticate 客戶應該在authorization頭中提供什么類型的授權信息?在包含401(unauthorized)狀態(tài)行的應答中這個頭是必需的。例如,response.setheader("www-authenticate", "basic realm=\"executives\"")。
注意servlet一般不進行這方面的處理,而是讓web服務器的專門機制來控制受密碼保護頁面的訪問(例如.htaccess)。
8.3 實例:內(nèi)容改變時自動刷新頁面
下面這個servlet用來計算大素數(shù)。因為計算非常大的數(shù)字(例如500位)可能要花不少時間,所以servlet將立即返回已經(jīng)找到的結果,同時在后臺繼續(xù)計算。后臺計算使用一個優(yōu)先級較低的線程以避免過多地影響web服務器的性能。如果計算還沒有完成,servlet通過發(fā)送refresh頭指示瀏覽器在幾秒之后繼續(xù)請求新的內(nèi)容。
注意,本例除了說明http應答頭的用處之外,還顯示了servlet的另外兩個很有價值的功能。首先,它表明servlet能夠處理多個并發(fā)的連接,每個都有自己的線程。servlet維護了一份已有素數(shù)計算請求的vector表,通過查找素數(shù)個數(shù)(素數(shù)列表的長度)和數(shù)字個數(shù)(每個素數(shù)的長度)將當前請求和已有請求相匹配,把所有這些請求同步到這個列表上。第二,本例證明,在servlet中維持請求之間的狀態(tài)信息是非常容易的。維持狀態(tài)信息在傳統(tǒng)的cgi編程中是一件很麻煩的事情。由于維持了狀態(tài)信息,瀏覽器能夠在刷新頁面時訪問到正在進行的計算過程,同時也使得servlet能夠保存一個有關最近請求結果的列表,當一個新的請求指定了和最近請求相同的參數(shù)時可以立即返回結果。
primenumbers.java
注意,該servlet要用到前面給出的servletutilities.java。另外還要用到:primelist.java,用于在后臺線程中創(chuàng)建一個素數(shù)的vector;primes.java,用于隨機生成biginteger類型的大數(shù)字,檢查它們是否是素數(shù)。(此處略去primelist.java和primes.java的代碼。)
package hall;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
public class primenumbers extends httpservlet {
private static vector primelistvector = new vector();
private static int maxprimelists = 30;
public void doget(httpservletrequest request,
httpservletresponse response)
throws servletexception, ioexception {
int numprimes = servletutilities.getintparameter(request, "numprimes", 50);
int numdigits = servletutilities.getintparameter(request, "numdigits", 120);
primelist primelist = findprimelist(primelistvector, numprimes, numdigits);
if (primelist == null) {
primelist = new primelist(numprimes, numdigits, true);
synchronized(primelistvector) {
if (primelistvector.size() >= maxprimelists)
primelistvector.removeelementat(0);
primelistvector.addelement(primelist);
}
}
vector currentprimes = primelist.getprimes();
int numcurrentprimes = currentprimes.size();
int numprimesremaining = (numprimes - numcurrentprimes);
boolean islastresult = (numprimesremaining == 0);
if (!islastresult) {
response.setheader("refresh", "5");
}
response.setcontenttype("text/html");
printwriter out = response.getwriter();
string title = "some " + numdigits + "-digit prime numbers";
out.println(servletutilities.headwithtitle(title) +
"<body bgcolor=\"#fdf5e6\">\n" +
"<h2 align=center>" + title + "</h2>\n" +
"<h3>primes found with " + numdigits +
" or more digits: " + numcurrentprimes + ".</h3>");
if (islastresult)
out.println("<b>done searching.</b>");
else
out.println("<b>still looking for " + numprimesremaining +
" more<blink>...</blink></b>");
out.println("<ol>");
for(int i=0; i<numcurrentprimes; i++) {
out.println(" <li>" + currentprimes.elementat(i));
}
out.println("</ol>");
out.println("</body></html>");
}
public void dopost(httpservletrequest request,
httpservletresponse response)
throws servletexception, ioexception {
doget(request, response);
}
// 檢查是否存在同類型請求(已經(jīng)完成,或者正在計算)。
// 如存在,則返回現(xiàn)有結果而不是啟動新的后臺線程。
private primelist findprimelist(vector primelistvector,
int numprimes,
int numdigits) {
synchronized(primelistvector) {
for(int i=0; i<primelistvector.size(); i++) {
primelist primes = (primelist)primelistvector.elementat(i);
if ((numprimes == primes.numprimes()) &&
(numdigits == primes.numdigits()))
return(primes);
}
return(null);
}
}
}
primenumbers.html
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<title>大素數(shù)計算</title>
</head>
<center>
<body bgcolor="#fdf5e6">
<form action="/servlet/hall.primenumbers">
<b>要計算幾個素數(shù):</b>
<input type="text" name="numprimes" value=25 size=4><br>
<b>每個素數(shù)的位數(shù):</b>
<input type="text" name="numdigits" value=150 size=3><br>
<input type="submit" value="開始計算">
</form>
</center>
</body>
</html>
新聞熱點
疑難解答
圖片精選