javascript
网络编程-JavaScript中发送网络请求汇总
文章目錄
- 1.前后端分離優勢
- 2.HTTP協議的解析
- 2.1 HTTP的介紹
- 2.2 HTTP的組成
- 2.3 HTTP的版本
- 2.4 HTTP請求方式
- 2.5 HTTP請求頭字段
- 2.6 HTTP響應狀態碼
- 2.7 HTTP響應頭
- 3.AJAX網絡請求
- 3.1 AJAX發送請求
- 3.2 XHR的狀態
- 3.3 XHR其他事件監聽
- 3.4 響應數據和響應類型
- 3.5 HTTP的響應狀態
- 3.7 GET/POST傳遞參數
- 4.AJAX網絡請求封裝
- 補充: 過期時間和取消請求
- 過期時間
- 取消請求
- 5.Fetch使用和上傳文件
- 5.1 Fetch基本使用
- 5.2 Fetch數據的響應
- 5.3 Fetch網絡請求的演練
- 5.4 Fetch POST請求
- 5.4 Fetch POST請求
1.前后端分離優勢
早期的網頁都是通過后端渲染來完成的:服務器端渲染(SSR,server side render):
- 客戶端發出請求 -> 服務端接收請求并返回相應HTML文檔 -> 頁面刷新,客戶端加載新的HTML文檔;
服務器端渲染的缺點:
- 當用戶點擊頁面中的某個按鈕向服務器發送請求時,頁面本質上只是一些數據發生了變化,而此時服務器卻要將重繪的整個頁面再返回給瀏覽器加載,這顯然有悖于程序員的“DRY( Don‘t repeat yourself )”原則;
- 而且明明只是一些數據的變化卻迫使服務器要返回整個HTML文檔,這本身也會給網絡帶寬帶來不必要的開銷。
有沒有辦法在頁面數據變動時,只向服務器請求新的數據,并且在阻止頁面刷新的情況下,動態的替換頁面中展示的數據呢?
- 答案正是“AJAX”。
AJAX是“Asynchronous JavaScript And XML”的縮寫(異步的JavaScript和XML),是一種實現無頁面刷新獲取服務器數據的技術。
- AJAX最吸引人的就是它的“異步”特性,也就是說它可以在不重新刷新頁面的情況下與服務器通信,交換數據,或更新頁面。
你可以使用AJAX最主要的兩個特性做下列事 :
- 在不重新加載頁面的情況下發送請求給服務器;
- 接受并使用從服務器發來的數據。
這里有兩幅圖給大家理解一下:
2.HTTP協議的解析
2.1 HTTP的介紹
什么是HTTP呢?我們來看一下維基百科的解釋:
- 超文本傳輸協議(英語:HyperText Transfer Protocol,縮寫:HTTP)是一種用于分布式、協作式和超媒體信息系統的應用層協議;
- HTTP是萬維網的數據通信的基礎,設計HTTP最初的目的是為了提供一種發布和接收HTML頁面的方法;
- 通過HTTP或者HTTPS協議請求的資源由統一資源標識符(Uniform Resource Identifiers,URI)來標識;
HTTP是一個客戶端(用戶)和服務端(網站)之間請求和響應的標準。
通過使用網頁瀏覽器、網絡爬蟲或者其它的工具,客戶端發起一個HTTP請求到服務器上指定端口(默認端口為80);
- 我們稱這個客戶端為用戶代理程序(user agent);
響應的服務器上存儲著一些資源,比如HTML文件和圖像。
- 我們也稱這個響應服務器為源服務器(origin server);
我們網頁中的資源通常是被放在Web資源服務器中,由瀏覽器自動發送HTTP請求來獲取、解析、展示的。
目前我們頁面中很多數據是動態展示的:
- 比如頁面中的數據展示、搜索數據、表單驗證等等,也是通過在JavaScript中發送HTTP請求獲取的;
2.2 HTTP的組成
一次HTTP請求主要包括:請求(Request)和響應(Response)(如下圖所示)
請求又包含請求行、請求頭、請求體 (如下圖所示)
響應也包含響應行、響應頭、響應體 (如下圖所示)
2.3 HTTP的版本
HTTP/0.9
- 發布于1991年;
- 只支持GET請求方法獲取文本數據,當時主要是為了獲取HTML頁面內容;
HTTP/1.0
- 發布于1996年;
- 支持POST、HEAD等請求方法,支持請求頭、響應頭等,支持更多種數據類型(不再局限于文本數據) ;
- 但是瀏覽器的每次請求都需要與服務器建立一個TCP連接,請求處理完成后立即斷開TCP連接,每次建立連接增加了性能損耗;
HTTP/1.1(目前使用最廣泛的版本)
- 發布于1997年;
- 增加了PUT、DELETE等請求方法;
- 采用持久連接(Connection: keep-alive),多個請求可以共用同一個TCP連接;
HTTP/2.0, 2015年
HTTP/3.0, 2018年
2.4 HTTP請求方式
在RFC中定義了一組請求方式,來表示要對給定資源執行的操作:
-
GET:GET 方法請求一個指定資源的表示形式,使用 GET 的請求應該只被用于獲取數據。
-
HEAD:HEAD 方法請求一個與 GET 請求的響應相同的響應,但沒有響應體。
比如在準備下載一個文件前,先獲取文件的大小,再決定是否進行下載;
-
POST:POST 方法用于將實體提交到指定的資源。
-
PUT:PUT 方法用請求有效載荷(payload)替換目標資源的所有當前表示;
-
DELETE:DELETE 方法刪除指定的資源;
-
PATCH:PATCH 方法用于對資源應部分修改;
-
CONNECT:CONNECT 方法建立一個到目標資源標識的服務器的隧道,通常用在代理服務器,網頁開發很少用到。
-
TRACE:TRACE 方法沿著到目標資源的路徑執行一個消息環回測試。
在開發中使用最多的是GET、POST請求;
- 在后續的后臺管理項目中,我們也會使用PATCH、DELETE請求;
2.5 HTTP請求頭字段
在request對象的header中也包含很多有用的信息,客戶端會默認傳遞過來一些信息(如下) :
content-type: 這次請求攜帶的數據的類型
- application/x-www-form-urlencoded:表示數據被編碼成以 ‘&’ 分隔的鍵 - 值對,同時以 ‘=’ 分隔鍵和值
- application/json:表示是一個json類型;
- text/plain:表示是文本類型;
- application/xml:表示是xml類型;
- multipart/form-data:表示是上傳文件;
content-length:文件的大小長度
keep-alive :
- http是基于TCP協議的,但是通常在進行一次請求和響應結束后會立刻中斷;
- 在http1.0中,如果想要繼續保持連接:
- 瀏覽器需要在請求頭中添加 connection: keep-alive;
- 服務器需要在響應頭中添加 connection:keey-alive;
- 當客戶端再次放請求時,就會使用同一個連接,直接一方中斷連接;
- 在http1.1中,所有連接默認是 connection: keep-alive的;
- 不同的Web服務器會有不同的保持 keep-alive的時間;
- Node中默認是5s中;
accept-encoding:告知服務器,客戶端支持的文件壓縮格式,比如js文件可以使用gzip編碼,對應 .gz文件;
accept:告知服務器,客戶端可接受文件的格式類型;
user-agent:客戶端相關的信息;
2.6 HTTP響應狀態碼
Http狀態碼(Http Status Code)是用來表示Http響應狀態的數字代碼:
- Http狀態碼非常多,可以根據不同的情況,給客戶端返回不同的狀態碼;
| 200 | OK | 客戶端請求成功 |
| 201 | Created | POST請求,創建新的資源 |
| 301 | Moved Permanently | 請求資源的URL已經修改,響應中會給出新的URL |
| 400 | Bad Request | 客戶端的錯誤,服務器無法或者不進行處理 |
| 401 | Unauthorized | 未授權的錯誤,必須攜帶請求的身份信息 |
| 403 | Forbidden | 客戶端沒有權限訪問,被拒接 |
| 404 | Not Found | 服務器找不到請求的資源。 |
| 500 | Internal Server Error | 服務器遇到了不知道如何處理的情況。 |
| 503 | Service Unavailable | 服務器不可用,可能處理維護或者重載狀態,暫時無法訪問 |
更多響應碼介紹在MDN文檔上, 鏈接給到大家: MDN上響應碼文檔: https://developer.mozilla.org/zh-CN/docs/web/http/status
2.7 HTTP響應頭
響應的header中包括一些服務器給客戶端的信息:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-L3n6aQgp-1657070357626)(img/Snipaste_2022-07-05_11-06-57.png)]
3.AJAX網絡請求
3.1 AJAX發送請求
AJAX 是異步的 JavaScript 和 XML(Asynchronous JavaScript And XML)
- 它可以使用 JSON,XML,HTML 和 text 文本等格式發送和接收數據;
如何來完成AJAX請求呢?
-
第一步:創建網絡請求的AJAX對象(使用XMLHttpRequest)
-
第二步:監聽XMLHttpRequest對象狀態的變化,或者監聽onload事件(請求完成時觸發);
-
第三步:配置網絡請求(通過open方法), open方法可以傳入兩個參數;
參數一: method(請求的方式: get, post …)
參數二: url(請求的地址)
-
第四步:發送send網絡請求;
【演示代碼】
// 1.創建網絡請求對象 const xhr = new XMLHttpRequest()// 2.監聽對象狀態的變化 xhr.addEventListener("readystatechange", function() {// 拿到網絡請求的結果console.log(xhr.response) })// 3.配置網絡請求 // 參數一: 請求的方式; 參數二: 要請求的url地址 xhr.open("get", "http://192.168.0.110:1888")// 4.發生網絡請求 xhr.send()發送同步請求:
-
我們發送網絡請求, 默認是異步的, 但是我們也可以發送同步的網絡請求
-
我們是需要將open的第三個參數設置為false (默認時true), 就可以開啟同步的請求
-
當然我們實際開發還是用異步的請求
3.2 XHR的狀態
事實上,我們在一次網絡請求中看到狀態發生了很多次變化,這是因為對于一次請求來說包括如下的狀態:
| 0 | UNSENT | 代理被創建,但尚未調用 open() 方法。 |
| 1 | OPENED | open() 方法已經被調用。 |
| 2 | HEADERS_RECEIVED | send() 方法已經被調用,并且頭部和狀態已經可獲得。 |
| 3 | LOADING | 下載中;responseText 屬性已經包含部分數據。 |
| 4 | DONE | 下載操作已完成。 |
狀態0我們是監聽不到的
例如: 我們想要獲取結果, 應該在下載操作已完成后獲取
// 1.創建網絡請求對象 const xhr = new XMLHttpRequest()// 2.監聽對象狀態的變化 xhr.addEventListener("readystatechange", function() {// 狀態不為4的話直接returnif (xhr.readyState !== XMLHttpRequest.DONE) return// 拿到的結果是一個字符串, 我們可以轉成js對象const resJSON = JSON.parse(xhr.response)console.log(resJSON) })// 3.配置網絡請求 // 參數一: 請求的方式; 參數二: 要請求的url地址 xhr.open("get", "http://192.168.0.110:1888")// 4.發生網絡請求 xhr.send()注意<:這個狀態并非是HTTP的響應狀態,而是記錄的XMLHttpRequest對象的狀態變化。
- http響應狀態通過status獲取;
3.3 XHR其他事件監聽
我們除了可以監聽readystatechange之外, 還有其他的事件可以監聽
- loadstart:請求開始。
- progress: 一個響應數據包到達,此時整個 response body 都在 response 中。
- abort:調用 xhr.abort() 取消了請求。
- error:發生連接錯誤,例如,域錯誤。不會發生諸如 404 這類的 HTTP 錯誤。
- load:請求成功完成。
- timeout:由于請求超時而取消了該請求(僅發生在設置了 timeout 的情況下)。
- loadend:在 load,error,timeout 或 abort 之后觸發。
我們也可以在load中獲取請求數據:
// 在load中獲取請求結果 xhr.addEventListener("load", function() {console.log(xhr.response) })3.4 響應數據和響應類型
發送了請求后,我們需要獲取對應的結果:response屬性
- XMLHttpRequest response 屬性返回響應的正文內容;
- 返回的類型取決于responseType的屬性設置
通過responseType可以設置獲取數據的類型
- 如果將 responseType 的值設置為空字符串,則會使用 text 作為默認值。
- 設置數據類型, 一般在監聽事件之后, 且在send方法之前
和responseText、responseXML的區別:
- 早期通常服務器返回的數據是普通的文本和XML,所以我們通常會通過responseText、 responseXML來獲取響應結果 , 之后將它們轉化成JavaScript對象形式;
- 目前服務器基本返回的都是json數據,直接設置為json即可;
3.5 HTTP的響應狀態
前面我們提到, XMLHttpRequest的state是用于記錄xhr對象本身的狀態變化,并非針對于HTTP的網絡請求狀態。
如果我們希望獲取HTTP響應的網絡狀態,可以通過status和statusText來獲取:
- status是獲取狀態碼
- statusText是獲取狀態描述
我們寫一個不存在的接口, 測試一下狀態碼和狀態描述
const xhr = new XMLHttpRequest()xhr.addEventListener("load", function() {console.log(xhr.response) // null// 1.獲取狀態碼console.log(xhr.status) // 404// 2.獲取狀態描述console.log(xhr.statusText) // Not Found })xhr.responseType = "json"xhr.open("get", "http://123.207.32.32:8000/aaa/bbb/ccc")xhr.send()大家可以再看看剛剛的常用狀態碼表格
| 200 | OK | 客戶端請求成功 |
| 201 | Created | POST請求,創建新的資源 |
| 301 | Moved Permanently | 請求資源的URL已經修改,響應中會給出新的URL |
| 400 | Bad Request | 客戶端的錯誤,服務器無法或者不進行處理 |
| 401 | Unauthorized | 未授權的錯誤,必須攜帶請求的身份信息 |
| 403 | Forbidden | 客戶端沒有權限訪問,被拒接 |
| 404 | Not Found | 服務器找不到請求的資源。 |
| 500 | Internal Server Error | 服務器遇到了不知道如何處理的情況。 |
| 503 | Service Unavailable | 服務器不可用,可能處理維護或者重載狀態,暫時無法訪問 |
3.7 GET/POST傳遞參數
在開發中,我們使用最多的是GET和POST請求,在發送請求的過程中,我們也可以傳遞給服務器數據。
常見的傳遞給服務器數據的方式有如下幾種 :
-
方式一:GET請求的query參數(常用)
const xhr = new XMLHttpRequest()xhr.addEventListener("load", function() {// 傳入的參數服務器會返回console.log(xhr.response) })xhr.responseType = "json"// 在傳入的url輸入查詢字符串傳遞參數 xhr.open("get", "http://123.207.32.32:1888/02_param/get?name=kaisa&age=18&address=成都市" )xhr.send()請求結果
-
方式二:POST請求 x-www-form-urlencoded 格式
const xhr = new XMLHttpRequest()xhr.addEventListener("load", function() {console.log(xhr.response) })xhr.responseType = "json"// 1.發送post請求 xhr.open("post", "http://123.207.32.32:1888/02_param/posturl")// 2.告知服務器要發送數據的格式 xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded") // 3.在請求體中傳入參數 xhr.send("name=kaisa&age=18&address=成都市")請求結果
-
方式三:POST請求 FormData 格式
-
方式四:POST請求 JSON 格式(常用)
const xhr = new XMLHttpRequest()xhr.addEventListener("load", function() {console.log(xhr.response) })xhr.responseType = "json"// 1.發送post請求 xhr.open("post", "http://123.207.32.32:1888/02_param/posturl")// 2.告知服務器要發送數據的格式 xhr.setRequestHeader("Content-type", "application/json")// 3.發送JSON格式的字符串 xhr.send(JSON.stringify({ name: "kaisa", age: 18, height: 1.88 }))請求結果
4.AJAX網絡請求封裝
在實際開發中, 我們并不會自己封裝AJAX, 而是直接使用axios庫, 再對axios庫進行二次封裝
我們封裝AJAX原因是借此練習一下前面所學的知識
由于我們每次使用網絡請求, 都寫寫很多行同樣的代碼, 使用起來是非常不方便的, 我們封裝的目的就是為了使用起來更方便
封裝步驟的解釋寫在代碼注釋里面, 源代碼和測試代碼給大家
// 定義一個函數封裝ajax, // 由于使用參數可能會傳很多個, 因此我們可以讓使用者傳入一個對象 function myajax({url,method = "get",date = {},success,failure } = {}) {// 1.創建對象const xhr = new XMLHttpRequest()// 2.監聽數據xhr.onload = function() {// 用響應碼判斷是否成功if (xhr.status >= 200 && xhr.status < 300) {success && success(xhr.response)} else {failure && failure({ status: xhr.status, message: xhr.statusText })}}// 3.設置響應類型xhr.responseType = "json"// 考慮get請求放在date對象中的情況單獨處理if (method.toUpperCase() === "GET") {const urlStrings = []for (key in date) {urlStrings.push(`${key}=${date[key]}`)}url = url + "?" + urlStrings.join("&")xhr.open(method, url)xhr.send()} else {// 4.open方法xhr.open(method, url)// 5.send方法xhr.setRequestHeader("Content-type", "application/json")xhr.send(JSON.stringify(date))} }// 測試get請求 myajax({url: "http://123.207.32.32:1888/02_param/get",date: {name: "get",age: 18},// 傳入一個請求成功的回調success: function(res) {console.log(res)},// 傳入失敗的回調failure: function(err) {console.log("err", err)} })// 測試post請求 myajax({url: "http://123.207.32.32:1888/02_param/posturl",method: "post",// 傳入一個請求成功的回調success: function(res) {console.log(res)},date: {name: "post",age: 18},// 傳入失敗的回調failure: function(err) {console.log("err", err)} })我們對上面的基本封裝做一點優化, 為了防止回調地獄的情況出現, 我們可以返回一個promise, 并且不需要再傳入成功的回調和失敗的回調, 因為promise中有
function myajax({url,method = "get",date = {} } = {}) {// 返回一個promisereturn new Promise((resolve, reject) => {const xhr = new XMLHttpRequest() xhr.onload = function() {if (xhr.status >= 200 && xhr.status < 300) {// 使用resolve成功的回調resolve(xhr.response)} else {// 使用reject失敗的回調reject({ status: xhr.status, message: xhr.statusText })}} xhr.responseType = "json" if (method.toUpperCase() === "GET") {const urlStrings = []for (key in date) {urlStrings.push(`${key}=${date[key]}`)}url = url + "?" + urlStrings.join("&") xhr.open(method, url)xhr.send()} else {xhr.open(method, url) xhr.setRequestHeader("Content-type", "application/json")xhr.send(JSON.stringify(date))}}) }// 測試get請求 myajax({url: "http://123.207.32.32:1888/02_param/get",date: {name: "get",age: 18} // then中成功的結果 }).then(res => {console.log(res) // catch中失敗的結果 }).catch(err => {console.log(err) })// 測試post請求 myajax({url: "http://123.207.32.32:1888/02_param/posturl",method: "post",date: {name: "post",age: 18} // then中成功的結果 }).then(res => {console.log(res) // catch中失敗的結果 }).catch(err => {console.log(err) })補充: 過期時間和取消請求
下面我用的是一個延時的接口
過期時間
在網絡請求的過程中,為了避免過長的時間服務器無法返回數據,通常我們會為請求設置一個超時時間:timeout。
- 當達到超時時間后依然沒有獲取到數據,那么這個請求會自動被取消掉;
- 默認值為0,表示沒有設置超時時間;
下面寫個案例, 如果超過3000毫秒沒有請求到數據就取消本次網絡請求
- 請求超時可以在timeout中監聽
取消請求
取消網絡請求, 我們也可以通過abort方法強制 (手動) 取消請求;
- 請求取消可以在abort中監聽
5.Fetch使用和上傳文件
5.1 Fetch基本使用
Fetch可以看做是早期的XMLHttpRequest的替代方案,它提供了一種更加現代的處理方案:
比如返回值是一個Promise,提供了一種更加優雅的處理結果方式
- 在請求發送成功時,調用resolve回調then;
- 在請求發送失敗時,調用reject回調catch;
比如不像XMLHttpRequest一樣,所有的操作都在一個對象上
fetch函數的使用:
fetch(input[, init])
input:定義要獲取的資源地址,可以是一個URL字符串,也可以使用一個Request對象(實驗性特性)類型;
init:其他初始化參數, 是一個對象
- method: 請求使用的方法,如 GET、POST;
- headers: 請求的頭信息;
- body: 請求的 body 信息;
【演示代碼】
發送一個get請求(先了解一下 馬上會詳細分析)
fetch("http://123.207.32.32:8000/home/multidata").then(res => {// 獲取具體的結果還需調用一次// 如果是文本就res.text, 其他的同樣的道理res.json().then(res => {console.log(res)}) }).catch(err => {console.log(err) })5.2 Fetch數據的響應
Fetch的數據響應主要分為兩個階段 :
階段一:當服務器返回了響應(response)
- fetch 返回的 promise 就使用內建的 Response class 對象來對響應頭進行解析;
- 在這個階段,我們可以通過檢查響應頭,來檢查 HTTP 狀態以確定請求是否成功;
- 如果 fetch 無法建立一個 HTTP 請求,例如網絡問題,亦或是請求的網址不存在,那么 promise 就會 reject;
- 異常的 HTTP 狀態,例如 404 或 500,不會導致出現 error;
我們可以在 response 的屬性中看到 HTTP 狀態:
- status:HTTP 狀態碼,例如 200;
- ok:布爾值,如果 HTTP 狀態碼為 200-299,則為 true;
第二階段,為了獲取 response body,我們需要使用一個其他的方法調用, 這個方法同樣返回Promise。
- response.text() —— 讀取 response,并以文本形式返回 response;
- response.json() —— 將 response 解析為 JSON;
5.3 Fetch網絡請求的演練
基于Promise的使用方案:
fetch("http://123.207.32.32:8000/home/multidata").then(res => {// 獲取具體的結果還需調用一次// 如果是文本就res.text, 其他的同樣的道理return res.json() }).then(res => {console.log(res) }).catch(err => {console.log(err) })基于async、await的使用方案:
async function getDate() {const response1 = await fetch("http://123.207.32.32:8000/home/multidata")const response2 = await response1.json()// 打印結果console.log(response2) }getDate()5.4 Fetch POST請求
創建一個 POST 請求,或者其他方法的請求,我們需要使用 fetch 選項:
method:HTTP 方法,例如 POST,
body:request body,其中之一:
- 字符串(例如 JSON 編碼的),
- FormData 對象,以 multipart/form-data 形式發送數據,
5.4 Fetch POST請求
創建一個 POST 請求,或者其他方法的請求,我們需要使用 fetch 選項:
method:HTTP 方法,例如 POST,
body:request body,其中之一:
- 字符串(例如 JSON 編碼的),
- FormData 對象,以 multipart/form-data 形式發送數據,
總結
以上是生活随笔為你收集整理的网络编程-JavaScript中发送网络请求汇总的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 若依集成actuator实现优雅关闭应用
- 下一篇: 让你认清楚JSP中的所有东西(java/