深入學(xué)習(xí)JS?XML和Fetch請(qǐng)求
1.HTTP 簡(jiǎn)介
HTTP ( HyperText Transfer Protocol)
超文本傳輸協(xié)議,是萬維網(wǎng)(World Wide Web)
的基礎(chǔ)協(xié)議
HTTP/0.9 ( 1991 )
- 僅支持
GET
請(qǐng)求 - 不包含
HTTP
頭,只能傳輸 HTML 文件 - 沒有狀態(tài)碼或錯(cuò)誤代碼
HTTP/1.0 (1996 )
- 發(fā)送時(shí)添加協(xié)議版本信息
- 響應(yīng)添加狀態(tài)碼,我們熟知的
200
、404
等 - 引入了
HTTP
頭,多了傳遞信息的手段,更加靈活和方便擴(kuò)展 - HTTP 頭里面引入了重要的
content-type
屬性,具備了傳輸除純文本HTML
文件以外其他類型文檔的能力
HTTP/1.1(1997)
- 連接復(fù)用,長(zhǎng)連接
- 多個(gè)請(qǐng)求都可以復(fù)用一個(gè)
tcp
連接。
- 多個(gè)請(qǐng)求都可以復(fù)用一個(gè)
- 而
1.0
每次請(qǐng)求都需要重新建立連接。
- 管道化技術(shù)
- 多個(gè)連續(xù)的請(qǐng)求不用等待返回就可以發(fā)送下一個(gè)請(qǐng)求,這樣就減少了耗費(fèi)在網(wǎng)絡(luò)延遲上的時(shí)間
- 響應(yīng)分塊
- 單個(gè)請(qǐng)求返回部分內(nèi)容,需前后端協(xié)商
- 新的緩存控制機(jī)制
cache-control
、eTag
就是1.1引入的,強(qiáng)緩存和協(xié)商緩存
- 新增
host
請(qǐng)求頭,能夠使不同域名配置在同一個(gè)IP
地址的服務(wù)器上
HTTP1.x請(qǐng)求報(bào)文
HTTP1.x響應(yīng)報(bào)文
常用狀態(tài)碼
header請(qǐng)求頭
名字 | 說明 | 示例 |
---|---|---|
Accept | 告知(服務(wù)器)客戶端可以處理的內(nèi)容類型 | text/html, application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 |
Aaccept-Encoding | 客戶端能夠理解的內(nèi)容編碼方式 | gzip, deflate |
Accept-Language | 客戶端可以理解的語言 | zh-CN,zh;q=0.9,en;q=0.8 |
Cache-Control | 表示瀏覽器的緩存方式 | Cache-Control: max-age = xxx |
cookie | cookie信息 | |
Connection | 是否是長(zhǎng)連接 | keep-live |
Content-Type | 實(shí)際發(fā)送的數(shù)據(jù)類型 | content-type:application/x-www-form |
Host | 要發(fā)送到的服務(wù)器主機(jī)名和端口號(hào) | www.baidu.com |
User-Agent | 用戶代理。包含應(yīng)用類型、操作系統(tǒng)、軟件開發(fā)商以及版本號(hào)等 | |
Referer | 當(dāng)前請(qǐng)求的來源頁面的地址 |
header響應(yīng)頭
名字 | 說明 | 示例 |
---|---|---|
Date | 服務(wù)器的響應(yīng)的日期和時(shí)間 | |
Connection | 是否會(huì)關(guān)閉網(wǎng)絡(luò)連接 | Connection: keep-alive |
Keep-Alive | 空閑連接需要保持打開狀態(tài)Keep-Alive: timeout=5, max=10的最小時(shí)長(zhǎng)和最大請(qǐng)求數(shù)( Connection設(shè)置為“keep-alive”才有意義) | Keep-Alive: timeout=5, max=10空閑5秒,最多接收10次請(qǐng)求就斷開。 |
Content-Encoding | 內(nèi)容編碼方式 | Content-Encoding: gzip |
Content-Length | 報(bào)文中實(shí)體主體的字節(jié)大小 | content-Length: 1963 |
Content-Type | 內(nèi)容的內(nèi)容類型 | Content-Type: text/html; charset=utf-8 |
Server | 服務(wù)器所用到的軟件相關(guān)信息 | Server: openresty 基于NGINX的可伸縮的Web平臺(tái) |
Set-Cookie | 向客戶端發(fā)送cookie | Set-Cookie: imooc_isnew=2; expires=Thu, 02-Mar-202312:3242 GMT; Max-Age=31536000; path=/; domain=.baidu.com |
Content-Type
1.application/x-www-form-urlencoded
<body> <button type="button" id="btnSend">發(fā)送請(qǐng)求</button> <div> <div>結(jié)果:</div> <div id="result"></div> </div> <script> btnSend.onclick = fetchByUrlencoded; function fetchByUrlencoded() { // 對(duì)中文還能自行編碼 // const params = new URLSearchParams({ // name: 'yunmu', // age: 18 // }); fetch("/urlencoded", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded", }, body: "name=yunmu&age=18", // body: params.toString() }) .then((res) => res.json()) .then((res) => { console.log("收到結(jié)果:", res); result.innerHTML = JSON.stringify(res); }); } </script> </body>
2.multipart/form-data
<body> <div>結(jié)果:</div> <div id="result"></div> <div>表單提交</div> <form action="/multipart" method="post" enctype="multipart/form-data"> <input type="text" name="name" value="tom" /> <input type="text" name="age" value="18" /> <button type="submit">提交表單</button> </form> <hr /> <div>代碼提交:</div> <button type="button" id="btnSend">發(fā)送請(qǐng)求</button> <script> btnSend.onclick = fetchByMultipart; function fetchByMultipart() { const formData = new FormData(); formData.append("name", "yunmu"); formData.append("age", 18); fetch("/multipart", { method: "POST", // 不要設(shè)置 content-type // headers: { // "Content-Type": "multipart/form-data", // }, body: formData, }) .then((res) => res.json()) .then((res) => { console.log("收到結(jié)果:", res); result.innerHTML = JSON.stringify(res); }); } </script> </body>
3.application/json
<body> <button type="button" id="btnSend">發(fā)送請(qǐng)求</button> <div> <div>結(jié)果:</div> <div id="result"></div> </div> <script> btnSend.onclick = fetchByJSON; function fetchByJSON() { fetch("/json", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ name: "yunmu", age: 18 }), }) .then((res) => { console.log("返回的content-type:", res.headers.get("Content-Type")); return res; }) .then((res) => res.json()) .then((res) => { console.log("收到結(jié)果:", res); result.innerHTML = JSON.stringify(res); }); } </script> </body>
服務(wù)端代碼:
const express = require("express"); const path = require("path"); const multer = require("multer"); const server = express(); server.use( express.urlencoded({ extended: true, }) ); server.use(express.json()); server.use(express.static(path.join(__dirname, "./static"))); server.use("/urlencoded", function (req, res) { console.log("收到請(qǐng)求(urlencoded)"); console.log("body:", req.body); res.json({ code: 10000, data: req.body, }); }); server.use("/multipart", multer().none(), function (req, res) { console.log("收到請(qǐng)求(multipart)"); console.log("body:", req.body); res.json({ code: 10000, data: req.body, }); }); server.use("/json", multer().none(), function (req, res) { console.log("收到請(qǐng)求(json)"); console.log("body:", req.body); res.json({ code: 10000, data: req.body, }); }); server.listen(3000, function () { console.log("listening at port 3000"); });
https
HTTPS (Hypertext Transfer Protocol Secure)
:超文本傳輸安全協(xié)議,在HTTP的基礎(chǔ)上加了一個(gè)Secure
安全HTTPS
是HTTP
協(xié)議的一種擴(kuò)展,使用傳輸層安全性(TLS)
或安全套接字層(SSL)
對(duì)通信協(xié)議進(jìn)行加密HTTP + SSL(TLS) = HTTPS
HTTP2
- 二進(jìn)制幀
- 多路復(fù)用
- 頭部壓縮
- 服務(wù)器推送
HTTP3
- 基于UDP的傳輸層協(xié)議,那就是快啊
2.Ajax
- 全稱:
Asynchronous Javascript And XML
(異步JavaScript和XML ) - 它并不是指單一的某種技術(shù),而是多種現(xiàn)有技術(shù)的結(jié)合,實(shí)現(xiàn)“無頁面刷新的數(shù)據(jù)獲取”
- 這些技術(shù)包括了:
HTML
或XHTML
、CSS
、JavaScript
、DOM
、XML
、XSLT
,以及最重要的XMLHttpRequest
XHR
基本使用:
<body> <div>測(cè)試ajax 界面</div> <button id="ajaxBtn">AjAX局部刷新</button> <div class="ajax-change" id="responseDiv">change區(qū)域</div> <script> function test() { //1. 創(chuàng)建實(shí)例對(duì)象 const xhrObj = new XMLHttpRequest(); //注冊(cè)readystatechange回調(diào)監(jiān)聽 xhrObj.onreadystatechange = function () { //readyState==4 && status=200 代表請(qǐng)求成功 if (xhrObj.readyState == 4 && xhrObj.status == 200) { //局部刷新文本 document.getElementById("responseDiv").innerHTML = xhrObj.responseText; } }; //請(qǐng)求錯(cuò)誤回調(diào) xhrObj.onerror = function () { console.log("-------onerror-------:"); }; //請(qǐng)求成功完成回調(diào) xhrObj.onload = function () { console.log("-------onload-------:", xhrObj.responseText); }; //請(qǐng)求開始回調(diào) xhrObj.onloadstart = function () { console.log("-------onloadstart-------"); }; //請(qǐng)求完成回調(diào),不論請(qǐng)求成功與否 xhrObj.onloadend = function () { console.log("-------onloadend-------"); }; //設(shè)置請(qǐng)求地址,true 異步請(qǐng)求,false:同步請(qǐng)求, xhrObj.open("post", "http://127.0.0.1:3000/xhr", true); //設(shè)置請(qǐng)求攜帶header xhrObj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); //發(fā)送請(qǐng)求數(shù)據(jù) xhrObj.send("xhr=1"); } document.getElementById("ajaxBtn").addEventListener("click", function () { test(); }); </script> </body>
服務(wù)端代碼:
import http from "http"; import bodyParser from "body-parser"; import express from "express"; import createError from "http-errors"; // const multiparty = require('multiparty'); const port = 3000; const app = express(); app.use(bodyParser.urlencoded({ extended: true })); const server = http.createServer(app); //設(shè)置跨域訪問 app.use(function (req, res, next) { //設(shè)置允許跨域的域名,*代表允許任意域名跨域 //"*" res.header("Access-Control-Allow-Origin", req.headers.origin); //允許攜帶cookie res.header("Access-Control-Allow-Credentials", "true"); //允許的header類型 res.header("Access-Control-Allow-Headers", [ "X-PINGOTHER", "content-type", "Origin", "X-Requested-With", ]); //跨域允許的請(qǐng)求方式 res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS"); res.header("Access-Control-Max-Age", `${20}`); if (req.method.toLowerCase() == "options") res.send(200); //讓options嘗試請(qǐng)求快速結(jié)束 else next(); }); app.post("/xhr", async (_req, _res) => { console.log("xhr: 收到請(qǐng)求"); await sleep(2 * 1000); _res.json({ code: 10000, }); }); function sleep(time: number) { return new Promise((resolve) => setTimeout(resolve, time)); } app.get("/fetch", async (_req, res) => { console.log("fetch:收到請(qǐng)求", _req.url); await sleep(10 * 1000); return res.json({ code: 10000, }); }); app.get("/test1", (_req, res) => { res.send("test1"); }); app.get("/test2", (_req, res) => { res.send("test2"); }); app.get("/timeout", async (_req, res) => { await sleep(12 * 1000); res.send("test2"); }); app.get("/test4", async (_req, res) => { console.log("收到請(qǐng)求=test4=", _req.url); // res.send('hello') await sleep(30000); return res.json({ REV: true, DATA: { msg: "成功", }, }); }); server.listen(port, () => { console.log("監(jiān)聽端口:", port); }); // catch 404 and forward to error handler app.use((_req: express.Request, _res: express.Response, next: express.NextFunction) => { const error = createError(404); next(error); }); process.on("unhandledRejection", (reason: {} | null | undefined, p: Promise<any>) => { console.error("自定義錯(cuò)誤 Unhandled Rejection at:", p, "reason:", reason); // application specific logging, throwing an error, or other logic here });
缺點(diǎn)
- 容易回調(diào)地獄
- 不符合關(guān)注分離
- 請(qǐng)求和響應(yīng)都在 XHR 對(duì)象上
Fetch
- 在原有 XHR 基礎(chǔ)上改革,但是因?yàn)榧夹g(shù)債的約束,不好更新
- 重新設(shè)計(jì)了一套fetch API
優(yōu)點(diǎn)
Promise
語法,解決了回調(diào)地獄問題- 更合理的設(shè)計(jì),分離
Request
,Response
等通用對(duì)象 - 前端可攔截
301
,302
等跳轉(zhuǎn) - 支持?jǐn)?shù)據(jù)流
(Stream)
,方便處理大文件 - 語法簡(jiǎn)單
基本使用:
<script> // get fetch("http://127.0.0.1:3000/test1") .then((response) => response.text()) .then((text) => console.log("獲取到的數(shù)據(jù)對(duì)象:", text)) .catch((err) => console.log("Request Failed", err)); //post fetch("http://127.0.0.1:3000/report", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded;", }, body: "report=2", mode: "cors", //設(shè)置跨域 }) .then((response) => response.json()) .then((json) => console.log("post 獲取到的數(shù)據(jù)對(duì)象:", json)) .catch((err) => console.log("Request Failed", err)); </script>
攔截3xx重定向
<body> <div> <button id="btnXhr">XHR</button> <button id="btnFetch">Fetch</button> </div> <script> btnXhr.onclick = xhr30x; btnFetch.onclick = fetch30x; function fetch30x() { fetch("http://www.baidu.com", { redirect: "error" }).catch((err) => console.log("err:", err) ); } function xhr30x() { const xhrObj = new XMLHttpRequest(); xhrObj.onreadystatechange = function () { console.log("xhrObj.status==", xhrObj.status); }; xhrObj.open("get", "http://www.baidu.com", true); //設(shè)置請(qǐng)求攜帶header xhrObj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); //發(fā)送請(qǐng)求數(shù)據(jù) xhrObj.send("xhr=1"); xhrObj.onerror = function () { console.log("-------onerror-------:"); }; } </script> </body>
缺點(diǎn)
- 中斷請(qǐng)求麻煩
- 使用其他
API
實(shí)現(xiàn)(AbortController
和AbortSignal
)
- 使用其他
- 缺少直接獲取請(qǐng)求傳輸進(jìn)度的能力,例如
XHR
的onProgress
事件- 使用
Response.body
給我們返回了一個(gè)ReadableStream
對(duì)象
- 使用
- 不支持超時(shí)
- 使用setTimeout自己封裝
- 錯(cuò)誤不會(huì)被拒絕(狀態(tài)碼 400-500),并不會(huì)觸發(fā)
Promise
的reject
回調(diào) - 兼容性
XML請(qǐng)求取消
- XMLHttpRequest.abort()
Fetch請(qǐng)求取消
AbortController
對(duì)象的abort()
<body> <div>測(cè)試fetch 界面</div> <button id="btnSend">發(fā)送請(qǐng)求</button> <button id="btnCancel">取消請(qǐng)求</button> <script> const controller = new AbortController(); const signal = controller.signal; btnSend.onclick = function sendFetch(test) { fetch("http://127.0.0.1:3000/fetch", { signal }) .then((response) => { return response.text(); }) .then((text) => { console.log(text); }); }; btnCancel.onclick = function () { console.log("取消請(qǐng)求"); controller.abort(); }; </script> </body>
Axios請(qǐng)求取消
const controller = new AbortController(); const signal = controller.signal; axios.get("/foo", { signal, }).then(() => {}); // 取消請(qǐng)求 controller.abort();
XML獲取 progress
<body> <div>測(cè)試ajax 界面</div> <button id="ajaxBtn">AjAX局部刷新</button> <script> function test() { // 創(chuàng)建實(shí)例對(duì)象 const xhrObj = new XMLHttpRequest(); xhrObj.responseType = "blob"; //onprogress xhrObj.onprogress = function (event) { console.log( "total:", event.total, "progress:", event.loaded, "%:", (event.loaded / event.total) * 100 + "%" ); if (event.lengthComputable) { console.log("獲取完畢"); } }; xhrObj.open("get", "./test.mp4", true); //發(fā)送請(qǐng)求數(shù)據(jù) xhrObj.send(); //請(qǐng)求成功完成后下載 // xhrObj.onload = function (oEvent) { // console.log(oEvent, "oEvent==="); // console.log(xhrObj.status, "status==="); // console.log(xhrObj.response, "response==="); // if (xhrObj.readyState === 4 && xhrObj.status === 200) { // const blob = new Blob([xhrObj.response]); // const video = URL.createObjectURL(blob); // const link = document.createElement("a"); // link.href = video; // link.download = "test.mp4"; // link.click(); // } // }; } document.getElementById("ajaxBtn").addEventListener("click", function () { test(); }); </script> </body>
Fetch獲取Progress
<body> <script> let progress = 0; let contentLength = 0; fetch("./test.mp4") .then((response) => { // 通過響應(yīng)頭獲取文件大小 contentLength = response.headers.get("Content-Length"); const reader = response.body.getReader(); return reader.read().then(function processResult(result) { if (result.done) { console.log("請(qǐng)求完畢"); return; } progress += result.value.byteLength; console.log( "total:", contentLength, "progress:", progress, "%:", (progress / contentLength) * 100 + "%" ); return reader.read().then(processResult); }); }) .catch((err) => console.log("Request Failed", err)); </script> </body>
XML超時(shí)
<body> <div>測(cè)試ajax 界面</div> <button id="ajaxBtn">發(fā)起超時(shí)請(qǐng)求</button> <div class="ajax-change" id="responseDiv">change區(qū)域</div> <script> function test() { //1. 創(chuàng)建實(shí)例對(duì)象 const xhrObj = new XMLHttpRequest(); //請(qǐng)求錯(cuò)誤回調(diào) xhrObj.onerror = function () { console.log("-------onerror-------:"); }; //請(qǐng)求完成回調(diào),不論請(qǐng)求成功與否 xhrObj.onloadend = function () { console.log("-------onloadend-------"); }; //超時(shí)監(jiān)聽 xhrObj.ontimeout = function () { console.error("The request timed out."); document.getElementById("responseDiv").innerHTML = "The request timed out"; }; //設(shè)置網(wǎng)絡(luò)超時(shí)時(shí)間 xhrObj.timeout = 5 * 1000; xhrObj.open("GET", "http://127.0.0.1:3000/timeout", true); //發(fā)送請(qǐng)求數(shù)據(jù) xhrObj.send(); } document.getElementById("ajaxBtn").addEventListener("click", function () { test(); }); </script> </body>
Fetch超時(shí)
<body> <div>fetch 不支持超時(shí)</div> <button id="ajaxBtn">發(fā)起超時(shí)請(qǐng)求</button> <div class="ajax-change" id="responseDiv">change區(qū)域</div> <script> const oldFetch = fetch; window.fetch = function (input, opts) { return new Promise(function (resolve, reject) { //開啟超時(shí) const timeoutId = setTimeout(function () { reject(new Error("fetch timeout")); }, opts.timeout); oldFetch(input, opts).then( (res) => { //清除超時(shí) clearTimeout(timeoutId); resolve(res); }, (err) => { //清除超時(shí) clearTimeout(timeoutId); reject(err); } ); }); }; function test() { // get fetch("http://127.0.0.1:3000/timeout", { timeout: 5 * 1000 }) .then((response) => response.text()) .then((text) => console.log("獲取到的數(shù)據(jù)對(duì)象:", text)) .catch((err) => console.error("Request Failed", err)); } document.getElementById("ajaxBtn").addEventListener("click", function () { test(); }); </script> </body>
Fetch同源攜帶Cookie
<body> <button id="ajaxBtn">xhr 攜帶cookie</button> <script> function test() { //2018年以后 默認(rèn)值從 {"credentials":"omit"} 修改為 {"credentials":"same-origin"} fetch("./a.png") .then((response) => response.text()) .then((text) => console.log("獲取到的數(shù)據(jù)對(duì)象:", text)) .catch((err) => console.log("Request Failed", err)); } document.getElementById("ajaxBtn").addEventListener("click", function () { test(); }); </script> </body>
Fetch錯(cuò)誤碼
<body> <button id="ajaxBtn">fetch 404錯(cuò)誤碼</button> <script> function test() { fetch("http://127.0.0.1:3000/test3", { credentials: "include", mode: "cors", }) .then((response) => { console.log("請(qǐng)求成功status:", response.status); return response.text(); }) .catch((err) => console.log("Request Failed", err)); } document.getElementById("ajaxBtn").addEventListener("click", function () { test(); }); </script> </body>
兼容XHR對(duì)象
function getXHR() { let xhr = null; if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else if (window.ActiveXObject) { //遍歷IE中不同版本的ActiveX對(duì)象 let version = ["Msxml2", "Microsoft"]; for (let i = 0; i < version.length; i++) { try { xhr = new window.ActiveXObject(version[i] + ".XMLHTTP"); return; } catch (e) { console.log(e); } } } return xhr; }
3.同源策略和跨域請(qǐng)求
- 同源策略限制了不同源之間如何進(jìn)行資源交互,是用于隔離潛在惡意文件的重要安全機(jī)制
- 同源:
protocol + hostname + port
同源策略限制
- 存儲(chǔ):
localStroage
,sessionStorage
和indexedDB
受限,cookie
以本域和父域?yàn)橄拗?/li> dom
獲取受限- 發(fā)送
ajax
受限
跨域網(wǎng)絡(luò)訪問
- 跨域?qū)懖僮饕话惚辉试S,例如∶鏈接(a標(biāo)簽),重定向,表單提交
- 跨域資源嵌入一般被允許,如
script
、link
、img
、video
、object
、embed
、iframe
標(biāo)簽
不同源的窗口/文檔交流
網(wǎng)絡(luò)跨域解決方案
1.JSONP
<script> function jsonpCallback(data) { console.log("我收到的數(shù)據(jù)了:", data); } </script> <script src="http://127.0.0.1:3000/jsonp_request?callback=jsonpCallback"></script>
app.get("/jsonp_request", (_req, res) => { const params = urlLib.parse(_req.url, true); if (params.query && params.query.callback) { const str = params.query.callback + "(" + JSON.stringify({ test: "服務(wù)端數(shù)據(jù)" }) + ")"; res.send(str); } else { res.send("Hello Yun"); } // 可拿到回調(diào)函數(shù)的名稱 console.log(params.query.callback); });
JSONP缺點(diǎn)
- 只支持
GET
請(qǐng)求,不支持POST
等其他類型HTTP
請(qǐng)求 - 存在明顯的安全問題(服務(wù)端返回什么都執(zhí)行)
2.CORS
- 定義∶跨源資源共享
(cross-origin sharing)
,是一種基于HTTP
頭的機(jī)制 - 該機(jī)制允許服務(wù)器除它自己以外的
origin
訪問加載其資源
簡(jiǎn)單請(qǐng)求
如果攜帶身份憑證
(cookie)
,服務(wù)器不得設(shè)置Access-Control-Allow-Origin
為通配符*
,應(yīng)設(shè)置特定域服務(wù)器不能將
Access-Control-Allow-Headers
的值設(shè)為通配符“*
”,而應(yīng)將其設(shè)置為首部名稱的列表,如:Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
服務(wù)器不能將
Access-Control-Allow-Methods
的值設(shè)為通配符“*
”,而應(yīng)將其設(shè)置為特定請(qǐng)求方法名稱的列表,如:Access-Control-Allow-Methods: POST, GET
let whitList = ["http://127.0.0.1:5500"]; //設(shè)置白名單 //設(shè)置跨域訪問 app.use(function (req, res, next) { const origin = req.headers.origin as string; if (whitList.includes(origin)) { //設(shè)置允許跨域的域名,*代表允許任意域名跨域 res.header("Access-Control-Allow-Origin", origin); //允許攜帶憑證 res.header("Access-Control-Allow-Credentials", "true"); //允許的header類型 res.header("Access-Control-Allow-Headers", ["X-PINGOTHER", "content-type", "Origin", "Accept"]); //允許瀏覽器訪問的響應(yīng)頭 res.header("Access-Control-Expose-Headers", "test"); //跨域允許的請(qǐng)求方式 res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS"); //預(yù)檢結(jié)果保存時(shí)間 1小時(shí) res.header("Access-Control-Max-Age", `${5}`); if (req.method.toLowerCase() == "options") { res.send(204); //讓 options 嘗試請(qǐng)求快速結(jié)束 return; } } next(); });
CORS中間件
let whitList = ["http://127.0.0.1:5500"]; //設(shè)置白名單 const corsOptions = { origin: function (origin, callback) { if (whitList.indexOf(origin) !== -1) { callback(null, true); } else { callback(new Error("Not allowed by CORS")); } }, credentials: true, maxAge: 20, allowedHeaders: ["X-PINGOTHER", "content-type", "Origin", "Accept"], }; app.use(cors(corsOptions));
復(fù)雜請(qǐng)求
- 不滿足簡(jiǎn)單請(qǐng)求自然就是復(fù)雜請(qǐng)求了
- 復(fù)雜請(qǐng)求會(huì)先發(fā)一個(gè)預(yù)檢請(qǐng)求
- 需要預(yù)檢的請(qǐng)求,必須使用
OPTIONS
方法發(fā)起一個(gè)預(yù)檢請(qǐng)求到服務(wù)器,查看服務(wù)器是否允許發(fā)送實(shí)際請(qǐng)求
網(wǎng)絡(luò)跨域解決方案-正向代理
- cli工具(
webpack
配置devServer proxy
) charles
、fidler
等代理軟件,本質(zhì)就是攔截請(qǐng)求代理
網(wǎng)絡(luò)跨域解決方案-反向代理
WebSocket
- 客戶端和服務(wù)器之間存在持久的連接,而且雙方都可以隨時(shí)發(fā)送數(shù)據(jù)
服務(wù)端:
const WebSocket = require("ws"); const server = new WebSocket.Server({ port: 18000 }); server.on("open", function open() { console.log("connected"); }); server.on("close", function close() { console.log("disconnected"); }); server.on("connection", function connection(ws, req) { // 發(fā)送歡迎信息給客戶端 ws.send("服務(wù)器歡迎你鏈接"); ws.on("message", function incoming(message) { // 廣播消息給所有客戶端 server.clients.forEach(function each(client) { if (client.readyState === WebSocket.OPEN) { client.send("服務(wù)器收到客戶端消息 -> " + message); } }); }); });
客戶端:
<style> .txt { font-size: 30px; } .inputBtn { font-size: 40px; } </style> <body> <form onsubmit="return false;"> <h1>慕課聊天室:</h1> <textarea id="repText" class="txt" style="width: 800px; height: 600px"></textarea> <br /> <input class="inputBtn" type="text" id="myInput" name="message" style="width: 300px" value="Hello world" /> <input class="inputBtn" type="button" id="mySend" value="發(fā)送消息" onclick="send(this.form.message.value)" /> </form> <script type="text/javascript"> let socket; const repTextEl = document.getElementById("repText"); if (window.WebSocket) { socket = new WebSocket("ws://127.0.0.1:18000"); socket.onmessage = function (event) { repTextEl.value = repTextEl.value + "\n" + event.data; }; socket.onopen = function (event) { repTextEl.value = "webSocket已鏈接"; }; socket.onclose = function (event) { repTextEl.value = repTextEl.value + "連接被關(guān)閉"; }; } else { console.log("瀏覽器不支持webSocket"); } function send(message) { if (!window.WebSocket) { return; } if (socket.readyState == WebSocket.OPEN) { socket.send(message); } else { console.log("webSocket還沒有開啟"); } } </script> </body>
總結(jié)
到此這篇關(guān)于深入學(xué)習(xí)JS XML和Fetch請(qǐng)求的文章就介紹到這了,更多相關(guān)JS XML和Fetch內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript實(shí)現(xiàn)隨機(jī)點(diǎn)名器實(shí)例詳解
這篇文章主要介紹了JavaScript隨機(jī)點(diǎn)名器,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05js判斷兩個(gè)數(shù)組是否存在相同元素的四種方法
這篇文章主要給大家介紹了關(guān)于js判斷兩個(gè)數(shù)組是否存在相同元素的四種方法,js中是不能直接用==或者===來計(jì)算兩個(gè)數(shù)組是否相等的,那么就需要對(duì)數(shù)組的值進(jìn)行比較,需要的朋友可以參考下2023-07-07基于javascript實(shí)現(xiàn)瀏覽器滾動(dòng)條快到底部時(shí)自動(dòng)加載數(shù)據(jù)
這篇文章主要介紹了基于javascript實(shí)現(xiàn)瀏覽器滾動(dòng)條快到底部時(shí)自動(dòng)加載數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2015-11-11打印Proxy對(duì)象和ref對(duì)象的包實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了打印Proxy對(duì)象和ref對(duì)象的包實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11ES6中常見基本知識(shí)點(diǎn)的基本使用實(shí)例匯總
這篇文章主要給大家介紹了關(guān)于ES6中常見基本知識(shí)點(diǎn)的基本使用的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-04-04淺談javascript的call()、apply()、bind()的用法
這篇文章主要為大家詳細(xì)介紹了javascript的call()、apply()、bind()的用法,探討JavaScript中函數(shù)的一些特殊用法,感興趣的小伙伴們可以參考一下2016-02-02javascript中的緩動(dòng)效果實(shí)現(xiàn)程序
javascript中的緩動(dòng)效果可以應(yīng)用于很多地方,比如距離位移上的變化:圖片的滾動(dòng)、焦點(diǎn)圖的輪轉(zhuǎn)切換,透明度上的變化:漸隱漸現(xiàn)。凡是存在運(yùn)動(dòng)的狀態(tài)都適用,下面以最基本的塊在容器內(nèi)從左到右滑動(dòng)為例,講下幾種不同的緩動(dòng)處理方式2012-12-12基于zepto.js實(shí)現(xiàn)仿手機(jī)QQ空間的大圖查看組件ImageView.js詳解
這篇文章主要介紹了基于zepto.js實(shí)現(xiàn)仿手機(jī)QQ空間的大圖查看組件ImageView.js的源碼和使用方法,并附上一個(gè)使用ImageView.js的實(shí)例,這里分享給大家,有需要的小伙伴參考下。2015-03-03