注于Java領(lǐng)域優(yōu)質(zhì)技術(shù),歡迎關(guān)注
作者:滌生_Woo
本篇文章篇幅比較長(zhǎng),先來(lái)個(gè)思維導(dǎo)圖預(yù)覽一下。
一張圖帶你看完本篇文章
1.計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)分層
計(jì)算機(jī)網(wǎng)絡(luò)體系結(jié)構(gòu)分層
2.TCP/IP 通信傳輸流
利用 TCP/IP 協(xié)議族進(jìn)行網(wǎng)絡(luò)通信時(shí),會(huì)通過(guò)分層順序與對(duì)方進(jìn)行通信。發(fā)送端從應(yīng)用層往下走,接收端則從鏈路層往上走。如下:
TCP/IP 通信傳輸流
如下圖所示:
HTTP 請(qǐng)求
在網(wǎng)絡(luò)體系結(jié)構(gòu)中,包含了眾多的網(wǎng)絡(luò)協(xié)議,這篇文章主要圍繞 HTTP 協(xié)議(HTTP/1.1版本)展開(kāi)。
HTTP協(xié)議(HyperText Transfer Protocol,超文本傳輸協(xié)議)是用于從WWW服務(wù)器傳輸超文本到本地瀏覽器的傳輸協(xié)議。它可以使瀏覽器更加高效,使網(wǎng)絡(luò)傳輸減少。它不僅保證計(jì)算機(jī)正確快速地傳輸超文本文檔,還確定傳輸文檔中的哪一部分,以及哪部分內(nèi)容首先顯示(如文本先于圖形)等。 HTTP是客戶(hù)端瀏覽器或其他程序與Web服務(wù)器之間的應(yīng)用層通信協(xié)議。在Internet上的Web服務(wù)器上存放的都是超文本信息,客戶(hù)機(jī)需要通過(guò)HTTP協(xié)議傳輸所要訪(fǎng)問(wèn)的超文本信息。HTTP包含命令和傳輸信息,不僅可用于Web訪(fǎng)問(wèn),也可以用于其他因特網(wǎng)/內(nèi)聯(lián)網(wǎng)應(yīng)用系統(tǒng)之間的通信,從而實(shí)現(xiàn)各類(lèi)應(yīng)用資源超媒體訪(fǎng)問(wèn)的集成。 我們?cè)跒g覽器的地址欄里輸入的網(wǎng)站地址叫做URL (Uniform Resource Locator,統(tǒng)一資源定位符)。就像每家每戶(hù)都有一個(gè)門(mén)牌地址一樣,每個(gè)網(wǎng)頁(yè)也都有一個(gè)Internet地址。當(dāng)你在瀏覽器的地址框中輸入一個(gè)URL或是單擊一個(gè)超級(jí)鏈接時(shí),URL就確定了要瀏覽的地址。瀏覽器通過(guò)超文本傳輸協(xié)議(HTTP),將Web服務(wù)器上站點(diǎn)的網(wǎng)頁(yè)代碼提取出來(lái),并翻譯成漂亮的網(wǎng)頁(yè)。
HTTP請(qǐng)求響應(yīng)模型
HTTP通信機(jī)制是在一次完整的 HTTP 通信過(guò)程中,客戶(hù)端與服務(wù)器之間將完成下列7個(gè)步驟:
1 建立 TCP 連接
在HTTP工作開(kāi)始之前,客戶(hù)端首先要通過(guò)網(wǎng)絡(luò)與服務(wù)器建立連接,該連接是通過(guò) TCP 來(lái)完成的,該協(xié)議與 IP 協(xié)議共同構(gòu)建 Internet,即著名的 TCP/IP 協(xié)議族,因此 Internet 又被稱(chēng)作是 TCP/IP 網(wǎng)絡(luò)。HTTP 是比 TCP 更高層次的應(yīng)用層協(xié)議,根據(jù)規(guī)則,只有低層協(xié)議建立之后,才能進(jìn)行高層協(xié)議的連接,因此,首先要建立 TCP 連接,一般 TCP 連接的端口號(hào)是80;
2 客戶(hù)端向服務(wù)器發(fā)送請(qǐng)求命令
一旦建立了TCP連接,客戶(hù)端就會(huì)向服務(wù)器發(fā)送請(qǐng)求命令;
例如:GET/sample/hello.jsp HTTP/1.1
3 客戶(hù)端發(fā)送請(qǐng)求頭信息
客戶(hù)端發(fā)送其請(qǐng)求命令之后,還要以頭信息的形式向服務(wù)器發(fā)送一些別的信息,之后客戶(hù)端發(fā)送了一空白行來(lái)通知服務(wù)器,它已經(jīng)結(jié)束了該頭信息的發(fā)送;
4 服務(wù)器應(yīng)答
客戶(hù)端向服務(wù)器發(fā)出請(qǐng)求后,服務(wù)器會(huì)客戶(hù)端返回響應(yīng);
例如: HTTP/1.1 200 OK
響應(yīng)的第一部分是協(xié)議的版本號(hào)和響應(yīng)狀態(tài)碼
5 服務(wù)器返回響應(yīng)頭信息
正如客戶(hù)端會(huì)隨同請(qǐng)求發(fā)送關(guān)于自身的信息一樣,服務(wù)器也會(huì)隨同響應(yīng)向用戶(hù)發(fā)送關(guān)于它自己的數(shù)據(jù)及被請(qǐng)求的文檔;
6 服務(wù)器向客戶(hù)端發(fā)送數(shù)據(jù)
服務(wù)器向客戶(hù)端發(fā)送頭信息后,它會(huì)發(fā)送一個(gè)空白行來(lái)表示頭信息的發(fā)送到此為結(jié)束,接著,它就以 Content-Type 響應(yīng)頭信息所描述的格式發(fā)送用戶(hù)所請(qǐng)求的實(shí)際數(shù)據(jù);
7 服務(wù)器關(guān)閉 TCP 連接
一般情況下,一旦服務(wù)器向客戶(hù)端返回了請(qǐng)求數(shù)據(jù),它就要關(guān)閉 TCP 連接,然后如果客戶(hù)端或者服務(wù)器在其頭信息加入了這行代碼 Connection:keep-alive ,TCP 連接在發(fā)送后將仍然保持打開(kāi)狀態(tài),于是,客戶(hù)端可以繼續(xù)通過(guò)相同的連接發(fā)送請(qǐng)求。保持連接節(jié)省了為每個(gè)請(qǐng)求建立新連接所需的時(shí)間,還節(jié)約了網(wǎng)絡(luò)帶寬。
1.通過(guò)請(qǐng)求和響應(yīng)的交換達(dá)成通信
應(yīng)用 HTTP 協(xié)議時(shí),必定是一端擔(dān)任客戶(hù)端角色,另一端擔(dān)任服務(wù)器端角色。僅從一條通信線(xiàn)路來(lái)說(shuō),服務(wù)器端和客服端的角色是確定的。HTTP 協(xié)議規(guī)定,請(qǐng)求從客戶(hù)端發(fā)出,最后服務(wù)器端響應(yīng)該請(qǐng)求并返回。換句話(huà)說(shuō),肯定是先從客戶(hù)端開(kāi)始建立通信的,服務(wù)器端在沒(méi)有接收到請(qǐng)求之前不會(huì)發(fā)送響應(yīng)。
2.HTTP 是不保存狀態(tài)的協(xié)議
HTTP 是一種無(wú)狀態(tài)協(xié)議。協(xié)議自身不對(duì)請(qǐng)求和響應(yīng)之間的通信狀態(tài)進(jìn)行保存。也就是說(shuō)在 HTTP 這個(gè)級(jí)別,協(xié)議對(duì)于發(fā)送過(guò)的請(qǐng)求或響應(yīng)都不做持久化處理。這是為了更快地處理大量事務(wù),確保協(xié)議的可伸縮性,而特意把 HTTP 協(xié)議設(shè)計(jì)成如此簡(jiǎn)單的。
可是隨著 Web 的不斷發(fā)展,我們的很多業(yè)務(wù)都需要對(duì)通信狀態(tài)進(jìn)行保存。于是我們引入了 Cookie 技術(shù)。有了 Cookie 再用 HTTP 協(xié)議通信,就可以管理狀態(tài)了。
3.使用 Cookie 的狀態(tài)管理
Cookie 技術(shù)通過(guò)在請(qǐng)求和響應(yīng)報(bào)文中寫(xiě)入 Cookie 信息來(lái)控制客戶(hù)端的狀態(tài)。Cookie 會(huì)根據(jù)從服務(wù)器端發(fā)送的響應(yīng)報(bào)文內(nèi)的一個(gè)叫做 Set-Cookie 的首部字段信息,通知客戶(hù)端保存Cookie。當(dāng)下次客戶(hù)端再往該服務(wù)器發(fā)送請(qǐng)求時(shí),客戶(hù)端會(huì)自動(dòng)在請(qǐng)求報(bào)文中加入 Cookie 值后發(fā)送出去。服務(wù)器端發(fā)現(xiàn)客戶(hù)端發(fā)送過(guò)來(lái)的 Cookie 后,會(huì)去檢查究竟是從哪一個(gè)客戶(hù)端發(fā)來(lái)的連接請(qǐng)求,然后對(duì)比服務(wù)器上的記錄,最后得到之前的狀態(tài)信息。
Cookie 的流程
4.請(qǐng)求 URI 定位資源
HTTP 協(xié)議使用 URI 定位互聯(lián)網(wǎng)上的資源。正是因?yàn)?URI 的特定功能,在互聯(lián)網(wǎng)上任意位置的資源都能訪(fǎng)問(wèn)到。
5.告知服務(wù)器意圖的 HTTP 方法(HTTP/1.1)
HTTP 方法
6.持久連接
HTTP 協(xié)議的初始版本中,每進(jìn)行一個(gè) HTTP 通信都要斷開(kāi)一次 TCP 連接。比如使用瀏覽器瀏覽一個(gè)包含多張圖片的 HTML 頁(yè)面時(shí),在發(fā)送請(qǐng)求訪(fǎng)問(wèn) HTML 頁(yè)面資源的同時(shí),也會(huì)請(qǐng)求該 HTML 頁(yè)面里包含的其他資源。因此,每次的請(qǐng)求都會(huì)造成無(wú)畏的 TCP 連接建立和斷開(kāi),增加通信量的開(kāi)銷(xiāo)。
為了解決上述 TCP 連接的問(wèn)題,HTTP/1.1 和部分 HTTP/1.0 想出了持久連接的方法。其特點(diǎn)是,只要任意一端沒(méi)有明確提出斷開(kāi)連接,則保持 TCP 連接狀態(tài)。旨在建立一次 TCP 連接后進(jìn)行多次請(qǐng)求和響應(yīng)的交互。在 HTTP/1.1 中,所有的連接默認(rèn)都是持久連接。
7.管線(xiàn)化
持久連接使得多數(shù)請(qǐng)求以管線(xiàn)化方式發(fā)送成為可能。以前發(fā)送請(qǐng)求后需等待并接收到響應(yīng),才能發(fā)送下一個(gè)請(qǐng)求。管線(xiàn)化技術(shù)出現(xiàn)后,不用等待亦可發(fā)送下一個(gè)請(qǐng)求。這樣就能做到同時(shí)并行發(fā)送多個(gè)請(qǐng)求,而不需要一個(gè)接一個(gè)地等待響應(yīng)了。
比如,當(dāng)請(qǐng)求一個(gè)包含多張圖片的 HTML 頁(yè)面時(shí),與挨個(gè)連接相比,用持久連接可以讓請(qǐng)求更快結(jié)束。而管線(xiàn)化技術(shù)要比持久連接速度更快。請(qǐng)求數(shù)越多,時(shí)間差就越明顯。
1.HTTP 報(bào)文
用于 HTTP 協(xié)議交互的信息被稱(chēng)為 HTTP 報(bào)文。請(qǐng)求端(客戶(hù)端)的 HTTP 報(bào)文叫做請(qǐng)求報(bào)文;響應(yīng)端(服務(wù)器端)的叫做響應(yīng)報(bào)文。HTTP 報(bào)文本身是由多行(用 CR+LF 作換行符)數(shù)據(jù)構(gòu)成的字符串文本。
2.HTTP 報(bào)文結(jié)構(gòu)
HTTP 報(bào)文大致可分為報(bào)文首部和報(bào)文主體兩部分。兩者由最初出現(xiàn)的空行(CR+LF)來(lái)劃分。通常,并不一定有報(bào)文主體。如下:
HTTP 報(bào)文結(jié)構(gòu)
2.1請(qǐng)求報(bào)文結(jié)構(gòu)
請(qǐng)求報(bào)文結(jié)構(gòu)
請(qǐng)求報(bào)文的首部?jī)?nèi)容由以下數(shù)據(jù)組成:
請(qǐng)求報(bào)文的示例,如下:
請(qǐng)求報(bào)文示例
2.2響應(yīng)報(bào)文結(jié)構(gòu)
響應(yīng)報(bào)文結(jié)構(gòu)
響應(yīng)報(bào)文的首部?jī)?nèi)容由以下數(shù)據(jù)組成:
響應(yīng)報(bào)文的示例,如下:
響應(yīng)報(bào)文示例
1.請(qǐng)求行
舉個(gè)栗子,下面是一個(gè) HTTP 請(qǐng)求的報(bào)文:
GET /index.htm HTTP/1.1 Host: sample.com
其中,下面的這行就是請(qǐng)求行,
GET /index.htm HTTP/1.1
綜合來(lái)看,大意是請(qǐng)求訪(fǎng)問(wèn)某臺(tái) HTTP 服務(wù)器上的 /index.htm 頁(yè)面資源。
2.狀態(tài)行
同樣舉個(gè)栗子,下面是一個(gè) HTTP 響應(yīng)的報(bào)文:
HTTP/1.1 200 OK Date: Mon, 10 Jul 2017 15:50:06 GMT Content-Length: 256 Content-Type: text/html <html> ...
其中,下面的這行就是狀態(tài)行,
HTTP/1.1 200 OK
1.首部字段概述
先來(lái)回顧一下首部字段在報(bào)文的位置,HTTP 報(bào)文包含報(bào)文首部和報(bào)文主體,報(bào)文首部包含請(qǐng)求行(或狀態(tài)行)和首部字段。
在報(bào)文眾多的字段當(dāng)中,HTTP 首部字段包含的信息最為豐富。首部字段同時(shí)存在于請(qǐng)求和響應(yīng)報(bào)文內(nèi),并涵蓋 HTTP 報(bào)文相關(guān)的內(nèi)容信息。使用首部字段是為了給客服端和服務(wù)器端提供報(bào)文主體大小、所使用的語(yǔ)言、認(rèn)證信息等內(nèi)容。
2.首部字段結(jié)構(gòu)
3.首部字段類(lèi)型
首部字段根據(jù)實(shí)際用途被分為以下4種類(lèi)型:
4.通用首部字段(HTTP/1.1)
4.1 Cache-Control
通過(guò)指定首部字段 Cache-Control 的指令,就能操作緩存的工作機(jī)制。
4.1.1 可用的指令一覽
可用的指令按請(qǐng)求和響應(yīng)分類(lèi)如下:
緩存請(qǐng)求指令
緩存響應(yīng)指令
4.1.2 表示能否緩存的指令
public 指令
Cache-Control: public
當(dāng)指定使用 public 指令時(shí),則明確表明其他用戶(hù)也可利用緩存。
private 指令
Cache-Control: private
當(dāng)指定 private 指令后,響應(yīng)只以特定的用戶(hù)作為對(duì)象,這與 public 指令的行為相反。緩存服務(wù)器會(huì)對(duì)該特定用戶(hù)提供資源緩存的服務(wù),對(duì)于其他用戶(hù)發(fā)送過(guò)來(lái)的請(qǐng)求,代理服務(wù)器則不會(huì)返回緩存。
no-cache 指令
Cache-Control: no-cache
Cache-Control: no-cache=Location
由服務(wù)器返回的響應(yīng)中,若報(bào)文首部字段 Cache-Control 中對(duì) no-cache 字段名具體指定參數(shù)值,那么客戶(hù)端在接收到這個(gè)被指定參數(shù)值的首部字段對(duì)應(yīng)的響應(yīng)報(bào)文后,就不能使用緩存。換言之,無(wú)參數(shù)值的首部字段可以使用緩存。只能在響應(yīng)指令中指定該參數(shù)。
no-store 指令
Cache-Control: no-store
當(dāng)使用 no-store 指令時(shí),暗示請(qǐng)求(和對(duì)應(yīng)的響應(yīng))或響應(yīng)中包含機(jī)密信息。因此,該指令規(guī)定緩存不能在本地存儲(chǔ)請(qǐng)求或響應(yīng)的任一部分。
注意:no-cache 指令代表不緩存過(guò)期的指令,緩存會(huì)向源服務(wù)器進(jìn)行有效期確認(rèn)后處理資源;no-store 指令才是真正的不進(jìn)行緩存。
4.1.3 指定緩存期限和認(rèn)證的指令
s-maxage 指令
Cache-Control: s-maxage=604800(單位:秒)
max-age 指令
Cache-Control: max-age=604800(單位:秒)
min-fresh 指令
Cache-Control: min-fresh=60(單位:秒)
min-fresh 指令要求緩存服務(wù)器返回至少還未過(guò)指定時(shí)間的緩存資源。
max-stale 指令
Cache-Control: max-stale=3600(單位:秒)
only-if-cached 指令
Cache-Control: only-if-cached
表示客戶(hù)端僅在緩存服務(wù)器本地緩存目標(biāo)資源的情況下才會(huì)要求其返回。換言之,該指令要求緩存服務(wù)器不重新加載響應(yīng),也不會(huì)再次確認(rèn)資源的有效性。
must-revalidate 指令
Cache-Control: must-revalidate
使用 must-revalidate 指令,代理會(huì)向源服務(wù)器再次驗(yàn)證即將返回的響應(yīng)緩存目前是否仍有效。另外,使用 must-revalidate 指令會(huì)忽略請(qǐng)求的 max-stale 指令。
proxy-revalidate 指令
Cache-Control: proxy-revalidate
proxy-revalidate 指令要求所有的緩存服務(wù)器在接收到客戶(hù)端帶有該指令的請(qǐng)求返回響應(yīng)之前,必須再次驗(yàn)證緩存的有效性。
no-transform 指令
Cache-Control: no-transform
使用 no-transform 指令規(guī)定無(wú)論是在請(qǐng)求還是響應(yīng)中,緩存都不能改變實(shí)體主體的媒體類(lèi)型。這樣做可防止緩存或代理壓縮圖片等類(lèi)似操作。
4.1.4 Cache-Control 擴(kuò)展
Cache-Control: private, community="UCI"
通過(guò) cache-extension 標(biāo)記(token),可以擴(kuò)展 Cache-Control 首部字段內(nèi)的指令。上述 community 指令即擴(kuò)展的指令,如果緩存服務(wù)器不能理解這個(gè)新指令,就會(huì)直接忽略掉。
4.2 Connection
Connection 首部字段具備以下兩個(gè)作用:
控制不再轉(zhuǎn)發(fā)的首部字段
Connection: Upgrade
在客戶(hù)端發(fā)送請(qǐng)求和服務(wù)器返回響應(yīng)中,使用 Connection 首部字段,可控制不再轉(zhuǎn)發(fā)給代理的首部字段,即刪除后再轉(zhuǎn)發(fā)(即Hop-by-hop首部)。
管理持久連接
Connection: close
HTTP/1.1 版本的默認(rèn)連接都是持久連接。當(dāng)服務(wù)器端想明確斷開(kāi)連接時(shí),則指定 Connection 首部字段的值為 close。
Connection: Keep-Alive
HTTP/1.1 之前的 HTTP 版本的默認(rèn)連接都是非持久連接。為此,如果想在舊版本的 HTTP 協(xié)議上維持持續(xù)連接,則需要指定 Connection 首部字段的值為 Keep-Alive。
4.3 Date
表明創(chuàng)建 HTTP 報(bào)文的日期和時(shí)間。
Date: Mon, 10 Jul 2017 15:50:06 GMT
HTTP/1.1 協(xié)議使用在 RFC1123 中規(guī)定的日期時(shí)間的格式。
4.4 Pragma
Pragma 首部字段是 HTTP/1.1 版本之前的歷史遺留字段,僅作為與 HTTP/1.0 的向后兼容而定義。
Pragma: no-cache
Cache-Control: no-cache Pragma: no-cache
4.5 Trailer
Trailer: Expires
首部字段 Trailer 會(huì)事先說(shuō)明在報(bào)文主體后記錄了哪些首部字段。可應(yīng)用在 HTTP/1.1 版本分塊傳輸編碼時(shí)。
4.6 Transfer-Encoding
Transfer-Encoding: chunked
4.7 Upgrade
Upgrade: TSL/1.0
用于檢測(cè) HTTP 協(xié)議及其他協(xié)議是否可使用更高的版本進(jìn)行通信,其參數(shù)值可以用來(lái)指定一個(gè)完全不同的通信協(xié)議。
4.8 Via
Via: 1.1 a1.sample.com(Squid/2.7)
4.9 Warning
該首部字段通常會(huì)告知用戶(hù)一些與緩存相關(guān)的問(wèn)題的警告。
Warning 首部字段的格式如下:
Warning:[警告碼][警告的主機(jī):端口號(hào)] "[警告內(nèi)容]"([日期時(shí)間])
最后的日期時(shí)間可省略。
HTTP/1.1 中定義了7種警告,警告碼對(duì)應(yīng)的警告內(nèi)容僅推薦參考,另外,警告碼具備擴(kuò)展性,今后有可能追加新的警告碼。
5. 請(qǐng)求首部字段(HTTP/1.1)
5.1 Accept
Accept: text/html, application/xhtml+xml, application/xml; q=0.5
5.2 Accept-Charset
Accept-Charset: iso-8859-5, unicode-1-1; q=0.8
Accept-Charset 首部字段可用來(lái)通知服務(wù)器用戶(hù)代理支持的字符集及字符集的相對(duì)優(yōu)先順序。另外,可一次性指定多種字符集。同樣使用 q=[數(shù)值] 來(lái)表示相對(duì)優(yōu)先級(jí)。
5.3 Accept-Encoding
Accept-Encoding: gzip, deflate
Accept-Encoding 首部字段用來(lái)告知服務(wù)器用戶(hù)代理支持的內(nèi)容編碼及內(nèi)容編碼的優(yōu)先順序,并可一次性指定多種內(nèi)容編碼。同樣使用 q=[數(shù)值] 來(lái)表示相對(duì)優(yōu)先級(jí)。也可使用星號(hào)(*)作為通配符,指定任意的編碼格式。
5.4 Accept-Language
Accept-Lanuage: zh-cn,zh;q=0.7,en=us,en;q=0.3
告知服務(wù)器用戶(hù)代理能夠處理的自然語(yǔ)言集(指中文或英文等),以及自然語(yǔ)言集的相對(duì)優(yōu)先級(jí),可一次性指定多種自然語(yǔ)言集。同樣使用 q=[數(shù)值] 來(lái)表示相對(duì)優(yōu)先級(jí)。
5.5 Authorization
Authorization: Basic ldfKDHKfkDdasSAEdasd==
告知服務(wù)器用戶(hù)代理的認(rèn)證信息(證書(shū)值)。通常,想要通過(guò)服務(wù)器認(rèn)證的用戶(hù)代理會(huì)在接收到返回的 401 狀態(tài)碼響應(yīng)后,把首部字段 Authorization 加入請(qǐng)求中。共用緩存在接收到含有 Authorization 首部字段的請(qǐng)求時(shí)的操作處理會(huì)略有差異。
5.6 Expect
Expect: 100-continue
告知服務(wù)器客戶(hù)端期望出現(xiàn)的某種特定行為。
5.7 From
From: Deeson_Woo@163.com
告知服務(wù)器使用用戶(hù)代理的電子郵件地址。
5.8 Host
Host: www.jianshu.com
5.9 If-Match
形如 If-xxx 這種樣式的請(qǐng)求首部字段,都可稱(chēng)為條件請(qǐng)求。服務(wù)器接收到附帶條件的請(qǐng)求后,只有判斷指定條件為真時(shí),才會(huì)執(zhí)行請(qǐng)求。
If-Match: "123456"
5.10 If-Modified-Since
If-Modified-Since: Mon, 10 Jul 2017 15:50:06 GMT
5.11 If-None-Match
If-None-Match: "123456"
首部字段 If-None-Match 屬于附帶條件之一。它和首部字段 If-Match 作用相反。用于指定 If-None-Match 字段值的實(shí)體標(biāo)記(ETag)值與請(qǐng)求資源的 ETag 不一致時(shí),它就告知服務(wù)器處理該請(qǐng)求。
5.12 If-Range
If-Range: "123456"
5.13 If-Unmodified-Since
If-Unmodified-Since: Mon, 10 Jul 2017 15:50:06 GMT
首部字段 If-Unmodified-Since 和首部字段 If-Modified-Since 的作用相反。它的作用的是告知服務(wù)器,指定的請(qǐng)求資源只有在字段值內(nèi)指定的日期時(shí)間之后,未發(fā)生更新的情況下,才能處理請(qǐng)求。如果在指定日期時(shí)間后發(fā)生了更新,則以狀態(tài)碼 412 Precondition Failed 作為響應(yīng)返回。
5.14 Max-Forwards
Max-Forwards: 10
通過(guò) TRACE 方法或 OPTIONS 方法,發(fā)送包含首部字段 Max-Forwards 的請(qǐng)求時(shí),該字段以十進(jìn)制整數(shù)形式指定可經(jīng)過(guò)的服務(wù)器最大數(shù)目。服務(wù)器在往下一個(gè)服務(wù)器轉(zhuǎn)發(fā)請(qǐng)求之前,Max-Forwards 的值減 1 后重新賦值。當(dāng)服務(wù)器接收到 Max-Forwards 值為 0 的請(qǐng)求時(shí),則不再進(jìn)行轉(zhuǎn)發(fā),而是直接返回響應(yīng)。
5.15 Proxy-Authorization
Proxy-Authorization: Basic dGlwOjkpNLAGfFY5
5.16 Range
Range: bytes=5001-10000
5.17 Referer
Referer: http://www.sample.com/index.html
首部字段 Referer 會(huì)告知服務(wù)器請(qǐng)求的原始資源的 URI。
5.18 TE
TE: gzip, deflate; q=0.5
5.19 User-Agent
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:13.0) Gecko/20100101
6.1 Accept-Ranges
Accept-Ranges: bytes
6.2 Age
Age: 1200
6.3 ETag
ETag: "usagi-1234"
6.4 Location
Location: http://www.sample.com/sample.html
6.5 Proxy-Authenticate
Proxy-Authenticate: Basic realm="Usagidesign Auth"
6.6 Retry-After
Retry-After: 180
6.7 Server
Server: Apache/2.2.6 (Unix) PHP/5.2.5
首部字段 Server 告知客戶(hù)端當(dāng)前服務(wù)器上安裝的 HTTP 服務(wù)器應(yīng)用程序的信息。不單單會(huì)標(biāo)出服務(wù)器上的軟件應(yīng)用名稱(chēng),還有可能包括版本號(hào)和安裝時(shí)啟用的可選項(xiàng)。
6.8 Vary
Vary: Accept-Language
6.9 WWW-Authenticate
WWW-Authenticate: Basic realm="Usagidesign Auth"
首部字段 WWW-Authenticate 用于 HTTP 訪(fǎng)問(wèn)認(rèn)證。它會(huì)告知客戶(hù)端適用于訪(fǎng)問(wèn)請(qǐng)求 URI 所指定資源的認(rèn)證方案(Basic 或是 Digest)和帶參數(shù)提示的質(zhì)詢(xún)(challenge)。
7. 實(shí)體首部字段(HTTP/1.1)
7.1 Allow
Allow: GET, HEAD
7.2 Content-Encoding
Content-Encoding: gzip
7.3 Content-Language
Content-Language: zh-CN
首部字段 Content-Language 會(huì)告知客戶(hù)端,實(shí)體主體使用的自然語(yǔ)言(指中文或英文等語(yǔ)言)。
7.4 Content-Length
Content-Length: 15000
首部字段 Content-Length 表明了實(shí)體主體部分的大小(單位是字節(jié))。對(duì)實(shí)體主體進(jìn)行內(nèi)容編碼傳輸時(shí),不能再使用 Content-Length首部字段。
7.5 Content-Location
Content-Location: http://www.sample.com/index.html
首部字段 Content-Location 給出與報(bào)文主體部分相對(duì)應(yīng)的 URI。和首部字段 Location 不同,Content-Location 表示的是報(bào)文主體返回資源對(duì)應(yīng)的 URI。
7.6 Content-MD5
Content-MD5: OGFkZDUwNGVhNGY3N2MxMDIwZmQ4NTBmY2IyTY==
首部字段 Content-MD5 是一串由 MD5 算法生成的值,其目的在于檢查報(bào)文主體在傳輸過(guò)程中是否保持完整,以及確認(rèn)傳輸?shù)竭_(dá)。
7.7 Content-Range
Content-Range: bytes 5001-10000/10000
針對(duì)范圍請(qǐng)求,返回響應(yīng)時(shí)使用的首部字段 Content-Range,能告知客戶(hù)端作為響應(yīng)返回的實(shí)體的哪個(gè)部分符合范圍請(qǐng)求。字段值以字節(jié)為單位,表示當(dāng)前發(fā)送部分及整個(gè)實(shí)體大小。
7.8 Content-Type
Content-Type: text/html; charset=UTF-8
首部字段 Content-Type 說(shuō)明了實(shí)體主體內(nèi)對(duì)象的媒體類(lèi)型。和首部字段 Accept 一樣,字段值用 type/subtype 形式賦值。參數(shù) charset 使用 iso-8859-1 或 euc-jp 等字符集進(jìn)行賦值。
7.9 Expires
Expires: Mon, 10 Jul 2017 15:50:06 GMT
7.10 Last-Modified
Last-Modified: Mon, 10 Jul 2017 15:50:06 GMT
首部字段 Last-Modified 指明資源最終修改的時(shí)間。一般來(lái)說(shuō),這個(gè)值就是 Request-URI 指定資源被修改的時(shí)間。但類(lèi)似使用 CGI 腳本進(jìn)行動(dòng)態(tài)數(shù)據(jù)處理時(shí),該值有可能會(huì)變成數(shù)據(jù)最終修改時(shí)的時(shí)間。
8. 為 Cookie 服務(wù)的首部字段
8.1 Set-Cookie
Set-Cookie: status=enable; expires=Mon, 10 Jul 2017 15:50:06 GMT; path=/;
下面的表格列舉了 Set-Cookie 的字段值。
8.1.1 expires 屬性
8.1.2 path 屬性
Cookie 的 path 屬性可用于限制指定 Cookie 的發(fā)送范圍的文件目錄。
8.1.3 domain 屬性
8.1.4 secure 屬性
Cookie 的 secure 屬性用于限制 Web 頁(yè)面僅在 HTTPS 安全連接時(shí),才可以發(fā)送 Cookie。
8.1.5 HttpOnly 屬性
8.2 Cookie
Cookie: status=enable
首部字段 Cookie 會(huì)告知服務(wù)器,當(dāng)客戶(hù)端想獲得 HTTP 狀態(tài)管理支持時(shí),就會(huì)在請(qǐng)求中包含從服務(wù)器接收到的 Cookie。接收到多個(gè) Cookie 時(shí),同樣可以以多個(gè) Cookie 形式發(fā)送。
9. 其他首部字段
HTTP 首部字段是可以自行擴(kuò)展的。所以在 Web 服務(wù)器和瀏覽器的應(yīng)用上,會(huì)出現(xiàn)各種非標(biāo)準(zhǔn)的首部字段。
以下是最為常用的首部字段。
9.1 X-Frame-Options
X-Frame-Options: DENY
首部字段 X-Frame-Options 屬于 HTTP 響應(yīng)首部,用于控制網(wǎng)站內(nèi)容在其他 Web 網(wǎng)站的 Frame 標(biāo)簽內(nèi)的顯示問(wèn)題。其主要目的是為了防止點(diǎn)擊劫持(clickjacking)攻擊。首部字段 X-Frame-Options 有以下兩個(gè)可指定的字段值:
9.2 X-XSS-Protection
X-XSS-Protection: 1
首部字段 X-XSS-Protection 屬于 HTTP 響應(yīng)首部,它是針對(duì)跨站腳本攻擊(XSS)的一種對(duì)策,用于控制瀏覽器 XSS 防護(hù)機(jī)制的開(kāi)關(guān)。首部字段 X-XSS-Protection 可指定的字段值如下:
9.3 DNT
DNT: 1
首部字段 DNT 屬于 HTTP 請(qǐng)求首部,其中 DNT 是 Do Not Track 的簡(jiǎn)稱(chēng),意為拒絕個(gè)人信息被收集,是表示拒絕被精準(zhǔn)廣告追蹤的一種方法。首部字段 DNT 可指定的字段值如下:
由于首部字段 DNT 的功能具備有效性,所以 Web 服務(wù)器需要對(duì) DNT做對(duì)應(yīng)的支持。
9.4 P3P
P3P: CP="CAO DSP LAW CURa ADMa DEVa TAIa PSAa PSDa IVAa IVDa OUR BUS IND
首部字段 P3P 屬于 HTTP 響應(yīng)首部,通過(guò)利用 P3P(The Platform for Privacy Preferences,在線(xiàn)隱私偏好平臺(tái))技術(shù),可以讓 Web 網(wǎng)站上的個(gè)人隱私變成一種僅供程序可理解的形式,以達(dá)到保護(hù)用戶(hù)隱私的目的。
要進(jìn)行 P3P 的設(shè)定,需按以下操作步驟進(jìn)行:
1. 狀態(tài)碼概述
2. 狀態(tài)碼類(lèi)別
我們可以自行改變 RFC2616 中定義的狀態(tài)碼或者服務(wù)器端自行創(chuàng)建狀態(tài)碼,只要遵守狀態(tài)碼的類(lèi)別定義就可以了。
3. 常用狀態(tài)碼解析
HTTP 狀態(tài)碼種類(lèi)繁多,數(shù)量達(dá)幾十種。其中最常用的有以下 14 種,一起來(lái)看看。
3.1 200 OK
表示從客戶(hù)端發(fā)來(lái)的請(qǐng)求在服務(wù)器端被正常處理了。
3.2 204 No Content
3.3 206 Partial Content
表示客戶(hù)端進(jìn)行了范圍請(qǐng)求,而服務(wù)器成功執(zhí)行了這部分的 GET 請(qǐng)求。響應(yīng)報(bào)文中包含由 Content-Range 首部字段指定范圍的實(shí)體內(nèi)容。
3.4 301 Moved Permanently
永久性重定向。表示請(qǐng)求的資源已被分配了新的 URI。以后應(yīng)使用資源現(xiàn)在所指的 URI。也就是說(shuō),如果已經(jīng)把資源對(duì)應(yīng)的 URI 保存為書(shū)簽了,這時(shí)應(yīng)該按 Location 首部字段提示的 URI 重新保存。
3.5 302 Found
3.6 303 See Other
3.7 304 Not Modified
3.8 307 Temporary Redirect
臨時(shí)重定向。該狀態(tài)碼與 302 Found 有著相同的含義。
3.9 400 Bad Request
3.10 401 Unauthorized
3.11 403 Forbidden
表明對(duì)請(qǐng)求資源的訪(fǎng)問(wèn)被服務(wù)器拒絕了。服務(wù)器端沒(méi)有必要給出詳細(xì)的拒絕理由,當(dāng)然也可以在響應(yīng)報(bào)文的實(shí)體主體部分對(duì)原因進(jìn)行描述。
3.12 404 Not Found
表明服務(wù)器上無(wú)法找到請(qǐng)求的資源。除此之外,也可以在服務(wù)器端拒絕請(qǐng)求且不想說(shuō)明理由的時(shí)候使用。
3.13 500 Internal Server Error
表明服務(wù)器端在執(zhí)行請(qǐng)求時(shí)發(fā)生了錯(cuò)誤。也可能是 Web 應(yīng)用存在的 bug 或某些臨時(shí)的故障。
3.14 503 Service Unavailable
表明服務(wù)器暫時(shí)處于超負(fù)載或正在進(jìn)行停機(jī)維護(hù),現(xiàn)在無(wú)法處理請(qǐng)求。如果事先得知解除以上狀況需要的時(shí)間,最好寫(xiě)入 Retry-After 首部字段再返回給客戶(hù)端。
1. HTTP 報(bào)文實(shí)體概述
HTTP 報(bào)文結(jié)構(gòu)
大家請(qǐng)仔細(xì)看看上面示例中,各個(gè)組成部分對(duì)應(yīng)的內(nèi)容。
接著,我們來(lái)看看報(bào)文和實(shí)體的概念。如果把 HTTP 報(bào)文想象成因特網(wǎng)貨運(yùn)系統(tǒng)中的箱子,那么 HTTP 實(shí)體就是報(bào)文中實(shí)際的貨物。
我們可以看到,上面示例右圖中深紅色框的內(nèi)容就是報(bào)文的實(shí)體部分,而藍(lán)色框的兩部分內(nèi)容分別就是實(shí)體首部和實(shí)體主體。而左圖中粉紅框內(nèi)容就是報(bào)文主體。
通常,報(bào)文主體等于實(shí)體主體。只有當(dāng)傳輸中進(jìn)行編碼操作時(shí),實(shí)體主體的內(nèi)容發(fā)生變化,才導(dǎo)致它和報(bào)文主體產(chǎn)生差異。
2. 內(nèi)容編碼
內(nèi)容編碼類(lèi)型:
3. 傳輸編碼
內(nèi)容編碼是對(duì)報(bào)文的主體進(jìn)行的可逆變換,是和內(nèi)容的具體格式細(xì)節(jié)緊密相關(guān)的。
傳輸編碼也是作用在實(shí)體主體上的可逆變換,但使用它們是由于架構(gòu)方面的原因,同內(nèi)容的格式無(wú)關(guān)。使用傳輸編碼是為了改變報(bào)文中的數(shù)據(jù)在網(wǎng)絡(luò)上傳輸?shù)姆绞健?/p>
內(nèi)容編碼和傳輸編碼的對(duì)比
4. 分塊編碼
分塊編碼把報(bào)文分割成若干已知大小的塊。塊之間是緊挨著發(fā)送的,這樣就不需要在發(fā)送之前知道整個(gè)報(bào)文的大小了。分塊編碼是一種傳輸編碼,是報(bào)文的屬性。
分塊編碼與持久連接
若客戶(hù)端與服務(wù)器端之間不是持久連接,客戶(hù)端就不需要知道它在讀取的主體的長(zhǎng)度,而只需要讀取到服務(wù)器關(guān)閉主體連接為止。
當(dāng)使用持久連接時(shí),在服務(wù)器寫(xiě)主體之前,必須知道它的大小并在 Content-Length 首部中發(fā)送。如果服務(wù)器動(dòng)態(tài)創(chuàng)建內(nèi)容,就可能在發(fā)送之前無(wú)法知道主體的長(zhǎng)度。
分塊編碼為這種困難提供了解決方案,只要允許服務(wù)器把主體分塊發(fā)送,說(shuō)明每塊的大小就可以了。因?yàn)橹黧w是動(dòng)態(tài)創(chuàng)建的,服務(wù)器可以緩沖它的一部分,發(fā)送其大小和相應(yīng)的塊,然后在主體發(fā)送完之前重復(fù)這個(gè)過(guò)程。服務(wù)器可以用大小為 0 的塊作為主體結(jié)束的信號(hào),這樣就可以繼續(xù)保持連接,為下一個(gè)響應(yīng)做準(zhǔn)備。
來(lái)看看一個(gè)分塊編碼的報(bào)文示例:
分塊編碼的報(bào)文
5.多部分媒體類(lèi)型
MIME 中的 multipart(多部分)電子郵件報(bào)文中包含多個(gè)報(bào)文,它們合在一起作為單一的復(fù)雜報(bào)文發(fā)送。每一部分都是獨(dú)立的,有各自的描述其內(nèi)容的集,不同部分之間用分界字符串連接在一起。
相應(yīng)得,HTTP 協(xié)議中也采納了多部分對(duì)象集合,發(fā)送的一份報(bào)文主體內(nèi)可包含多種類(lèi)型實(shí)體。
多部分對(duì)象集合包含的對(duì)象如下:
6. 范圍請(qǐng)求
假設(shè)你正在下載一個(gè)很大的文件,已經(jīng)下了四分之三,忽然網(wǎng)絡(luò)中斷了,那下載就必須重頭再來(lái)一遍。為了解決這個(gè)問(wèn)題,需要一種可恢復(fù)的機(jī)制,即能從之前下載中斷處恢復(fù)下載。要實(shí)現(xiàn)該功能,這就要用到范圍請(qǐng)求。
有了范圍請(qǐng)求, HTTP 客戶(hù)端可以通過(guò)請(qǐng)求曾獲取失敗的實(shí)體的一個(gè)范圍(或者說(shuō)一部分),來(lái)恢復(fù)下載該實(shí)體。當(dāng)然這有一個(gè)前提,那就是從客戶(hù)端上一次請(qǐng)求該實(shí)體到這一次發(fā)出范圍請(qǐng)求的時(shí)間段內(nèi),該對(duì)象沒(méi)有改變過(guò)。例如:
GET /bigfile.html HTTP/1.1 Host: www.sample.com Range: bytes=20224- ···
實(shí)體范圍請(qǐng)求示例
上面示例中,客戶(hù)端請(qǐng)求的是文檔開(kāi)頭20224字節(jié)之后的部分。
HTTP 通信時(shí),除客戶(hù)端和服務(wù)器外,還有一些用于協(xié)助通信的應(yīng)用程序。如下列出比較重要的幾個(gè):代理、緩存、網(wǎng)關(guān)、隧道、Agent 代理。
1.代理
代理
HTTP 代理服務(wù)器是 Web 安全、應(yīng)用集成以及性能優(yōu)化的重要組成模塊。代理位于客戶(hù)端和服務(wù)器端之間,接收客戶(hù)端所有的 HTTP 請(qǐng)求,并將這些請(qǐng)求轉(zhuǎn)發(fā)給服務(wù)器(可能會(huì)對(duì)請(qǐng)求進(jìn)行修改之后再進(jìn)行轉(zhuǎn)發(fā))。對(duì)用戶(hù)來(lái)說(shuō),這些應(yīng)用程序就是一個(gè)代理,代表用戶(hù)訪(fǎng)問(wèn)服務(wù)器。
出于安全考慮,通常會(huì)將代理作為轉(zhuǎn)發(fā)所有 Web 流量的可信任中間節(jié)點(diǎn)使用。代理還可以對(duì)請(qǐng)求和響應(yīng)進(jìn)行過(guò)濾,安全上網(wǎng)或綠色上網(wǎng)。
2. 緩存
瀏覽器第一次請(qǐng)求:
瀏覽器第一次請(qǐng)求
瀏覽器再次請(qǐng)求:
瀏覽器再次請(qǐng)求
Web 緩存或代理緩存是一種特殊的 HTTP 代理服務(wù)器,可以將經(jīng)過(guò)代理傳輸?shù)某S梦臋n復(fù)制保存起來(lái)。下一個(gè)請(qǐng)求同一文檔的客戶(hù)端就可以享受緩存的私有副本所提供的服務(wù)了。客戶(hù)端從附近的緩存下載文檔會(huì)比從遠(yuǎn)程 Web 服務(wù)器下載快得多。
3. 網(wǎng)關(guān)
HTTP / FTP 網(wǎng)關(guān)
網(wǎng)關(guān)是一種特殊的服務(wù)器,作為其他服務(wù)器的中間實(shí)體使用。通常用于將 HTTP 流量轉(zhuǎn)換成其他的協(xié)議。網(wǎng)關(guān)接收請(qǐng)求時(shí)就好像自己是資源的源服務(wù)器一樣。客戶(hù)端可能并不知道自己正在跟一個(gè)網(wǎng)關(guān)進(jìn)行通信。
4. 隧道
HTTP/SSL 隧道
隧道是會(huì)在建立起來(lái)之后,就會(huì)在兩條連接之間對(duì)原始數(shù)據(jù)進(jìn)行盲轉(zhuǎn)發(fā)的 HTTP 應(yīng)用程序。HTTP 隧道通常用來(lái)在一條或多條 HTTP 連接上轉(zhuǎn)發(fā)非 HTTP 數(shù)據(jù),轉(zhuǎn)發(fā)時(shí)不會(huì)窺探數(shù)據(jù)。
HTTP 隧道的一種常見(jiàn)用途就是通過(guò) HTTP 連接承載加密的安全套接字層(SSL)流量,這樣 SSL 流量就可以穿過(guò)只允許 Web 流量通過(guò)的防火墻了。
5. Agent 代理
自動(dòng)搜索引擎“網(wǎng)絡(luò)蜘蛛”
Agent 代理是代表用戶(hù)發(fā)起 HTTP 請(qǐng)求的客戶(hù)端應(yīng)用程序。所有發(fā)布 Web 請(qǐng)求的應(yīng)用程序都是 HTTP Agent 代理。
來(lái)源:簡(jiǎn)書(shū) 鏈接:https://www.jianshu.com/p/6e9e4156ece3
今日內(nèi)容:
1. Servlet
2. HTTP協(xié)議
3. Request
一、Servlet補(bǔ)充內(nèi)容:
1.1 Servlet的體系結(jié)構(gòu):
Servlet -- 接口
|
GenericServlet -- 抽象類(lèi)
|
HttpServlet -- 抽象類(lèi)
1.2 GenericServlet:將Servlet接口中其他的方法做了默認(rèn)空實(shí)現(xiàn),只將service()方法作為抽象,將來(lái)定義Servlet類(lèi)時(shí),可以繼承GenericServlet,實(shí)現(xiàn)service()方法即可
1.3 HttpServlet:對(duì)http協(xié)議的一種封裝,簡(jiǎn)化操作
1. 定義類(lèi)繼承HttpServlet
2. 復(fù)寫(xiě)doGet/doPost方法
1.2 Servlet相關(guān)配置
1.2.1 urlpartten:Servlet訪(fǎng)問(wèn)路徑
1. 一個(gè)Servlet可以定義多個(gè)訪(fǎng)問(wèn)路徑 : @WebServlet({"/d4","/dd4","/ddd4"})
2. 路徑定義規(guī)則:
1. /xxx:路徑匹配
2. /xxx/xxx:多層路徑,目錄結(jié)構(gòu)
3. *.do:擴(kuò)展名匹配
二、HTTP:
2.1 概念:Hyper Text Transfer Protocol 超文本傳輸協(xié)議
* 傳輸協(xié)議:定義了,客戶(hù)端和服務(wù)器端通信時(shí),發(fā)送數(shù)據(jù)的格式
* 特點(diǎn):
1. 基于TCP/IP的高級(jí)協(xié)議
2. 默認(rèn)端口號(hào):80
3. 基于請(qǐng)求/響應(yīng)模型的:一次請(qǐng)求對(duì)應(yīng)一次響應(yīng)
4. 無(wú)狀態(tài)的:每次請(qǐng)求之間相互獨(dú)立,不能交互數(shù)據(jù)
* 歷史版本:
* 1.0:每一次請(qǐng)求響應(yīng)都會(huì)建立新的連接
* 1.1:復(fù)用連接
2.2 請(qǐng)求消息數(shù)據(jù)格式
2.2.1 請(qǐng)求行:
請(qǐng)求方式 請(qǐng)求url 請(qǐng)求協(xié)議/版本
GET /login.html HTTP/1.1
2.2.2 請(qǐng)求方式:
* HTTP協(xié)議有7中請(qǐng)求方式,常用的有2種
* GET:
1. 請(qǐng)求參數(shù)在請(qǐng)求行中,在url后。
2. 請(qǐng)求的url長(zhǎng)度有限制的
3. 不太安全
* POST:
1. 請(qǐng)求參數(shù)在請(qǐng)求體中
2. 請(qǐng)求的url長(zhǎng)度沒(méi)有限制的
3. 相對(duì)安全
2.2.3 請(qǐng)求頭:客戶(hù)端瀏覽器告訴服務(wù)器一些信息
請(qǐng)求頭名稱(chēng): 請(qǐng)求頭值
* 常見(jiàn)的請(qǐng)求頭:
1. User-Agent:瀏覽器告訴服務(wù)器,我訪(fǎng)問(wèn)你使用的瀏覽器版本信息
* 可以在服務(wù)器端獲取該頭的信息,解決瀏覽器的兼容性問(wèn)題
2. Referer:http://localhost/login.html
* 告訴服務(wù)器,我(當(dāng)前請(qǐng)求)從哪里來(lái)?
* 作用:
1. 防盜鏈:
2. 統(tǒng)計(jì)工作:
2.2.4 請(qǐng)求空行
空行,就是用于分割POST請(qǐng)求的請(qǐng)求頭,和請(qǐng)求體的。
2.2.5 請(qǐng)求體(正文):
* 封裝POST請(qǐng)求消息的請(qǐng)求參數(shù)的
2.2.6 請(qǐng)求消息舉例:
POST /login.html HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/login.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
username=zhangsan
三、Request:
3.1 request對(duì)象和response對(duì)象的原理
1. request和response對(duì)象是由服務(wù)器創(chuàng)建的。我們來(lái)使用它們
2. request對(duì)象是來(lái)獲取請(qǐng)求消息,response對(duì)象是來(lái)設(shè)置響應(yīng)消息
3.2 request對(duì)象繼承體系結(jié)構(gòu):
ServletRequest -- 接口
| 繼承
HttpServletRequest -- 接口
| 實(shí)現(xiàn)
org.apache.catalina.connector.RequestFacade 類(lèi)(tomcat)
3.3 request功能:
3.3.1 獲取請(qǐng)求消息數(shù)據(jù)
1. 獲取請(qǐng)求行數(shù)據(jù)
* GET /day14/demo1?name=zhangsan HTTP/1.1
* 方法:
1. 獲取請(qǐng)求方式 :GET
* String getMethod()
2. (*)獲取虛擬目錄:/day14
* String getContextPath()
3. 獲取Servlet路徑: /demo1
* String getServletPath()
4. 獲取get方式請(qǐng)求參數(shù):name=zhangsan
* String getQueryString()
5. (*)獲取請(qǐng)求URI:/day14/demo1
* String getRequestURI(): /day14/demo1
* StringBuffer getRequestURL() :http://localhost/day14/demo1
* URL:統(tǒng)一資源定位符 : http://localhost/day14/demo1
* URI:統(tǒng)一資源標(biāo)識(shí)符 : /day14/demo1
6. 獲取協(xié)議及版本:HTTP/1.1
* String getProtocol()
7. 獲取客戶(hù)機(jī)的IP地址:
* String getRemoteAddr()
2. 獲取請(qǐng)求頭數(shù)據(jù)
* 方法:
* (*)String getHeader(String name):通過(guò)請(qǐng)求頭的名稱(chēng)獲取請(qǐng)求頭的值
* Enumeration<String> getHeaderNames():獲取所有的請(qǐng)求頭名稱(chēng)
3. 獲取請(qǐng)求體數(shù)據(jù):
* 請(qǐng)求體:只有POST請(qǐng)求方式,才有請(qǐng)求體,
在請(qǐng)求體中封裝了POST請(qǐng)求的請(qǐng)求參數(shù)
* 步驟:
1. 獲取流對(duì)象
* BufferedReader getReader():獲取字符輸入流,只能操作字符數(shù)據(jù)
* ServletInputStream getInputStream():獲取字節(jié)輸入流,可以操作所有類(lèi)型數(shù)據(jù)
2. 再?gòu)牧鲗?duì)象中拿數(shù)據(jù)
3.3.2 其他功能:獲取請(qǐng)求參數(shù)通用方式:不論get還是post請(qǐng)求方式都可以使用下列方法來(lái)獲取請(qǐng)求參數(shù):
1. String getParameter(String name):根據(jù)參數(shù)名稱(chēng)獲取值 username=zs
2. String[] getParameterValues(String name):根據(jù)參數(shù)名稱(chēng)獲取參數(shù)值的數(shù)組 hobby=xx&hobby=game
3. Enumeration<String> getParameterNames():獲取所有請(qǐng)求的參數(shù)名稱(chēng)
4. Map<String,String[]> getParameterMap():獲取所有參數(shù)的map集合
* 中文亂碼問(wèn)題:
* get方式:tomcat 8 已經(jīng)將get方式亂碼問(wèn)題解決了
* post方式:會(huì)亂碼
* 解決:在獲取參數(shù)前,設(shè)置request的編碼為
request.setCharacterEncoding("utf-8");
3.3.3 請(qǐng)求轉(zhuǎn)發(fā):一種在服務(wù)器內(nèi)部的資源跳轉(zhuǎn)方式
1. 步驟:
1. 通過(guò)request對(duì)象獲取請(qǐng)求轉(zhuǎn)發(fā)器對(duì)象:
RequestDispatcher getRequestDispatcher(String path)
2. 使用RequestDispatcher對(duì)象來(lái)進(jìn)行轉(zhuǎn)發(fā):
forward(ServletRequest request, ServletResponse response)
2. 特點(diǎn):
1. 瀏覽器地址欄路徑不發(fā)生變化
2. 只能轉(zhuǎn)發(fā)到當(dāng)前服務(wù)器內(nèi)部資源中。
3. 轉(zhuǎn)發(fā)是一次請(qǐng)求
3.3.4. 共享數(shù)據(jù):
* 域?qū)ο螅阂粋€(gè)有作用范圍的對(duì)象,可以在范圍內(nèi)共享數(shù)據(jù)
* request域:代表一次請(qǐng)求的范圍,一般用于請(qǐng)求轉(zhuǎn)發(fā)的多個(gè)資源中共享數(shù)據(jù)
* 方法:
1. void setAttribute(String name,Object obj):存儲(chǔ)數(shù)據(jù)
2. Object getAttitude(String name):通過(guò)鍵獲取值
3. void removeAttribute(String name):通過(guò)鍵移除鍵值對(duì)
3.3.5. 獲取ServletContext:
* ServletContext getServletContext()
四、案例:用戶(hù)登錄
* 用戶(hù)登錄案例需求:
1.編寫(xiě)login.html登錄頁(yè)面
username & password 兩個(gè)輸入框
2.使用Druid數(shù)據(jù)庫(kù)連接池技術(shù),操作mysql,day14數(shù)據(jù)庫(kù)中user表
3.使用JdbcTemplate技術(shù)封裝JDBC
4.登錄成功跳轉(zhuǎn)到SuccessServlet展示:登錄成功!用戶(hù)名,歡迎您
5.登錄失敗跳轉(zhuǎn)到FailServlet展示:登錄失敗,用戶(hù)名或密碼錯(cuò)誤
* 分析
* 開(kāi)發(fā)步驟
1. 創(chuàng)建項(xiàng)目,導(dǎo)入html頁(yè)面,配置文件,jar包
2. 創(chuàng)建數(shù)據(jù)庫(kù)環(huán)境
CREATE DATABASE day14;
USE day14;
CREATE TABLE USER(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32) UNIQUE NOT NULL,
PASSWORD VARCHAR(32) NOT NULL
);
3. 創(chuàng)建包c(diǎn)n.itcast.domain,創(chuàng)建類(lèi)User
package cn.itcast.domain;
/**
* 用戶(hù)的實(shí)體類(lèi)
*/
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id=id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username=username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password=password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
4. 創(chuàng)建包c(diǎn)n.itcast.util,編寫(xiě)工具類(lèi)JDBCUtils
package cn.itcast.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import javax.xml.crypto.Data;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
/**
* JDBC工具類(lèi) 使用Durid連接池
*/
public class JDBCUtils {
private static DataSource ds ;
static {
try {
//1.加載配置文件
Properties pro=new Properties();
//使用ClassLoader加載配置文件,獲取字節(jié)輸入流
InputStream is=JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//2.初始化連接池對(duì)象
ds=DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 獲取連接池對(duì)象
*/
public static DataSource getDataSource(){
return ds;
}
/**
* 獲取連接Connection對(duì)象
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
}
5. 創(chuàng)建包c(diǎn)n.itcast.dao,創(chuàng)建類(lèi)UserDao,提供login方法
package cn.itcast.dao;
import cn.itcast.domain.User;
import cn.itcast.util.JDBCUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* 操作數(shù)據(jù)庫(kù)中User表的類(lèi)
*/
public class UserDao {
//聲明JDBCTemplate對(duì)象共用
private JdbcTemplate template=new JdbcTemplate(JDBCUtils.getDataSource());
/**
* 登錄方法
* @param loginUser 只有用戶(hù)名和密碼
* @return user包含用戶(hù)全部數(shù)據(jù),沒(méi)有查詢(xún)到,返回null
*/
public User login(User loginUser){
try {
//1.編寫(xiě)sql
String sql="select * from user where username=? and password=?";
//2.調(diào)用query方法
User user=template.queryForObject(sql,
new BeanPropertyRowMapper<User>(User.class),
loginUser.getUsername(), loginUser.getPassword());
return user;
} catch (DataAccessException e) {
e.printStackTrace();//記錄日志
return null;
}
}
}
6. 編寫(xiě)cn.itcast.web.servlet.LoginServlet類(lèi)
package cn.itcast.web.servlet;
import cn.itcast.dao.UserDao;
import cn.itcast.domain.User;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/loginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.設(shè)置編碼
req.setCharacterEncoding("utf-8");
//2.獲取請(qǐng)求參數(shù)
String username=req.getParameter("username");
String password=req.getParameter("password");
//3.封裝user對(duì)象
User loginUser=new User();
loginUser.setUsername(username);
loginUser.setPassword(password);
//4.調(diào)用UserDao的login方法
UserDao dao=new UserDao();
User user=dao.login(loginUser);
//5.判斷user
if(user==null){
//登錄失敗
req.getRequestDispatcher("/failServlet").forward(req,resp);
}else{
//登錄成功
//存儲(chǔ)數(shù)據(jù)
req.setAttribute("user",user);
//轉(zhuǎn)發(fā)
req.getRequestDispatcher("/successServlet").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req,resp);
}
}
7. 編寫(xiě)FailServlet和SuccessServlet類(lèi)
@WebServlet("/successServlet")
public class SuccessServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//獲取request域中共享的user對(duì)象
User user=(User) request.getAttribute("user");
if(user !=null){
//給頁(yè)面寫(xiě)一句話(huà)
//設(shè)置編碼
response.setContentType("text/html;charset=utf-8");
//輸出
response.getWriter().write("登錄成功!"+user.getUsername()+",歡迎您");
}
}
@WebServlet("/failServlet")
public class FailServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//給頁(yè)面寫(xiě)一句話(huà)
//設(shè)置編碼
response.setContentType("text/html;charset=utf-8");
//輸出
response.getWriter().write("登錄失敗,用戶(hù)名或密碼錯(cuò)誤");
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
8. login.html中form表單的action路徑的寫(xiě)法
* 虛擬目錄+Servlet的資源路徑
9. BeanUtils工具類(lèi),簡(jiǎn)化數(shù)據(jù)封裝
* 用于封裝JavaBean的
1. JavaBean:標(biāo)準(zhǔn)的Java類(lèi)
1. 要求:
1. 類(lèi)必須被public修飾
2. 必須提供空參的構(gòu)造器
3. 成員變量必須使用private修飾
4. 提供公共setter和getter方法
2. 功能:封裝數(shù)據(jù)
2. 概念:
成員變量:
屬性:setter和getter方法截取后的產(chǎn)物
例如:getUsername() --> Username--> username
3. 方法:
1. setProperty()
2. getProperty()
3. populate(Object obj , Map map):將map集合的鍵值對(duì)信息,封裝到對(duì)應(yīng)的JavaBean對(duì)象中
文共2678字,預(yù)計(jì)學(xué)習(xí)時(shí)長(zhǎng)15分鐘
圖源:unsplash
使用JavaScript時(shí),總會(huì)有各種需要發(fā)出調(diào)用請(qǐng)求的情況,進(jìn)行ajax調(diào)用什么技術(shù)更適合呢?
最初,盡管有一些方法可以在不刷新頁(yè)面的情況下從服務(wù)器提取數(shù)據(jù),但它們通常依賴(lài)于笨拙的技術(shù)。直到微軟為Outlook電子郵件客戶(hù)端的替代瀏覽器開(kāi)發(fā)了XMLHttpRequest。它在2006年成為了Web標(biāo)準(zhǔn)。
2015年,F(xiàn)etch API隨ES6引入。通用的Request和Response接口提供了一致性,而Promises允許更容易的鏈接和沒(méi)有回調(diào)的異步/等待。Fetch簡(jiǎn)潔,優(yōu)雅且易于理解,但是還有其他不錯(cuò)的選擇,本文將簡(jiǎn)要的含義、語(yǔ)法以及利弊。
以下代碼展示了使用不同替代方法的基本HTTP GET和POST示例。現(xiàn)在開(kāi)始吧~
XMLHttpRequest對(duì)象可用于從Web服務(wù)器請(qǐng)求數(shù)據(jù)。它是這次比較中最早的方法,盡管其他選擇都優(yōu)于它,但由于其向后兼容性和成熟度,它仍然有效且有用。
得到:
var req=new XMLHttpRequest();//The onreadystatechange property
//specifies a function to be
//executed every time the status
//of the XMLHttpRequest changes
req.onreadystatechange=function() {
if (this.readyState==4 &&this.status==200) {
//The responseText property
//returns a text string
console.log(xhttp.responseText)
//Do some stuff
}
};req.open("GET", "http://dataserver/users", true);
req.send();
發(fā)送:
varformData=new FormData();
formData.append("name", "Murdock");
var req=new XMLHttpRequest();
req.open("POST", "http://dataserver/update");
req.send(formData);
優(yōu)點(diǎn):
· 不需要從外部源加載
· 向后兼容性
· 成熟/穩(wěn)定
· 在所有瀏覽器中均可使用
· 是原生瀏覽器API
缺點(diǎn):
· 支持回調(diào)地獄
· 笨拙冗長(zhǎng)的語(yǔ)法
· Fetch能自然地替代它
圖源:unsplash
Qwest是一個(gè)基于Promise的簡(jiǎn)單ajax庫(kù),它支持XmlHttpRequest2的獨(dú)立數(shù)據(jù),例如ArrayBuffer,Blob和FormData。
得到:
qwest.get('http://dataserver/data.json')
.then(function(xhr, response) {
// ...do some stuff whith data
});
發(fā)送:
qwest.post('http://dataserver/update',{
firstname: 'Murdock',
age: 30
})
.then(function(xhr, response) {
// Make some useful actions
})
.catch(function(e, xhr, response) {
// Process the error
});
優(yōu)點(diǎn):
· 可以建立請(qǐng)求限制
· 基于Promise
缺點(diǎn):
· 并非所有瀏覽器上都可使用XmlHttpRequest2
· 非原生
· 必須從外部源加載
該庫(kù)在不久前被廣泛用于發(fā)出HTTP異步請(qǐng)求。jQuery的所有Ajax方法都返回XMLHTTPRequest對(duì)象的超集
得到:
$.ajax({
url: 'http://dataserver/data.json'
}).done(function(data) {
// ...do some stuff whith data
}).fail(function() {
// Handle error
});
發(fā)送:
$.ajax({
type: "POST",
url: 'http://dataserver/update',
data: data,
success: successCallBack,
error: errorCallBack,
dataType: dataType
});
優(yōu)點(diǎn):
· 良好的支持和文檔
· 可配置的對(duì)象
· 在許多項(xiàng)目中使用
· 學(xué)習(xí)曲線(xiàn)低
· 它返回XMLHttpRequest對(duì)象,因此可以中止請(qǐng)求
缺點(diǎn):
· 非原生
· 必須從外部源加載
· 沒(méi)有與Promises結(jié)合
· 對(duì)于原生ES6 Fetch不是必需的。
圖源:unsplash
基于Promise的HTTP庫(kù),用于在瀏覽器和Nodejs上執(zhí)行HTTP請(qǐng)求。
得到:
axios({
url: 'http://dataserver/data.json',
method: 'get'
})
發(fā)送:
axios.post('http://dataserver/update',{
name: 'Murdock'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
優(yōu)點(diǎn):
· 使用promise避免回調(diào)地獄
· 在瀏覽器和Nodejs上均可使用
· 支持上傳進(jìn)度
· 可以設(shè)置響應(yīng)超時(shí)
· 通過(guò)簡(jiǎn)單地向其傳遞配置對(duì)象即可配置請(qǐng)求
· Axios已實(shí)現(xiàn)可撤銷(xiāo)的promise提議
· 自動(dòng)將數(shù)據(jù)轉(zhuǎn)換為JSON
缺點(diǎn):
· 非原生
· 必須從外部源加載
SuperAgent是ajax API,旨在提供靈活性,可讀性和較低的學(xué)習(xí)曲線(xiàn)。它也可以與Node.js一起使用。
得到:
request('GET','http://dataserver/data.json').then(
success, failure);
.query()方法接受對(duì)象,這些對(duì)象與GET方法一起使用時(shí)將形成查詢(xún)字符串。以下代碼將產(chǎn)生路徑/ dataserver / search?name=Manny&lastName=Peck&order=desc。
request
.get('/dataserver/search')
.query({ name: 'Templeton' })
.query({ lastname: 'Peck' })
.query({ order: 'desc' })
.then(res=> {console.dir(res)}
});
發(fā)送:
request
.post('http://dataserver/update')
.send({ name: 'Murdock' })
.set('Accept', 'application/json')
.then(res=> {
console.log('result' +JSON.stringify(res.body));
});
優(yōu)點(diǎn):
· 基于Promise
· 在Node.js和瀏覽器中均可使用
· 可以調(diào)用request.abort()方法中止請(qǐng)求
· 社區(qū)的知名庫(kù)
· 發(fā)出HTTP請(qǐng)求的無(wú)縫接口
· 出現(xiàn)故障時(shí)支持重試請(qǐng)求
缺點(diǎn):
· 它不支持以XMLHttpRequest的形式監(jiān)視加載進(jìn)度
· 非原生
· 必須從外部源加載
圖源:unsplash
Http-client允許使用JavaScript的訪(fǎng)存API組成HTTP客戶(hù)端。
得到:
//usingES6 modules
import { createFetch, base, accept, parse } from 'http-client'const fetch=createFetch(
base('http://dataserver/data.json'),
accept('application/json'),
parse('json')
)fetch('http://dataserver/data.json').then(response=> {
console.log(response.jsonData)
})
發(fā)送:
//usingES6 modules
import { createFetch, method, params } from 'http-client'const fetch=createFetch(
params({ name: 'Murdock' }),
base('http://dataserver/update')
)
優(yōu)點(diǎn):
· 在Node.js和瀏覽器中均可使用
· 由服務(wù)器端工作人員使用
· 基于Promise
· 提供頭部保護(hù)裝置,以提高CORS的安全性
缺點(diǎn):
· 必須從外部源加載
· 非原生
Fetch是原生瀏覽器API,用于發(fā)出替代XMLHttpRequest的請(qǐng)求。與XMLHttpRequest相比,F(xiàn)etch使網(wǎng)絡(luò)請(qǐng)求更容易。Fetch API使用Promises避免XMLHttpRequest回調(diào)地獄。
得到:
//WithES6 fetch
fetch('http://dataserver/data.json')
.then(data=> {
// ...do some stuff whith data
}).catch(error=> {
// Handle error
});
發(fā)送:
fetch('http://dataserver/update',{
method: 'post',
headers: {
'Accept': 'application/json,text/plain, */*',
'Content-Type': 'application/json'
},
body: JSON.stringify({name: 'Murdock'})
}).then(res=>res.json())
.then(res=> console.log(res));//ORwith ES2017 for example(async ()=> {
const response=awaitfetch('http://dataserver/update', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body:JSON.stringify({name='Murdock'})
});const result=awaitresponse.json();console.log(result);
})();
優(yōu)點(diǎn):
· 是原生瀏覽器API
· Fetch基本上是經(jīng)過(guò)完善的XMLHttpRequest
· 友好且易于學(xué)習(xí)
· 與大多數(shù)最近使用的瀏覽器兼容
· 是原生XMLHttpRequest對(duì)象的自然替代
· 學(xué)習(xí)曲線(xiàn)低
· 不需要從外部源加載它
· 使用promises避免回調(diào)地獄
· 不需要更多依賴(lài)項(xiàng)
缺點(diǎn):
· 處理JSON數(shù)據(jù)的過(guò)程分為兩步。第一個(gè)是發(fā)出請(qǐng)求,然后第二個(gè)是在響應(yīng)時(shí)調(diào)用.json()方法。對(duì)于Axios,默認(rèn)情況下會(huì)收到JSON響應(yīng)。
· 從Fetch()返回的Promise僅在網(wǎng)絡(luò)故障或任何阻止請(qǐng)求完成的情況發(fā)生時(shí)拒絕。即使響應(yīng)為HTTP 404或500,也不會(huì)拒絕HTTP錯(cuò)誤狀態(tài)。
· 缺乏其他庫(kù)的一些有用功能,例如:取消請(qǐng)求。
· 默認(rèn)情況下,F(xiàn)etch不會(huì)從服務(wù)器發(fā)送或接收Cookie,如果站點(diǎn)依賴(lài)于維持用戶(hù)會(huì)話(huà),則會(huì)導(dǎo)致未經(jīng)身份驗(yàn)證的請(qǐng)求。但是可以通過(guò)添加以下內(nèi)容來(lái)啟用:
{credentials: “same-origin.”}
圖源:unsplash
Fetch是一個(gè)新標(biāo)準(zhǔn),新版本的Chrome和Firefox無(wú)需使用任何其他庫(kù)就可支持它。
此外,Axios,SuperAgent或其他庫(kù)都有適合的文檔,易于使用,并且學(xué)習(xí)曲線(xiàn)不太高。在某些情況下,它們可以提供Fetch不具有的功能。
Fetch在JavaScript里是原生的,足以滿(mǎn)足項(xiàng)目需求。如果沒(méi)有特殊需求,我認(rèn)為Fetch就是最合適的選擇。
留言點(diǎn)贊關(guān)注
我們一起分享AI學(xué)習(xí)與發(fā)展的干貨
如轉(zhuǎn)載,請(qǐng)后臺(tái)留言,遵守轉(zhuǎn)載規(guī)范
*請(qǐng)認(rèn)真填寫(xiě)需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。