目標
- 怎么處理請求與響應
- web容器到底是怎么實現(xiàn)請求與響應的
- HttpServletRequest處理請求
- http請求頭信息有哪些
- get/post獲取參數(shù)
- get/post編碼
- 上傳圖片怎么解決笼痹?(http協(xié)議body數(shù)據怎么獲人褡选霹娄?)
- 容器內部如何共享數(shù)據?
- 兩個servlet程序之間如何調用?
- 內部轉發(fā)到底是什么邏輯?
- HttpServletResponse處理響應
- http響應頭信息有哪些
- 輸出內容也是需要編碼的!
- 二進制響應
- 重定向
- 直接輸出錯誤狀態(tài)(404)
1. web容器到底是怎么實現(xiàn)請求與響應的族檬?
- web容器接收到HTTP服務器轉發(fā)的請求內容
- 初始化HttpServletRequest對象,然后用相關參數(shù)進行賦值
- 初始化HttpServletResponse對象
- 初始化HttpServlet的實現(xiàn)類化戳,調用它的service函數(shù)单料,并把HttpServletRequest和HttpServletResponse兩個對象傳遞過去
- service調用doXXX函數(shù),在該函數(shù)中点楼,使用request的getParamter()函數(shù)獲取參數(shù)
- 處理相應的業(yè)務邏輯
- 設置response的setContentType輸出文件類型和編碼
- 調用HttpServletResponse的getWriter()函數(shù)獲得輸出用的PrintWriter對象扫尖,然后通過函數(shù)println()打印輸出html
需注意的是:
- 一次請求和響應是一個生命周期,在該周期呢掠廓,HttpServletRequest和HttpServletResponse由容器負責創(chuàng)建和銷毀换怖,它們不能跨越生命周期
- web容器在一個生命周期內處理的流程已經很清晰了,試想蟀瞧,如果沒有web容器沉颂,這些工作都要由程序員每次編碼實現(xiàn)条摸,很復雜!因此容器將系統(tǒng)自身業(yè)務邏輯和軟件需求業(yè)務邏輯進行分離铸屉,開發(fā)人員只需要編寫軟件需求業(yè)務邏輯即可钉蒲!
servlet原來設計是處理各種網絡協(xié)議的,因此提供的請求和響應對象類型是ServletRequest和ServletResponse彻坛,HttpServlet將這兩個對象轉化為HttpServletRequest和HttpServletResponse顷啼,請看具體代碼:
然后在service里面調用實際的doXXX相關函數(shù):
2. HttpServletRequest處理請求
-
http請求頭信息有哪些?
標示字段 | 含義 |
---|---|
Accept | 客戶端支持的數(shù)據類型 |
Accept-Charset | 客戶端支持的編碼 |
Acctpt-Encoding | 客戶端支持哪種數(shù)據壓縮格式 |
Acctpt-Language | 客戶端采用的是那種開發(fā)語言 |
Host | 客戶端想訪問的服務器主機 |
if-Modified-Since | 數(shù)據在客戶端緩存的時間 |
Referer | 客戶端從哪個頁面來的 |
User-Agent | 客戶端的操作系統(tǒng)和瀏覽器信息 |
Cookie | cookie數(shù)據 |
Connection | 持否需要持久鏈接 |
Content_length | 表示請求消息正文的長度 |
這是ljlj的請求頭信息截圖:
-
GET/POST獲取參數(shù)及標頭
取得web應用程序環(huán)境路徑:getContextPath()
獲取參數(shù)
函數(shù) | 功能描述 |
---|---|
getParameter(參數(shù)名稱) | 獲取指定參數(shù)名稱的值 |
getParameterValues() | 同一個請求參數(shù)會有多個值昌屉,返回的是一個數(shù)組钙蒙,返回類型為Enumeration對象 |
getParameterNames() | 返回所有參數(shù)的名稱 |
getParameterMap | 以key/value的方式返回參數(shù)與值 |
獲取標頭
函數(shù) | 功能描述 |
---|---|
getHeader(參數(shù)名稱) | 獲取指定參數(shù)名稱的標頭 |
getHeaders() | 類似于getParameterValues函數(shù),返回的也是Enumeration對象 |
getHeaderNames() | 返回所有標頭參數(shù)名稱 |
-
GET/POST編碼
編碼問題主要是針對非ASCII碼字符
-------------------------------post請求-------------------------------
瀏覽器端
Content-Type標頭中會設置字符編碼
Context-Type:text/html;charset=UTF-8
相當于是做了如下操作:
String text = java.net.URLEncoding.encode("林","UTF-8")
容器端
容器端默認的編碼是:ISO-8859-1怠益,所以如果要取得正確的中文字符仪搔,就需要:
req.setCharacterEncoding("UTF-8");
-------------------------------get請求-------------------------------
get請求的瀏覽器端和post一樣,不再贅述蜻牢!
HTTP服務器端
這里為什么和post不一樣,post是容器端偏陪,而這里是HTTP服務器端抢呆?
因為處理URL的是http服務器,而不是web容器笛谦,因此get參數(shù)不能使用HttpServletRequest對象1啊!
get獲取參數(shù)只能是獲得參數(shù)后饥脑,在進行編碼轉換恳邀,它會使用到String對象的getBytes()函數(shù),具體方式如下:
String name = req.getParameter("name");
String name = new String(name.getBytes("ISO-8859-1","UTF-8"));
-
上傳圖片怎么解決灶轰?(http協(xié)議body數(shù)據怎么獲纫シ小?)
先來確認一下http協(xié)議body數(shù)據是什么笋颤?http協(xié)議一般是由請求行乳附、請求頭部、空行和請求數(shù)據四部分組成的:
GET請求沒有body
GET /hello/index.html HTTP/1.1
#【↑】request line
#【↓】request headers
Accept: */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
Host: localhost:8000
#發(fā)送完關閉連接 or 等待
Connection: Keep-Alive
Cookie: JSESSIONID=BBBA54D519F7A320A54211F0107F5EA6
post請求的表單數(shù)據就是body
#url的部分仍然是urlencoded
POST /hello/checkUser.html?opt=xxx HTTP/1.1
Referer: http://localhost:8000/hello/index.html
Accept: */*
Accept-Language: zh-cn
#純文本的編碼:不編碼
Content-Type: text/plain
Accept-Encoding: gzip, deflate
Host: localhost:8000
Content-Length: 20
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: JSESSIONID=BBBA54D519F7A320A54211F0107F5EA6
#【↑】/r/n
#【↓】request body
hl=zh-CN&source=hp&q=domety
搞清楚body的原理后伴澄,把取body數(shù)據分為兩種赋除,一種是簡單的字符串表單,里面的都是文字非凌,另一種就是文件上傳了举农。
---------字符串------------
HttpServletRequest定義了getReader()方法,可以獲得BufferReadere對象敞嗡,通過該對象颁糟,就可以訪問body中的數(shù)據
---------文件(圖片)上傳------------
有兩種方式祭犯,一種是用HttpServletRequest的getInputStream()函數(shù)獲取ServletInputStream對象,是一種串流對象滚停,來處理上傳文件沃粗。另一種就是servlet3.0中使用的Part進行操作。前一種有點落伍键畴,就不贅述了最盅!
使用Part處理上傳圖片,必須加上@WebServlet("/upload.do")
-
容器內部如何共享數(shù)據起惕?
req.setAttrbute()
req.getAttrbute()
req.getAttrbuteNames()
req.removeAttrbute()
在一個請求的生命周期內涡贱,php共享數(shù)據靠session,java可以設置HttpServletRequest的屬性惹想,來在整個生命周期內问词,不論轉發(fā)了多少個servlet,這些屬性都存在嘀粱。
有些保留的屬性名稱就不要設置了:
-
兩個servlet程序之間如何調用激挪?
request.getRequestDispatcher("some.do").include(request,response)
request.getRequestDispatcher("some.do?data=xxx").include(request,response)
-
內部轉發(fā)到底是什么邏輯?
在調用forward函數(shù)前锋叨,當前servlet不能對請求進行響應(例如打印字符串)垄分,如果有響應但未確認(緩沖區(qū)未滿或未調用任何清除方法),則響應設置會被忽略娃磺。如果有效應且已確認薄湿,然后在調用forward()方法,則會拋出IllegalStateException異常
request.getRequestDispatcher("some.do").forward(request.response);
3. HttpServletResponse處理響應
-
http響應頭信息有哪些偷卧?
頭標示 | 頭標示解釋 |
---|---|
Location | 表示客戶端應該到哪里去提取文檔 |
Content-Encoding | 文檔的編碼方式 |
Content-Length | 表示內容長度 |
Content-Type | 表示后面的文檔屬于哪種MIME類型 |
Last-Modified | 文檔的最后修改時間 |
Expires | 文檔什么時候過期 |
Refresh | 表示瀏覽器因該在多少時間之后刷新文檔 |
Server | 服務器名字 |
Set-Cookie | 設置和頁面關聯(lián)的cookie |
---------------常規(guī)的設置標頭的函數(shù)---------------
- setHeader() 設置標頭名稱和值
- addHeader() 給同一標頭名稱上附加值
- setIntHeader() 如果標頭的值是整數(shù)
- addIntHeader() 同理
- setDateHeader() 如果標頭的值是日期時間
- addDateHeader() 同理
所有的標頭設置必須在響應輸出確認之前豺瘤,如果在之后,所有的設置會被忽略
---------------響應輸出有緩沖區(qū)---------------
只有在緩沖區(qū)滿或者強制刷新緩沖區(qū)的情況下听诸,才會真正的輸出內容到客戶端瀏覽器坐求。
- getBufferSize()
- setBufferSize()
- isCommited() 查看緩沖區(qū)的內容是否已經確認
- reset()
- resetBuffer()
- flushBuffer()
在調用HttpServletResponse的getWrite()或getOutputStream()方法之后調用setBufferSize(),會拋出IllegalStateException蛇更。(也就是在調用輸出函數(shù)之前就得設置好緩沖區(qū))
在響應輸出確認之后瞻赶,調用reset(),resetBuffer()會拋出IllegalStateException異常。(確認的輸出不能隨意重置)
-
輸出內容也是需要編碼的派任!
HttpServletResponse對象的setCharacterEncoding()和setContextType()都能設置編碼砸逊,建議使用后者,連同文件內容類型一起設置
response.setContextType("text/html","UTF-8")
-
二進制響應
在大部分情況下掌逛,會從HttpServletResponse取得PrintWriter實例师逸,使用println()對瀏覽器進行字符輸出。但是有時候豆混,需要直接對瀏覽器進行字節(jié)輸出篓像,這時候可以使用HttpServletResponse的getOutputStream()方法取得ServletOutputStream實例动知,他是OutputStream的子類。
區(qū)分字符流輸出和字節(jié)流輸出员辩,前者主要用來處理文字盒粮,包括多國語言的識別。而字節(jié)流主要用來處理文字意外的數(shù)據奠滑,例如二進制文件丹皱,圖片,PDF文件
這段代碼就展示了如果向瀏覽器輸出一個PDF文件宋税,如果瀏覽器有第三方插件可以在線打開這個字節(jié)流pdf文件摊崭,那就直接顯示,否則提示下載杰赛。
-
重定向
response.sendRedirect("http://xxxxxx.com");
重定向其實也是利用了HTTP狀態(tài)嗎和標頭呢簸,因此重定向前,不能有響應輸出確認乏屯,否則會拋出異常
-
直接輸出錯誤狀態(tài)(404)
response.sendError(HttpServletResponse.SC_NOT_FOUND)