深入学习JS XML和Fetch请求

Xanthe ·
更新时间:2024-11-13
· 153 次阅读

目录

1.HTTP 简介

HTTP/0.9 ( 1991 )

HTTP/1.0 (1996 )

HTTP/1.1(1997)

常用状态码

header请求头

header响应头

Content-Type

1.application/x-www-form-urlencoded

2.multipart/form-data

3.application/json

https

HTTP2

HTTP3

2.Ajax

XHR

缺点

Fetch

优点

缺点

XML请求取消

Fetch请求取消

Axios请求取消

XML获取 progress

Fetch获取Progress

XML超时

Fetch超时

Fetch错误码

兼容XHR对象

3.同源策略和跨域请求

同源策略限制

跨域网络访问

不同源的窗口/文档交流

网络跨域解决方案

1.JSONP

2.CORS

简单请求

复杂请求

网络跨域解决方案-正向代理

网络跨域解决方案-反向代理

WebSocket

总结

1.HTTP 简介

HTTP ( HyperText Transfer Protocol)超文本传输协议,是万维网(World Wide Web) 的基础协议

HTTP/0.9 ( 1991 )

仅支持 GET 请求

不包含 HTTP 头,只能传输 HTML 文件

没有状态码或错误代码

HTTP/1.0 (1996 )

发送时添加协议版本信息

响应添加状态码,我们熟知的200404 等

引入了 HTTP 头,多了传递信息的手段,更加灵活和方便扩展

HTTP 头里面引入了重要的 content-type 属性,具备了传输除纯文本 HTML 文件以外其他类型文档的能力

HTTP/1.1(1997)

连接复用,长连接多个请求都可以复用一个tcp连接。

1.0每次请求都需要重新建立连接。

管道化技术多个连续的请求不用等待返回就可以发送下一个请求,这样就减少了耗费在网络延迟上的时间

响应分块单个请求返回部分内容,需前后端协商

新的缓存控制机制cache-controleTag就是1.1引入的,强缓存和协商缓存

新增 host 请求头,能够使不同域名配置在同一个 IP 地址的服务器上

HTTP1.x请求报文

HTTP1.x响应报文

常用状态码

