在HTTP Servlet體系中,HttpServletResponse表明一個向客戶端發出的響應,它與HTTP響應消息不能徹底等同,可是因爲HTTP協議的特色,對HttpServletResponse對象的相關操做基本上能夠與修改HTTP響應消息的相關內容對應起來。因此,在編寫Servlet時,對響應所作的任何操做基本上都應該從HttpServletResponse對象着手,該對象對HTTP響應消息的內容和行爲提供了很是好的封裝。
響應的主要目的就是向客戶端返回響應消息,最多見的響應消息就是文本消息(HTML頁面內容),還有多是二進制形式內容、錯誤信息或者重定向操做等。
1.返回文本消息
使用文本消息響應客戶端是最經常使用的一種響應方式,前面的幾個Servlet也都是使用這種方式。
經過HttpServletResponse的getWriter()方法能夠得到一個向客戶端輸出字符信息的輸出流,它是一個PrintWriter對象。經過這個對象,Servlet能夠向客戶端輸出文本響應消息,響應消息的文本內容一般都是按HTML結構進行組織的。以下例的HtmlResponseServlet向客戶端返回一個HTML頁面的響應消息。
package cn.csai.web.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class HtmlResponseServlet implements Servlet {
public void destroy() {
}
public ServletConfig getServletConfig() {
return null;
}
public String getServletInfo() {
return null;
}
public void init(ServletConfig arg0) throws ServletException {
}
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException
{
PrintWriter writer = arg1.getWriter();
writer.write(getHtml());
writer.flush();
writer.close();
}
private String getHtml() {
StringBuilder html = new StringBuilder();
html.append("<html>");
html.append("<head><title>HtmlResponseServlet</title></head>");
html.append("<body>");
html.append("<h1 align=\"center\">HtmlResponseServlet</h1>");
html.append("<hr size=\"3\"/>");
html.append("<div align=\"center\">Content</div>");
html.append("</body>");
html.append("</html>");
return html.toString();
}
}
該Servlet的getHtml()是一個字符串,字符串的內容是一個HTML文檔,service()方法經過PrintWriter將字符串輸出到客戶端。顯示的頁面如圖8.17所示:
圖8.17 HtmlResponseServlet響應頁面
查看該頁面的源文件,以下所示:
<html>
<head><title>HtmlResponseServlet</title></head>
<body>
<h1 align="center">HtmlResponseServlet</h1>
<hr size="3"/>
<div align="center">Content</div>
</body>
</html>
能夠發現,該源文件的內容就是getHtml()方法返回的字符串的內容。
2.返回二進制數內容
Servlet不只能夠向客戶端返回HTML格式的文本消息,還能夠返回二進制數的內容,例如圖片、Word文檔等。
在Java中,字符輸出流是用Writer,而字節輸出流就是OutputStream。針對二進制數的字節內容,HttpServletResponse也提供了一個getOutputStream()方法,該方法返回一個ServletOutputStream對象,經過這個對象Servlet能夠向客戶端返回一個二進制數的響應消息。
爲了演示這個功能,首先在工程中新建一個包cn.csai.web.servlet.resource,挑選一個圖片放在該包中,假設圖片名取爲test.jpg。而後編寫一個名爲PictureResponseServlet,以下所示:
package cn.csai.web.servlet;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class PictureResponseServlet implements Servlet {
private static final String picPath = "/cn/csai/web/servlet/resource/test.jpg";
public void destroy() {
}
public ServletConfig getServletConfig() {
return null;
}
public String getServletInfo() {
return null;
}
public void init(ServletConfig arg0) throws ServletException {
}
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException
{
InputStream is = this.getClass().getResourceAsStream(picPath);
ServletOutputStream os = arg1.getOutputStream();
int avail = 0;
while((avail=is.available()) > 0) {
byte[] buff = new byte[avail];
is.read(buff);
os.write(buff);
}
os.flush();
is.close();
os.close();
}
}
訪問該Servlet返回的頁面如圖8.18所示:
圖8.18 PictureResponseServlet響應頁面
3.返回錯誤信息
當Servlet在處理用戶請求時,可能會遇到一些問題致使請求沒法被正常處理,這些問題多是用戶請求的格式不正確,也多是服務器端發生了一些異常。這種狀況下,Servlet須要向客戶端返回錯誤報告響應消息,這種錯誤報告響應消息具備不一樣於正常響應的響應碼(不一樣類別響應碼的含義在1.3.2節中已作介紹)。
HttpServletResponse提供了sendError()方法向客戶端返回錯誤報告響應消息,該方法容許Servlet指定錯誤消息的錯誤碼以及一個可選的錯誤文本信息。以下的SendErrorResponseServlet向客戶端返回一個錯誤報告響應消息,錯誤碼錶示用戶的請求消息不正確,而且反饋一個錯誤信息文本。
package cn.csai.web.servlet;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class SendErrorResponseServlet implements Servlet {
public void destroy() {
}
public ServletConfig getServletConfig() {
return null;
}
public String getServletInfo() {
return null;
}
public void init(ServletConfig arg0) throws ServletException {
}
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException
{
HttpServletResponse resp = (HttpServletResponse) arg1;
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Bad Request!");
}
}
響應頁面如圖8.19所示,sendError()方法中提供的提示信息「Bad Request!」會顯示在錯誤提示頁面中:
4.返回重定向操做
重定向操做就是向客戶端返回一個響應,響應要求客戶端瀏覽器的訪問請求從新指向另外一個URL。例如,將請求一個Servlet的請求從新定向到另外一個Servlet或另外一個圖片,甚至是另外一個網站的某個頁面。
圖8.19 SendErrorResponseServlet響應頁面
HttpServletResponse提供了sendRedirect()方法,該方法接受一個字符串參數,該參數表示要重定向的URL。下面的SendRedirectResponseServlet將用戶請求重定向到csai網的主頁:
package cn.csai.web.servlet;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class SendRedirectResponseServlet implements Servlet {
private static final String redirectURL = "http://www.csai.cn";
public void destroy() {
}
public ServletConfig getServletConfig() {
return null;
}
public String getServletInfo() {
return null;
}
public void init(ServletConfig arg0) throws ServletException {
}
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException
{
HttpServletResponse resp = (HttpServletResponse) arg1;
resp.sendRedirect(redirectURL);
}
}
訪問該Servlet後,請求會被重定向到「http://www.csai.cn」,若是客戶端能夠打開csai的主頁,那麼響應的頁面將會是csai的主頁,地址欄也會顯示csai主頁的地址,如圖8.20所示:
圖8.20 SendRedirectResponseServlet響應頁面
5.設置響應消息的內容類型
在介紹HTTP消息的頭域時,提到過一個Content-Type頭域,該頭域說明了消息內容的MIME類型,這是HTTP消息的發送方告知HTTP消息接收方有關消息體格式的惟一方法;HTTP消息接收方會根據Content-Type中攜帶的MIME類型決定在接收到消息體後對接收到的內容所採起的處理方式,能夠是在瀏覽器中打開、調用某個本地應用程序打開、直接保存爲本地文件,等等。
Servlet能夠顯式地爲響應消息設置一種MIME類型,若是Servlet沒有顯式地設置,那麼客戶端就沒法獲知消息體的MIME類型,這種狀況下大部分瀏覽器會根據得到的消息體的實際內容判斷消息體的MIME類型,而且使用合適的方式對消息體進行處理。
前面在介紹Tomcat的web.xml文件配置時,發如今${TOMCAT_HOME}\conf目錄下的web.xml文件中列出了許多有關文件後綴與MIME類型的映射關係(mime-mapping元素,見7.3.2節),這些映射關係是用於配置DefaultServlet行爲的,當程序員本身開發的Servlet向客戶端返回文件時這些設置並不會起做用。當客戶端請求的是某個文件(而不是Servlet)時,DefaultServlet會根據所請求文件的後綴得到所對應的MIME類型,並將這個類型設置到響應中。假如請求訪問的是程序員本身開發的Servlet時,這些映射並不會起做用。例如PictureResponseServlet最終向客戶端返回了一個圖片,可是Tomcat並不會自動向響應消息設置image/jpeg的MIME類型,由於讀取二進制數內容以及返回二進制數內容的工做都是由程序員的Servlet負責的,Tomcat並不知道二進制數消息體的具體類型,並且也沒有機會設置MIME類型。圖片返回給客戶端後,之因此IE可以將圖片正確顯示,那是由於IE經過分析返回的二進制數內容判斷出來了文件的類型(大部分類型文件的前幾個字節都具備必定的特徵)。並且在有些瀏覽器中(例如IE),本身判斷得到的MIME類型有可能還會覆蓋掉Servlet顯式設置的類型,這取決於瀏覽器的實現。
例如,下面的XMLDisplayServlet向客戶端輸出一段文本:
package cn.csai.web.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class XMLDisplayServlet implements Servlet {
public void destroy() {
}
public ServletConfig getServletConfig() {
return null;
}
public String getServletInfo() {
return null;
}
public void init(ServletConfig arg0) throws ServletException {
}
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException
{
PrintWriter writer = arg1.getWriter();
writer.write(getContent());
writer.flush();
writer.close();
}
private String getContent() {
StringBuilder html = new StringBuilder();
html.append("<persons>");
html.append("<person id=\"1\">");
html.append("<name>kevin</name>");
html.append("<age>26</age>");
html.append("</person>");
html.append("<person id=\"2\">");
html.append("<name>tom</name>");
html.append("<age>22</age>");
html.append("</person>");
html.append("</persons>");
return html.toString();
}
}
文本內容是:
<persons>
<person id="1">
<name>kevin</name>
<age>26</age>
</person>
<person id="2">
<name>tom</name>
<age>22</age>
</person>
</persons>
若是不設置響應的MIME類型,顯示的頁面如圖8.21所示:
因爲IE將返回響應的內容自動識別爲「text/html」MIME類型,也就是說,IE將返回的內容按HTML進行顯示,因此全部的標籤以及回車都被忽略了。
圖8.21 XMLDisplayServlet響應頁面
可是假如在service()中將響應的MIME類型顯式地設置爲「application/xml」,那麼IE就會按XML顯示。因此將XMLDisplayServlet修改成以下的XMLDisplayServlet2:
package cn.csai.web.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class XMLDisplayServlet2 implements Servlet {
public void destroy() {
}
public ServletConfig getServletConfig() {
return null;
}
public String getServletInfo() {
return null;
}
public void init(ServletConfig arg0) throws ServletException {
}
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException
{
PrintWriter writer = arg1.getWriter();
arg1.setContentType("application/xml");
writer.write(getContent());
writer.flush();
writer.close();
}
private String getContent() {
StringBuilder html = new StringBuilder();
html.append("<persons>");
html.append("<person id=\"1\">");
html.append("<name>kevin</name>");
html.append("<age>26</age>");
html.append("</person>");
html.append("<person id=\"2\">");
html.append("<name>tom</name>");
html.append("<age>22</age>");
html.append("</person>");
html.append("</persons>");
return html.toString();
}
}
其響應頁面如圖8.22所示:
圖8.22 XMLDisplayServlet2響應頁面
因爲在XMLDisplayServlet2中設置了MIME類型爲XML,因此IE就會以XML的格式正確顯式內容。
6.設置響應消息的字符編碼
若是返回的響應消息是文本類型,那麼瀏覽器就會涉及使用哪種字符編碼對消息中的文本進行解析的問題,這種情形通常會有三種解決方式:
瀏覽器使用本地操做系統默認的編碼方式:這種方式最簡單並且對響應提供者也沒有限制,但在互聯網上可能會出現使用任意一種編碼的響應文本,因此這種方式就限制了瀏覽器顯式其餘編碼的能力。
瀏覽器根據內容猜想編碼方式:雖然在不少瀏覽器中已經提供了這種功能,可是並不能依賴於這種功能,由於畢竟是由瀏覽器猜想的,可能會出現猜想不許確的狀況。
響應提供者在響應消息中指定編碼方式:這是一種最直接也是使用最廣泛的方式,這種方式能夠根據相應提供者的意圖對響應文本進行解析,可是就對響應提供者提出了要求。
實際上,在現實的使用中,是將這三種方式共同使用的:若是響應中指定了編碼方式,就按指定的編碼方式解析響應,不然瀏覽器會猜想一種編碼方式,若是提供的信息不足以猜想出編碼方式,那麼瀏覽器就會使用系統默認的編碼方式解析。
可見,若是開發人員在編寫Web系統時若是直接指定了編碼方式,將大大簡化了瀏覽器的工做量,並且也能保證須要顯示的內容可以被正確解析。
前面提到了HTTP響應消息用Content-Type頭域指定響應內容的MIME類型,但同時Content-Type頭域還會指定一個charset,例如:
Content-Type: text/html; charset=iso-8859-1
這個HTTP消息頭域指定了消息內容的MIME格式是text/html,內容編碼爲ISO—8859—1。
ServletResponse的setCharacterEncoding(String encoding)方法是用來設置響應的字符編碼的,在Servlet中直接調用ServletResponse的改方法設置所使用的編碼方式,以下面的SetEncodingServlet所示:
package cn.csai.web.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class SetEncodingServlet implements Servlet {
public void destroy() {
}
public ServletConfig getServletConfig() {
return null;
}
public String getServletInfo() {
return null;
}
public void init(ServletConfig arg0) throws ServletException {
}
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException
{
PrintWriter writer = arg1.getWriter();
arg1.setCharacterEncoding("UTF-8");
writer.write(getMessage());
writer.flush();
writer.close();
}
private String getMessage() {
StringBuffer sb = new StringBuffer();
sb.append("<html><head>");
sb.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>" );
sb.append("</head><body><h1>中文</h1></body></html>");
return sb.toString();
}
}
其響應頁面如圖8.23所示:
7.向響應消息中添加頭域
HTTP的響應消息由響應頭和響應消息體組成,響應頭經過頭域來設定響應消息的一系列屬性。前面講到的設置響應消息的內容類型就是經過在響應消息的頭域中添加Content-Type頭域實現的,以及返回重定向消息時重定向的URL也是經過設置Location頭域實現的。
圖8.23 SetEncodingServlet響應頁面
對於一些特殊的而且經常使用的頭域HttpServletResponse提供了專門的方法用於設置,同時HttpServletResponse還提供了通用的方法用於設置任意的頭域,這些方法包括:
經過這些方法,開發人員能夠向響應消息中添加任何頭域,能夠是HTTP協議中預約義的頭域,也能夠是產品自定義或者用戶本身定義的頭域。例以下面的SetHeaderServlet向響應消息中添加Last-Modified頭域,以指定返回內容的最後修改時間:
package cn.csai.web.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class SetHeaderServlet implements Servlet {
private Date now;
public void destroy() {
}
public ServletConfig getServletConfig() {
return null;
}
public String getServletInfo() {
return null;
}
public void init(ServletConfig arg0) throws ServletException {
}
public void service(ServletRequest arg0, ServletResponse arg1)
throws ServletException, IOException
{
HttpServletResponse resp = (HttpServletResponse) arg1;
now = new Date();
resp.setDateHeader("Last-Modified", now.getTime());
PrintWriter writer = resp.getWriter();
writer.write(getContent());
writer.flush();
writer.close();
}
private String getContent() {
StringBuilder html = new StringBuilder();
html.append("Last-Modified:" + now.toString());
return html.toString();
}
}