欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入學(xué)習(xí)JS?XML和Fetch請(qǐng)求

 更新時(shí)間:2022年09月20日 08:39:44   作者:云牧???????  
這篇文章主要介紹了深入學(xué)習(xí)JS?XML和Fetch請(qǐng)求,文章通過圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下

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連接。
  • 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
cookiecookie信息 
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ā)送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">發(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 安全
  • HTTPSHTTP協(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、JavaScriptDOM、XMLXSLT,以及最重要的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

image

優(yōu)點(diǎn)

  • Promise語法,解決了回調(diào)地獄問題
  • 更合理的設(shè)計(jì),分離Request , Response等通用對(duì)象
  • 前端可攔截301302等跳轉(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)(AbortControllerAbortSignal )
  • 缺少直接獲取請(qǐng)求傳輸進(jìn)度的能力,例如XHRonProgress事件
    • 使用Response.body給我們返回了一個(gè)ReadableStream對(duì)象
  • 不支持超時(shí)
    • 使用setTimeout自己封裝
  • 錯(cuò)誤不會(huì)被拒絕(狀態(tài)碼 400-500),并不會(huì)觸發(fā)Promisereject回調(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 ,sessionStorageindexedDB受限,cookie以本域和父域?yàn)橄拗?/li>
  • dom獲取受限
  • 發(fā)送ajax受限

跨域網(wǎng)絡(luò)訪問

  • 跨域?qū)懖僮饕话惚辉试S,例如∶鏈接(a標(biāo)簽),重定向,表單提交
  • 跨域資源嵌入一般被允許,如script、link、imgvideo、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)文章

最新評(píng)論