header请求头 名字说明示例
Accept告知(服务器)客户端可以处理的内容类型text/html, application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Aaccept-Encoding客户端能够理解的内容编码方式gzip, deflate
Accept-Language客户端可以理解的语言zh-CN,zh;q=0.9,en;q=0.8
Cache-Control表示浏览器的缓存方式Cache-Control: max-age = xxx
cookiecookie信息 
Connection是否是长连接keep-live
Content-Type实际发送的数据类型content-type:application/x-www-form
Host要发送到的服务器主机名和端口号www.baidu.com
User-Agent用户代理。包含应用类型、操作系统、软件开发商以及版本号等 
Referer当前请求的来源页面的地址 
header响应头 名字说明示例
Date服务器的响应的日期和时间 
Connection是否会关闭网络连接Connection: keep-alive
Keep-Alive空闲连接需要保持打开状态Keep-Alive: timeout=5, max=10的最小时长和最大请求数( Connection设置为“keep-alive”才有意义)Keep-Alive: timeout=5, max=10空闲5秒,最多接收10次请求就断开。
Content-Encoding内容编码方式Content-Encoding: gzip
Content-Length报文中实体主体的字节大小content-Length: 1963
Content-Type内容的内容类型Content-Type: text/html; charset=utf-8
Server服务器所用到的软件相关信息Server: openresty
基于NGINX的可伸缩的Web平台
Set-Cookie向客户端发送cookieSet-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">发送请求</button> <div> <div>结果:</div> <div id="result"></div> </div> <script> btnSend.onclick = fetchByUrlencoded; function fetchByUrlencoded() { // 对中文还能自行编码 // 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("收到结果:", res); result.innerHTML = JSON.stringify(res); }); } </script> </body> 2.multipart/form-data <body> <div>结果:</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">发送请求</button> <script> btnSend.onclick = fetchByMultipart; function fetchByMultipart() { const formData = new FormData(); formData.append("name", "yunmu"); formData.append("age", 18); fetch("/multipart", { method: "POST", // 不要设置 content-type // headers: { // "Content-Type": "multipart/form-data", // }, body: formData, }) .then((res) => res.json()) .then((res) => { console.log("收到结果:", res); result.innerHTML = JSON.stringify(res); }); } </script> </body> 3.application/json <body> <button type="button" id="btnSend">发送请求</button> <div> <div>结果:</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("收到结果:", res); result.innerHTML = JSON.stringify(res); }); } </script> </body>

服务端代码:

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("收到请求(urlencoded)"); console.log("body:", req.body); res.json({ code: 10000, data: req.body, }); }); server.use("/multipart", multer().none(), function (req, res) { console.log("收到请求(multipart)"); console.log("body:", req.body); res.json({ code: 10000, data: req.body, }); }); server.use("/json", multer().none(), function (req, res) { console.log("收到请求(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):超文本传输安全协议,在HTTP的基础上加了一个 Secure 安全

HTTPSHTTP协议的一种扩展,使用传输层安全性(TLS)或安全套接字层(SSL)对通信协议进行加密

HTTP + SSL(TLS) = HTTPS

HTTP2

二进制帧

多路复用

头部压缩

服务器推送

HTTP3

基于UDP的传输层协议,那就是快啊

2.Ajax

全称: Asynchronous Javascript And XML(异步JavaScript和XML )

它并不是指单一的某种技术,而是多种现有技术的结合,实现“无页面刷新的数据获取”

这些技术包括了:HTML 或 XHTMLCSSJavaScriptDOMXMLXSLT,以及最重要的XMLHttpRequest

XHR

基本使用:

<body> <div>测试ajax 界面</div> <button id="ajaxBtn">AjAX局部刷新</button> <div class="ajax-change" id="responseDiv">change区域</div> <script> function test() { //1. 创建实例对象 const xhrObj = new XMLHttpRequest(); //注册readystatechange回调监听 xhrObj.onreadystatechange = function () { //readyState==4 && status=200 代表请求成功 if (xhrObj.readyState == 4 && xhrObj.status == 200) { //局部刷新文本 document.getElementById("responseDiv").innerHTML = xhrObj.responseText; } }; //请求错误回调 xhrObj.onerror = function () { console.log("-------onerror-------:"); }; //请求成功完成回调 xhrObj.onload = function () { console.log("-------onload-------:", xhrObj.responseText); }; //请求开始回调 xhrObj.onloadstart = function () { console.log("-------onloadstart-------"); }; //请求完成回调,不论请求成功与否 xhrObj.onloadend = function () { console.log("-------onloadend-------"); }; //设置请求地址,true 异步请求,false:同步请求, xhrObj.open("post", "http://127.0.0.1:3000/xhr", true); //设置请求携带header xhrObj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); //发送请求数据 xhrObj.send("xhr=1"); } document.getElementById("ajaxBtn").addEventListener("click", function () { test(); }); </script> </body>

服务端代码:

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); //设置跨域访问 app.use(function (req, res, next) { //设置允许跨域的域名,*代表允许任意域名跨域 //"*" 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", ]); //跨域允许的请求方式 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尝试请求快速结束 else next(); }); app.post("/xhr", async (_req, _res) => { console.log("xhr: 收到请求"); 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:收到请求", _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("收到请求=test4=", _req.url); // res.send('hello') await sleep(30000); return res.json({ REV: true, DATA: { msg: "成功", }, }); }); server.listen(port, () => { console.log("监听端口:", 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("自定义错误 Unhandled Rejection at:", p, "reason:", reason); // application specific logging, throwing an error, or other logic here }); 缺点

容易回调地狱

不符合关注分离请求和响应都在 XHR 对象上

Fetch

在原有 XHR 基础上改革,但是因为技术债的约束,不好更新

重新设计了一套fetch API

优点

Promise语法,解决了回调地狱问题

更合理的设计,分离Request , Response等通用对象

前端可拦截301302等跳转

支持数据流(Stream),方便处理大文件

语法简单

基本使用:

<script> // get fetch("http://127.0.0.1:3000/test1") .then((response) => response.text()) .then((text) => console.log("获取到的数据对象:", 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", //设置跨域 }) .then((response) => response.json()) .then((json) => console.log("post 获取到的数据对象:", 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); //设置请求携带header xhrObj.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); //发送请求数据 xhrObj.send("xhr=1"); xhrObj.onerror = function () { console.log("-------onerror-------:"); }; } </script> </body> 缺点

中断请求麻烦使用其他API实现(AbortControllerAbortSignal )

缺少直接获取请求传输进度的能力,例如XHRonProgress事件使用Response.body给我们返回了一个ReadableStream对象

不支持超时使用setTimeout自己封装

错误不会被拒绝(状态码 400-500),并不会触发Promisereject回调

兼容性

XML请求取消

XMLHttpRequest.abort()

Fetch请求取消

AbortController对象的abort()

<body> <div>测试fetch 界面</div> <button id="btnSend">发送请求</button> <button id="btnCancel">取消请求</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("取消请求"); controller.abort(); }; </script> </body> Axios请求取消 const controller = new AbortController(); const signal = controller.signal; axios.get("/foo", { signal, }).then(() => {}); // 取消请求 controller.abort(); XML获取 progress <body> <div>测试ajax 界面</div> <button id="ajaxBtn">AjAX局部刷新</button> <script> function test() { // 创建实例对象 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); //发送请求数据 xhrObj.send(); //请求成功完成后下载 // 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) => { // 通过响应头获取文件大小 contentLength = response.headers.get("Content-Length"); const reader = response.body.getReader(); return reader.read().then(function processResult(result) { if (result.done) { console.log("请求完毕"); 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超时 <body> <div>测试ajax 界面</div> <button id="ajaxBtn">发起超时请求</button> <div class="ajax-change" id="responseDiv">change区域</div> <script> function test() { //1. 创建实例对象 const xhrObj = new XMLHttpRequest(); //请求错误回调 xhrObj.onerror = function () { console.log("-------onerror-------:"); }; //请求完成回调,不论请求成功与否 xhrObj.onloadend = function () { console.log("-------onloadend-------"); }; //超时监听 xhrObj.ontimeout = function () { console.error("The request timed out."); document.getElementById("responseDiv").innerHTML = "The request timed out"; }; //设置网络超时时间 xhrObj.timeout = 5 * 1000; xhrObj.open("GET", "http://127.0.0.1:3000/timeout", true); //发送请求数据 xhrObj.send(); } document.getElementById("ajaxBtn").addEventListener("click", function () { test(); }); </script> </body> Fetch超时 <body> <div>fetch 不支持超时</div> <button id="ajaxBtn">发起超时请求</button> <div class="ajax-change" id="responseDiv">change区域</div> <script> const oldFetch = fetch; window.fetch = function (input, opts) { return new Promise(function (resolve, reject) { //开启超时 const timeoutId = setTimeout(function () { reject(new Error("fetch timeout")); }, opts.timeout); oldFetch(input, opts).then( (res) => { //清除超时 clearTimeout(timeoutId); resolve(res); }, (err) => { //清除超时 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("获取到的数据对象:", 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年以后 默认值从 {"credentials":"omit"} 修改为 {"credentials":"same-origin"} fetch("./a.png") .then((response) => response.text()) .then((text) => console.log("获取到的数据对象:", text)) .catch((err) => console.log("Request Failed", err)); } document.getElementById("ajaxBtn").addEventListener("click", function () { test(); }); </script> </body> Fetch错误码 <body> <button id="ajaxBtn">fetch 404错误码</button> <script> function test() { fetch("http://127.0.0.1:3000/test3", { credentials: "include", mode: "cors", }) .then((response) => { console.log("请求成功status:", response.status); return response.text(); }) .catch((err) => console.log("Request Failed", err)); } document.getElementById("ajaxBtn").addEventListener("click", function () { test(); }); </script> </body> 兼容XHR对象 function getXHR() { let xhr = null; if (window.XMLHttpRequest) { xhr = new XMLHttpRequest(); } else if (window.ActiveXObject) { //遍历IE中不同版本的ActiveX对象 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.同源策略和跨域请求

同源策略限制了不同源之间如何进行资源交互,是用于隔离潜在恶意文件的重要安全机制

同源:protocol + hostname + port

同源策略限制

存储:localStroage ,sessionStorageindexedDB受限,cookie以本域和父域为限制

dom获取受限

发送ajax受限

跨域网络访问

跨域写操作一般被允许,例如∶链接(a标签),重定向,表单提交

跨域资源嵌入一般被允许,如scriptlinkimgvideoobjectembediframe标签

不同源的窗口/文档交流

网络跨域解决方案 1.JSONP

<script> function jsonpCallback(data) { console.log("我收到的数据了:", 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: "服务端数据" }) + ")"; res.send(str); } else { res.send("Hello Yun"); } // 可拿到回调函数的名称 console.log(params.query.callback); });

JSONP缺点

只支持GET请求,不支持POST等其他类型HTTP请求

存在明显的安全问题(服务端返回什么都执行)

2.CORS

定义∶跨源资源共享(cross-origin sharing),是一种基于HTTP头的机制

该机制允许服务器除它自己以外的origin访问加载其资源

简单请求

如果携带身份凭证(cookie),服务器不得设置Access-Control-Allow-Origin为通配符*,应设置特定域服务器不能将 Access-Control-Allow-Headers 的值设为通配符“*”,而应将其设置为首部名称的列表,如:Access-Control-Allow-Headers: X-PINGOTHER, Content-Type服务器不能将 Access-Control-Allow-Methods 的值设为通配符“*”,而应将其设置为特定请求方法名称的列表,如:Access-Control-Allow-Methods: POST, GET let whitList = ["http://127.0.0.1:5500"]; //设置白名单 //设置跨域访问 app.use(function (req, res, next) { const origin = req.headers.origin as string; if (whitList.includes(origin)) { //设置允许跨域的域名,*代表允许任意域名跨域 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"]); //允许浏览器访问的响应头 res.header("Access-Control-Expose-Headers", "test"); //跨域允许的请求方式 res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS"); //预检结果保存时间 1小时 res.header("Access-Control-Max-Age", `${5}`); if (req.method.toLowerCase() == "options") { res.send(204); //让 options 尝试请求快速结束 return; } } next(); });

CORS中间件

let whitList = ["http://127.0.0.1:5500"]; //设置白名单 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)); 复杂请求

不满足简单请求自然就是复杂请求了

复杂请求会先发一个预检请求

需要预检的请求,必须使用OPTIONS方法发起一个预检请求到服务器,查看服务器是否允许发送实际请求

网络跨域解决方案-正向代理

cli工具( webpack配置devServer proxy

charles 、 fidler 等代理软件,本质就是拦截请求代理

网络跨域解决方案-反向代理

WebSocket

客户端和服务器之间存在持久的连接,而且双方都可以随时发送数据

服务端:

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) { // 发送欢迎信息给客户端 ws.send("服务器欢迎你链接"); ws.on("message", function incoming(message) { // 广播消息给所有客户端 server.clients.forEach(function each(client) { if (client.readyState === WebSocket.OPEN) { client.send("服务器收到客户端消息 -> " + 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="发送消息" 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 + "连接被关闭"; }; } 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> 总结

到此这篇关于深入学习JS XML和Fetch请求的文章就介绍到这了,更多相关JS  XML和Fetch内容请搜索软件开发网以前的文章或继续浏览下面的相关文章希望大家以后多多支持软件开发网!



学习js XML fetch js

需要 登录 后方可回复, 如果你还没有账号请 注册新账号