一麸俘、Servlet概述
- 什么是Servlet
Servlet是基于Java語言的Web服務(wù)器端編程技術(shù)穷躁,按照Java EE規(guī)范定義狞山,Servlet是運行在Servlet容器中的Java類码泞,它能處理Web客戶的HTTP請求吱涉,并產(chǎn)生HTTP響應(yīng)逛薇。
- Servlet對請求的處理和響應(yīng)過程分為如下幾個步驟:
接收HTTP請求捺疼;
取得請求信息,包括請求頭和請求參數(shù)數(shù)據(jù)永罚;
調(diào)用其他Java類方法啤呼,完成具體的業(yè)務(wù)功能;
實現(xiàn)到其他Web組件的跳轉(zhuǎn)(包括重定向或請求轉(zhuǎn)發(fā))呢袱;
生成HTTP響應(yīng)(包括HTML或非HTML響應(yīng))官扣。
- 創(chuàng)建第一個Servlet
1.創(chuàng)建Java Web項目
2.創(chuàng)建Servlet
3.實現(xiàn)doPost()或doGet()方法
4.聲明配置Servlet:
3.x版本以上規(guī)范中使用注解聲明配置@WebServlet("/hello")
2.5版本規(guī)范中采用web.xml配置文件聲明配置
5.部署運行Servlet
- Servlet體系結(jié)構(gòu)
Servlet是使用Servlet API(應(yīng)用程序設(shè)計接口)及相關(guān)類和方法的Java程序。
Servlet API包含兩個軟件包:
- javax.servlet包
定義了所有Servlet類都必須實現(xiàn)或繼承的通用接口和類javax.servlet.http包
定義了采用HTTP協(xié)議通信的HttpServlet類羞福。
Servlet API的主要接口和類之間的關(guān)系
- Servlet聲明配置
Servlet聲明配置樣例
- loadOnStartup
指定Servlet的加載順序惕蹄。當此選項沒有指定時,表示容器在該Servlet第一次被請求時才加載治专;當值為0或者大于0時卖陵,表示容器在應(yīng)用啟動時就加載這個Servlet。值越小张峰,啟動該servlet的優(yōu)先級越高泪蔫。原則上不同的Servlet應(yīng)該使用不同的啟動順序數(shù)字
- init(ServletConfig config)
Servlet初始化方法,在Servlet實例化后挟炬,容器調(diào)用該方法進行Servlet的初始化鸥滨,init()方法只能被調(diào)用一次嗦哆,如果此方法沒有正常結(jié)束,就會拋出一個ServletException異常婿滓,一旦拋出該異常老速,Servlet將不再執(zhí)行,隨后對其進行再次調(diào)用會凸主,容器會重新載入并再次運行init()方法橘券。
- Servlet的映射路徑
urlPatterns= {"/hello","/hello.do","/hello/hello.html"} urlPatterns= {"/hello","*.do"}
1.精確匹配:
/hello
訪問路徑:http://localhost:8080/HelloServlet/hello
/hello/hello.do
訪問路徑:http://localhost:8080/HelloServlet/hello/hello.do
2.模糊匹配:
/*
訪問路徑:http://localhost:8080/HelloServlet/任意路徑
/hello/*
訪問路徑: http://localhost:8080/HelloServlet/hello/任意路徑
.do、.action卿吐、*.后綴名
訪問路徑: http://localhost:8080/HelloServlet/任意路徑.do
- Servlet2.5版本聲明配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >xmlns="http://xmlns.jcp.org/xml/ns/javaee" >xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee >http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" >version="3.1"> <display-name>ch02-HelloServlet2.5</display-name> <servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.neuedu.servlet.HelloServlet</servlet-class> <load-on-startup>0</load-on-startup> <init-param> <param-name>name</param-name> <param-value>neuedu</param-value> </init-param> <init-param> <param-name>email</param-name> <param-value>xxx@neuedu.com</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/hello.do</url-pattern> </servlet-mapping> </web-app>
- 在web.xml中通過“<servlet> </servlet>”元素聲明Servlet
<display-name>:指定該Servlet的顯示名旁舰,通常配合工具使用,等價于@WebServlet的displayName屬性
<servlet-name>:指定Servlet的名稱嗡官,一般與Servlet的類名相同箭窜,要求在一個web.xml文件內(nèi)名字唯一,等價于@WebServlet的name屬性
<servlet-class>:指定Servlet類的全限定名衍腥,即:包名.類名
<init-param>:指定Servlet初始化參數(shù)磺樱,等價于@WebServlet的initParams屬性,若有多個參數(shù)可重復定義此元素婆咸。此元素為可選配置
<param-name>:指定初始參數(shù)名
<param-value>:指定初始參數(shù)名對應(yīng)的值
<load-on-startup>:指定Servlet的加載順序竹捉,等價于@WebServlet的loadOnStartup屬性- web.xml中"<servlet-mapping> </servlet-mapping>”元素用于指定Servlet的URL映射
<servlet-name>:用來指定要映射的Servlet名稱,要與<servlet>聲明中的<servlet-name>值一致
<url-pattern>:指定Servlet的URL匹配模式尚骄,等價于@WebServlet的urlPatterns屬性或value屬性
- Servlet生命周期
Servlet生命周期是指Servlet實例從創(chuàng)建到響應(yīng)客戶請求块差,直至銷毀的過程。Servlet程序本身不直接在Java虛擬機上運行倔丈,由Servlet容器負責管理其整個生命周期憨闰。
Servlet生命周期可分為四個階段:實例化、初始化乃沙、處理請求起趾、銷毀。
- Servlet容器在如下時刻加載和實例化一個Servlet:
在Servlet容器啟動后警儒,客戶首次向Servlet發(fā)出請求,Servlet容器會判斷內(nèi)存中是否存在指定的Servlet對象眶根,如果沒有則創(chuàng)建它蜀铲,然后根據(jù)客戶的請求創(chuàng)建HttpRequest、HttpResponse對象属百,從而調(diào)用Servlet 對象的service方法记劝。
在為Servlet配置了自動裝入選項(load-on-startup)時,服務(wù)器在啟動時會自動裝入此Servlet族扰。- Servlet初始化
Servlet實例化后厌丑,Servlet容器將調(diào)用Servlet的init方法來對Servlet實例進行初始化定欧,初始化成功,Servlet在Web容器中會處于服務(wù)可用狀態(tài)怒竿;如果初始化失敗砍鸠,Servlet容器會銷毀該實例;當Servlet運行出現(xiàn)異常時耕驰,Servlet容器會使該實例變?yōu)榉?wù)不可用狀態(tài)爷辱。- 請求處理
服務(wù)器接收到客戶端請求,會為該請求創(chuàng)建“請求”對象和“響應(yīng)”對象朦肘,并調(diào)用service()方法饭弓,service()方法再調(diào)用其他方法來處理請求。
在Servlet生命周期中媒抠,service()方法可能被多次調(diào)用弟断。當多個客戶端同時訪問某個Servlet的service()方法時,服務(wù)器會為每個請求創(chuàng)建一個線程趴生,這樣可以并行處理多個請求夫嗓,減少請求處理的等待時間,提高服務(wù)器的響應(yīng)速度冲秽。但同時也要注意對同一對象的并發(fā)訪問問題舍咖。- 服務(wù)終止
當Servlet容器需要終止Servlet(如Web服務(wù)器被關(guān)閉或需要出讓資源),它會先調(diào)用Servlet的destroy()方法使其釋放正在使用的資源锉桑。
在調(diào)用destroy()方法之前排霉,必須讓當前正在執(zhí)行service()方法的任何線程完成執(zhí)行,或者超過了服務(wù)器定義的時間限制民轴。
在destroy()方法完成后攻柠,Servlet容器必須釋放Servlet實例以便被垃圾回收。
二后裸、Servlet基本應(yīng)用
- 處理超鏈接請求數(shù)據(jù)
- 超鏈接形式的數(shù)據(jù)請求語法格式:
<a href=”URL地址?參數(shù)=參數(shù)值[&參數(shù)=參數(shù)值...]”>鏈接文本</a>
發(fā)送請求的URL地址可以是絕對地址瑰钮,
如:http://localhost:8080/ServletDemo1/QueryServlet。
也可以是相對地址微驶,如:QueryServlet浪谴、../QueryServlet等形式。
在開發(fā)中大多數(shù)使用相對地址因苹,以便于項目的移植苟耻。<a href="UserQueryServlet?name=neuedu&email=neuedu@neuedu.com">查詢</a><br>
protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { String name = request.getParameter("name"); String email = request.getParameter("email"); response.getWriter().append("name:"+ name + ",email:" + email); }
查詢結(jié)果
- 處理Form表單請求數(shù)據(jù)
- Form表單數(shù)據(jù)請求語法格式:
<form action="UserQueryServlet" method = "post">
用戶名:<input type = "text" name = "name">
郵箱:<input type = "text" name = "email">
個人愛好:<input type="checkbox" name="love" value="1">吃飯
<input type="checkbox" name="love" value="2">睡覺
<input type="checkbox" name="love" value="3">打豆豆
<input type = "submit" value = "查詢">
</form>
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
String name = request.getParameter("name");
String email = request.getParameter("email");
//獲取復選框的值
String [] love = request.getParameterValues("love");
response.getWriter().append("name: " + name + ",email:" + email+ ",love:" + love);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
- 超鏈接一般用于獲取/查詢資源信息,屬于GET請求類型扶檐,請求的數(shù)據(jù)會附在URL之后凶杖,以?分割URL和傳輸數(shù)據(jù),參數(shù)之間以&相連款筑。由于其安全性(如:請求數(shù)據(jù)會以明文顯示在地址欄上)以及請求地址的長度限制智蝠,一般僅用于傳送一些簡單的數(shù)據(jù)腾么;
- Form表單一般用于更新資源信息,默認使用GET請求類型杈湾,多使用POST請求類型解虱。由于POST請求類型理論上沒有數(shù)據(jù)大小限制,可用表單來傳較大量的數(shù)據(jù)毛秘;
- 重定向
重定向是指由原請求地址重新定位到某個新地址饭寺,原有的request請求失效,客戶端看到的是新的request請求返回的響應(yīng)結(jié)果叫挟,客戶端瀏覽器地址欄變?yōu)樾抡埱蟮刂贰?/p>
- 重定向通過HttpServletResponse對象的sendRedirect()方法實現(xiàn)艰匙,該方法會通知客戶端去重新訪問新指定的URL地址,其語法格式如下:
public void sendRedirect(String location)throws java.io.IOException
location參數(shù)用以指定重定向的URL抹恳,它可以是相對路徑或絕對路徑员凝。
sendRedirect()方法不僅可以重定向到當前應(yīng)用程序中的其他資源,還可以重定向到同一個站點上的其他應(yīng)用程序中的資源奋献,甚至是使用絕對URL重定向到其他站點的資源健霹。@WebServlet("/RedirectServlet") public class RedirectServlet extends HttpServlet { protected void doGet(HttpServletRequest request, >HttpServletResponse response) throws ServletException, IOException { System.out.println("重定向前....."); response.sendRedirect("ResultServlet"); //response.sendRedirect("https://www.baidu.com"); System.out.println("重定向后....."); } protected void doPost(HttpServletRequest request, >HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
@WebServlet("/ResultServlet") public class ResultServlet extends HttpServlet { protected void doGet(HttpServletRequest request, >HttpServletResponse response) throws ServletException, IOException { // 設(shè)置響應(yīng)到客戶端的文本類型為HTML response.setContentType("text/html;charset=UTF-8"); // 獲取輸出流 PrintWriter out = response.getWriter(); // 輸出響應(yīng)結(jié)果 out.println("重定向與請求轉(zhuǎn)發(fā)的結(jié)果頁面"); } protected void doPost(HttpServletRequest request, >HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
- 請求轉(zhuǎn)發(fā)
請求轉(zhuǎn)發(fā)是指將請求再轉(zhuǎn)發(fā)到其他地址,轉(zhuǎn)發(fā)過程中使用的是同一個request請求瓶蚂,轉(zhuǎn)發(fā)后瀏覽器地址欄內(nèi)容不變糖埋。
- 請求轉(zhuǎn)發(fā)的過程發(fā)生在服務(wù)器內(nèi)部,只能從當前應(yīng)用內(nèi)部查找相應(yīng)的轉(zhuǎn)發(fā)資源窃这,而不能轉(zhuǎn)發(fā)到其它應(yīng)用的資源瞳别。
- 請求轉(zhuǎn)發(fā)使用RequestDispatcher接口中的forward()方法來實現(xiàn),該方法可以把請求轉(zhuǎn)發(fā)給另外一個資源杭攻,并讓該資源對此請求進行響應(yīng)祟敛。
RequestDispatcher接口有以下兩個方法:
forward()方法:將請求轉(zhuǎn)發(fā)給其他資源。
include()方法:將其他資源并入到當前請求中兆解。
- 請求轉(zhuǎn)發(fā)語法:
RequestDispatcher dispatcher = request.getRequestDispatcher(String path);
dispatcher.forward(ServletRequest request,ServletResponse response);
其中:
path參數(shù)用以指定轉(zhuǎn)發(fā)的URL馆铁,只能是相對路徑;
request和response參數(shù)取值為當前請求所對應(yīng)的HttpServletRequest和> > HttpServletResponse對象锅睛。@WebServlet("/ForwardServlet") public class ForwardServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { System.out.println("請求轉(zhuǎn)發(fā)前....."); request.getRequestDispatcher("ResultServlet").forward(request, >response); System.out.println("請求轉(zhuǎn)發(fā)后....."); } protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { doGet(request, response); } }
- 請求轉(zhuǎn)發(fā)與重定向數(shù)據(jù)傳遞
- 請求轉(zhuǎn)發(fā)與重定向?qū)ttpServletRequest對象屬性的存取埠巨。
HttpServletRequest對象屬性的存取語法:
request.setAttribute("attrobj", “value"); // 將attrobj屬性值存儲到request對象中
request.getAttribute("attrobj"); // 從request對象中取出attrobj屬性值
RedirectServlet中設(shè)置msg屬性值:
request.setAttribute("msg", "request redirect...");
ForwardServlet中設(shè)置msg屬性值:
request.setAttribute("msg", "request forward...");
ResultServlet中獲取msg屬性值:
String msg = (String)request.getAttribute("msg"); PrintWriter out = response.getWriter(); out.println(msg);
結(jié)果:
請求轉(zhuǎn)發(fā)
重定向
- 請求轉(zhuǎn)發(fā)與重定向的區(qū)別
重定向和請求轉(zhuǎn)發(fā)都可以讓瀏覽器獲得另外一個URL所指向的資源,但兩者的內(nèi)部運行機制有很大的區(qū)別:
- 請求轉(zhuǎn)發(fā)只能將請求轉(zhuǎn)發(fā)給同一個Web應(yīng)用中的組件衣撬;
而重定向不僅可以重定向到當前應(yīng)用程序中的其他資源乖订,還可以重定向到同一個站點上的其他應(yīng)用程序中的資源,或者重定向到其他站點的資源具练;- 重定向的訪問過程結(jié)束后,瀏覽器地址欄中顯示的URL會發(fā)生改變甜无,由初始的URL地址變成重定向的目標URL扛点;
而請求轉(zhuǎn)發(fā)過程結(jié)束后哥遮,瀏覽器地址欄保持初始的URL地址不變;- 請求轉(zhuǎn)發(fā)調(diào)用者與被調(diào)用者之間共享相同的request對象和response對象陵究,它們屬于同一個訪問請求和響應(yīng)過程眠饮;
而重定向調(diào)用者與被調(diào)用者使用各自的request對象和response對象,它們屬于兩個獨立的訪問請求和響應(yīng)過程铜邮。- 重定向和請求轉(zhuǎn)發(fā)不代表方法的結(jié)束仪召,下面的代碼還會繼續(xù)執(zhí)行,注意在轉(zhuǎn)發(fā)或重定向的語句后面不要在編寫代碼松蒜。
三扔茅、ServletConfig、ServletContext接口
- ServletConfig接口
- 容器在初始化一個Servlet時秸苗,將為該Servlet創(chuàng)建一個唯一的ServletConfig對象召娜,并將這個ServletConfig對象通過init(ServletConfig config)方法傳遞并保存在此Servlet對象中。
- 使用ServletConfig接口獲取Servlet初始化參數(shù)
通過使用@WebServlet注解的initParams熟悉來配置初始化參數(shù)(2.5版本中使用web.xml配置文件方式)@WebServlet(urlPatterns="/hello",loadOnStartup=1, initParams= {@WebInitParam(name="jdbcDriver",value="oracle.jdbc.driver.OracleDriver") , @WebInitParam(name="jdbcUrl",value="thin:2521")}) public class RequestWebInfoServlet extends HttpServlet { //init方法 @Override public void init(ServletConfig config) throws ServletException { String jdbcDriver = config.getInitParameter("jdbcDriver"); String jdbcUrl = config.getInitParameter("jdbcUrl"); System.out.println("request info Servlet init : jdbcDriver:" + >jdbcDriver +"," + "jdbcUrl" + jdbcUrl); } }
- ServletContext接口
- ServletContext也稱為Servlet上下文惊楼,代表當前Servlet運行環(huán)境玖瘸,是Servlet與Servlet容器之間直接通信的接口;
Servlet容器在啟動一個Web應(yīng)用時檀咙,會為該應(yīng)用創(chuàng)建一個唯一的ServletContext對象供該應(yīng)用中的所有Servlet對象共享雅倒,Servlet對象可以通過ServletContext對象來訪問容器中的各種資源。- 獲得ServletContext對象的兩種方式:
通過ServletConfig接口的getServletContext()方法獲得ServletContext對象弧可;
通過GenericServlet抽象類的getServletContext()方法獲得ServletContext對象蔑匣,實質(zhì)上該方法也是調(diào)用了ServletConfig的getServletContext()方法。- ServletContext接口中提供了以下幾種類型的方法:
獲取應(yīng)用范圍的初始化參數(shù)的方法侣诺;
存取應(yīng)用范圍域?qū)傩缘姆椒ǎ?br> 獲取當前Web應(yīng)用信息的方法殖演;
獲取當前容器信息和輸出日志的方法;
獲取服務(wù)器端文件資源的方法年鸳。- 配置ServletContext初始化參數(shù)
<!--配置應(yīng)用初始化參數(shù)(ServletContext) --> <context-param> <param-name>ctxParam</param-name> <param-value>ctxValue</param-value> </context-param>
- servlet文件中獲取ServletContext初始化參數(shù)
@Override public void init(ServletConfig config) throws ServletException { ServletContext ctx = config.getServletContext(); String param = ctx.getInitParameter("ctxParam"); System.out.println(param); }
- ServletContext域?qū)傩?/li>
- ServletContext對象可以理解為容器內(nèi)的一個共享空間趴久,可以存放具有應(yīng)用級別作用域的數(shù)據(jù),Web應(yīng)用中的各個組件都可以共享這些數(shù)據(jù)搔确。這些共享數(shù)據(jù)以key/value的形式存放在ServletContext對象中彼棍,并以key作為其屬性名被訪問。
- 應(yīng)用域?qū)傩缘拇嫒》椒?br> setAttribute(String name,Object object)膳算,把一個對象和一個屬性名綁定并存放到ServletContext中座硕,參數(shù)name指定屬性名,參數(shù)Object表示共享數(shù)據(jù)
getAttribute(String name)涕蜂,根據(jù)參數(shù)給定的屬性名华匾,返回一個Object類型的對象
getAttributeNames(),返回一個Enumeration對象机隙,該對象包含了所有存放在ServletContext中的屬性名蜘拉。
removeAttribute(String name)萨西,根據(jù)參數(shù)指定的屬性名,從ServletContext對象中刪除匹配的屬性- 頁面訪問量統(tǒng)計
@WebServlet("/ContextAttributeServlet") public class ContextAttributeServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { // 設(shè)置響應(yīng)到客戶端MIME類型和字符編碼方式 response.setContentType("text/html;charset=UTF-8"); //獲取ServletContext對象 ServletContext ctx = getServletContext(); //從ctx對象獲取count屬性存放的計數(shù)值 Integer count = (Integer) ctx.getAttribute("count"); if (count == null) {//判斷是否為第一次訪問頁面 count = 1; } else { count = count + 1; } //將更新后的數(shù)值存放到ServletContext對象的count屬性中 ctx.setAttribute("count", count); //獲取輸出流 PrintWriter out = response.getWriter(); //輸出計數(shù)信息 out.println("<p>本網(wǎng)站目前訪問人數(shù)是: " + count + "</p>"); } protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
- 獲取Web應(yīng)用信息
ServletContext對象還包含有關(guān)Web應(yīng)用的信息旭旭,
例如:當前Web應(yīng)用的根路徑谎脯、應(yīng)用的名稱、應(yīng)用組件間的轉(zhuǎn)發(fā)持寄、以及容器下其他Web應(yīng)用的ServletContext對象等源梭。@WebServlet("/ContextAppInfoServlet") public class ContextAppInfoServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { // 設(shè)置響應(yīng)到客戶端MIME類型和字符編碼方式 response.setContentType("text/html;charset=UTF-8"); // 獲取當前ServletContext對象 ServletContext context = super.getServletContext(); // 獲取容器中URL路徑為“/HelloServlet”的應(yīng)用的ServletContext對象 ServletContext contextByUrl = context.getContext("/HelloServlet"); // 獲取當前Web應(yīng)用的上下文根路徑 String contextPath = context.getContextPath(); // 獲取當前Web應(yīng)用的名稱 String contextName = context.getServletContextName(); // 獲取容器中URL路徑為“/chapter01”的應(yīng)用的應(yīng)用名稱 String contextByUrlName = contextByUrl.getServletContextName(); // 獲取轉(zhuǎn)發(fā)請求的RequestDispatcher對象 RequestDispatcher rd = >context.getRequestDispatcher("/HelloServlet"); // 獲取輸出流 PrintWriter out = response.getWriter(); out.println("<P>當前Web應(yīng)用的上下文根路徑是:" + contextPath + >"</p>"); out.println("<p>當前Web應(yīng)用的名稱是:" + contextName + "</p>"); out.println("<p>容器中URL路徑為“/HelloServlet”的應(yīng)用的應(yīng)用名稱是:" + contextByUrlName); } protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
四、Servlet請求與響應(yīng)
- HttpServletRequest接口
- ServletRequest接口被定義為用于封裝請求的信息稍味,ServletRequest對象由Servlet容器在用戶每次請求Servlet時創(chuàng)建并傳入Servlet的service()方法中废麻;
- HttpServletRequest接口繼承了ServletRequest接口,是專用于HTTP協(xié)議的子接口仲闽,用于封裝HTTP請求信息脑溢;
- 在HttpServlet類的service()方法中,傳入的ServletRequest對象被強制轉(zhuǎn)換為HttpServletRequest對象來進行HTTP請求信息的處理赖欣。
- HttpServletRequest接口提供了具有如下功能類型的方法:
獲取請求報文信息(包括請求行屑彻、請求頭、請求正文)的方法顶吮;
獲取網(wǎng)絡(luò)連接信息的方法社牲;
存取請求域?qū)傩缘姆椒ā?/li>
- HTTP協(xié)議
客戶端瀏覽器和服務(wù)器端Servlet通過HTTP協(xié)議進行通信,HTTP協(xié)議采用請求/響應(yīng)模型悴了,協(xié)議的請求報文由請求行搏恤、請求頭和可選的請求正文組成。
HTTP協(xié)議的請求報文請求報文信息格式樣例—我們可以通過firefox瀏覽器的開發(fā)者工具查看
- HTTP協(xié)議請求正文內(nèi)容為POST請求參數(shù)名稱和值所組成的一個字符湃交,GET請求參數(shù)附屬在請求行中熟空,沒有請求正文。
- HTTP請求參數(shù)中文亂碼問題
在進行請求參數(shù)傳遞時搞莺,經(jīng)常會遇到請求數(shù)據(jù)為中文時的亂碼問題息罗,出現(xiàn)亂碼的原因與客戶端的請求編碼方式(GET請求或POST請求)以及服務(wù)器的處理編碼方式有關(guān)。
POST請求亂碼問題:
- 瀏覽器會按當前顯示頁面所采用的字符集對請求的中文數(shù)據(jù)進行編碼才沧,再以報文體的形式傳送給服務(wù)器迈喉,Servlet在調(diào)用getParameter()方法獲取參數(shù)時,會以HttpServletRequest對象的getCharacterEncoding()方法返回的字符集對其進行解碼温圆,而該方法的返回值在未經(jīng)過setCharacterEncoding(charset)方法設(shè)置編碼的情況下為null挨摸,這時getParameter()方法將以服務(wù)器默認的“ISO-8859-1”字符集對參數(shù)進行解碼,而“ISO-8859-1”字符集并不包含中文岁歉,于是造成中文參數(shù)的亂碼問題得运。
- POST中文亂碼解決辦法:
在調(diào)用getParameter()方法前先調(diào)用setCharacterEncoding(charset)方法設(shè)定與頁面請求編碼相同的解碼字符集。request.setCharacterEncoding("UTF-8");
GET請求的亂碼問題:
- GET請求參數(shù)以“?”或“&”為連接字符附加在URL地址后,根據(jù)網(wǎng)絡(luò)標準RFC1738規(guī)定澈圈,只有字母和數(shù)字以及一些特殊符號和某些保留字才可以不經(jīng)過編碼直接用于URL彬檀,因此在請求參數(shù)為中文時必須先由瀏覽器進行編碼后才能發(fā)送給服務(wù)器帆啃,服務(wù)器端對GET請求參數(shù)依照服務(wù)器本身默認的字符集進行解碼瞬女。
- 在服務(wù)器端,由于GET請求參數(shù)是作為請求行發(fā)送給服務(wù)器的努潘,因此Servlet在通過getParameter()獲取請求參數(shù)時诽偷,并不能使用setCharacterEncoding(charset)方法指定的字符集進行解碼,而是依照服務(wù)器本身默認的字符集進行解碼疯坤。
- Tomcat服務(wù)器各版本中默認的URIEncoding字符集并不完全相同报慕,例如,Tomcat6和Tomcat7都默認為“ISO-8859-1”压怠,這類版本中眠冈,對于GET請求的中文參數(shù)必須經(jīng)處理后才會避免亂碼問題,因此在實際開發(fā)中盡量避免使用GET請求來傳遞中文參數(shù)菌瘫。
- HttpServletRequest屬性
- 存儲在HttpServletRequest對象中的對象稱之為請求域?qū)傩晕贤纾瑢儆谕粋€請求的多個處理組件之間可以通過請求域?qū)傩詠韨鬟f對象數(shù)據(jù)。
- HttpServletRequest接口提供了如下與請求域?qū)傩韵嚓P(guān)的方法:
void setAtrribute(String name,Object value)雨让,設(shè)定name屬性的值為value,保存在request范圍內(nèi)雇盖;
Object getAttribute(String name),從request范圍內(nèi)獲取name屬性的值栖忠;
void removeAttribute(String name)崔挖,從request范圍內(nèi)移除name屬性的值;
Enumeration getAttributeNames()庵寞,獲取所有request范圍的屬性
- HttpServletResponse接口
- ServletResponse接口被定義為用于創(chuàng)建響應(yīng)消息狸相,ServletResponse對象由Servlet容器在用戶每次請求Servlet時創(chuàng)建并傳入Servlet的service()方法中。
- HttpServletResponse接口繼承自ServletResponse接口捐川,是專用于HTTP協(xié)議的子接口脓鹃,用于封裝HTTP響應(yīng)消息。
- 在HttpServlet類的service()方法中属拾,傳入的ServletResponse對象被強制轉(zhuǎn)換為HttpServletResponse對象來進行HTTP響應(yīng)信息的處理将谊。
- HttpServletResponse接口提供了具有如下功能類型的方法:
設(shè)置響應(yīng)狀態(tài)的方法;
構(gòu)建響應(yīng)頭的方法渐白;
創(chuàng)建響應(yīng)正文的方法尊浓。
將服務(wù)器的端口號調(diào)整為8888,以后的訪問路徑為:http://localhost:8888/
- HTTP協(xié)議響應(yīng)報文
HTTP協(xié)議的響應(yīng)報文由響應(yīng)行纯衍、響應(yīng)頭和響應(yīng)正文組成栋齿。
HTTP協(xié)議響應(yīng)報文格式如下圖:
- HTTP協(xié)議響應(yīng)行
- HTTP協(xié)議響應(yīng)報文的響應(yīng)行由報文協(xié)議和版本以及狀態(tài)碼和狀態(tài)描述構(gòu)成,狀態(tài)碼由三個十進制數(shù)字組成,第一個十進制數(shù)字定義了狀態(tài)碼的類型瓦堵,后兩個數(shù)字沒有分類的作用基协。HTTP狀態(tài)碼可分為如下5種類型:
- 常見的響應(yīng)狀態(tài)碼:
200表示請求成功;
302表示資源(網(wǎng)頁等)暫時轉(zhuǎn)移到其它URL菇用;
404表示請求的資源(網(wǎng)頁等)不存在澜驮;
500表示服務(wù)器內(nèi)部錯誤。
- HTTP協(xié)議響應(yīng)消息頭
- 設(shè)置響應(yīng)消息頭
對于一些常用的消息頭惋鸥, HttpServletResponse提供了一些特定的方法來進行設(shè)置:
setContentType(String mime)杂穷,設(shè)定Content-Type消息頭;
setContentLength(int length)卦绣,設(shè)定Content-Length消息頭耐量;
addHeader(String name,String value),新增String類型的值到名為name的http頭部滤港;
addIntHeader(String name,int value)廊蜒,新增int類型的值到名為name的http頭部;
addDateHeader(String name,long date)溅漾,新增long類型的值到名為name的http頭部山叮;
addCookie(Cookie c)笆载,為Set-Cookie消息頭增加一個值姑曙;
- 實現(xiàn)一個頁面動態(tài)刷新效果
@WebServlet("/ResponseRefreshHeadServlet") public class ResponseRefreshHeadServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 設(shè)置Content-Type消息頭響應(yīng)文檔MIME類型和字符編碼方式 response.setContentType("text/html;charset=UTF-8"); // 設(shè)置響應(yīng)頭“refresh”的值為1秒 response.setHeader("refresh", "1"); //獲取輸出流 PrintWriter out = response.getWriter(); out.println("現(xiàn)在時間是:"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd >hh:mm:ss"); out.println(sdf.format(new Date())); } }
- 創(chuàng)建響應(yīng)正文
- 在Servlet中,向客戶端輸出的響應(yīng)數(shù)據(jù)是通過輸出流對象來完成的趋厉,HttpServletResponse接口提供了兩個獲取不同類型輸出流對象的方法:
1.getOutputStream()缝龄,返回字節(jié)輸出流對象ServletOutputStream汰现;
ServletOutputStream對象主要用于輸出二進制字節(jié)數(shù)據(jù)。例如叔壤,配合setContentType()方法響應(yīng)輸出一個圖像瞎饲、視頻等。
2.getWriter()炼绘,返回字符輸出流對象PrintWriter嗅战;
PrintWriter對象主要用于輸出字符文本內(nèi)容,但其內(nèi)部實現(xiàn)仍是將字符串轉(zhuǎn)換成某種字符集編碼的字節(jié)數(shù)組后再進行輸出俺亮。- 當向ServletOutputStream或PrintWriter對象中寫入數(shù)據(jù)后驮捍,Servlet容器會將這些數(shù)據(jù)作為響應(yīng)消息的正文,然后再與響應(yīng)狀態(tài)行和各響應(yīng)頭組合成完整的響應(yīng)報文輸出到客戶端脚曾,同時东且,在Serlvet的service()方法結(jié)束后,容器還將檢查getWriter()或getOutputStream()方法返回的輸出流對象是否已經(jīng)調(diào)用過close()方法本讥,如果沒有珊泳,容器將調(diào)用close()方法關(guān)閉該輸出流對象鲁冯。
- 實現(xiàn)響應(yīng)輸出一個圖像
@WebServlet("/OutputStreamServlet") public class OutputStreamServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //設(shè)置響應(yīng)消息頭Content-Type response.setContentType("image/jpeg"); // 獲取ServletContext對象 ServletContext context = super.getServletContext(); // 獲取讀取服務(wù)器端文件的輸入流 InputStream is = context.getResourceAsStream("/imgs/logo.png"); // 獲取ServletOutputStream輸出流 ServletOutputStream os = response.getOutputStream(); int i = 0; while ((i = is.read()) != -1) { os.write(i);//向輸出流中寫入二進制數(shù)據(jù) } is.close(); os.close(); } protected void doPost(HttpServletRequest request, HttpServletResponse >response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
- 響應(yīng)輸出中文亂碼問題
- 由于Java程序中的字符文本在內(nèi)存中以Unicode編碼的形式存在,因此PrintWriter對象在輸出字符文本時色查,需要先將它們轉(zhuǎn)換成其他某種字符集編碼的字節(jié)數(shù)組后輸出薯演。
- PrintWriter對象默認使用ISO-8859-1字符集進行Unicode字符串到字節(jié)數(shù)組的轉(zhuǎn)換,由于ISO-8859-1字符集中沒有中文字符秧了,因此Unicode編碼的中文字符將被轉(zhuǎn)換成無效的字符編碼后輸出給客戶端跨扮,這就是Servlet中文輸出亂碼問題的原因。
- ServletResponse接口中定義了三種方等方法來指定getWriter()方法返回的PrintWriter對象所使用的字符集編碼示惊。
1.response.setCharacterEncoding("UTF-8");
只能用來設(shè)置PrintWriter輸出流中字符的編碼方式好港,它的優(yōu)先權(quán)最高。
2.(推薦使用)response.setContentType("text/html;charset=UTF-8");
既可以設(shè)置PrintWriter輸出流中字符的編碼方式米罚,也可以設(shè)置瀏覽器接收到這些字符后以什么編碼方式來解碼,它的優(yōu)先權(quán)低于第一種方法丈探。
3.response.setLocale(new java.util.Locale("zh","CN"));
只能用來設(shè)置PrintWriter輸出流中字符的編碼方式录择,它的優(yōu)先權(quán)最低,在已經(jīng)使用前兩種方法中的一個設(shè)置了編碼方式以后碗降,它將被覆蓋而不再起作用隘竭。