精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Axios 是如何封裝 HTTP 請求的

開發 前端
本文用來整理項目中常用的 Axios 的封裝使用。同時學習源碼,手寫實現 Axios 的核心代碼。

Axios 毋庸多說大家在前端開發中常用的一個發送 HTTP 請求的庫,用過的都知道。本文用來整理項目中常用的 Axios 的封裝使用。同時學習源碼,手寫實現 Axios 的核心代碼。

Axios 常用封裝

是什么

Axios 是一個基于 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中。它的特性:

  •  從瀏覽器中創建 XMLHttpRequests
  •  從 node.js 創建 http 請求
  •  支持 Promise API
  •  攔截請求和響應
  •  轉換請求數據和響應數據
  •  取消請求
  •  自動轉換 JSON 數據
  •  客戶端支持防御 XSRF官網地址:http://www.axios-js.com/zh-cn/docs/#axios-config

Axios 使用方式有兩種:一種是直接使用全局的 Axios 對象;另外一種是通過 axios.create(config) 方法創建一個實例對象,使用該對象。兩種方式的區別是通過第二種方式創建的實例對象更清爽一些;全局的 Axios 對象其實也是創建的實例對象導出的,它本身上加載了很多默認屬性。后面源碼學習的時候會再詳細說明。

請求

Axios 這個 HTTP 的庫比較靈活,給用戶多種發送請求的方式,以至于有些混亂。細心整理會發現,全局的 Axios(或者 axios.create(config)創建的對象) 既可以當作對象使用,也可以當作函數使用: 

  1. // axios 當作對象使用  
  2. axios.request(config)  
  3. axios.get(url[, config])  
  4. axios.post(url[, data[, config]])  
  1. // axios() 當作函數使用。 發送 POST 請求  
  2. axios({  
  3.   method: 'post',  
  4.   url: '/user/12345',  
  5.   data: {  
  6.     firstName: 'Fred',  
  7.     lastName: 'Flintstone'  
  8.   }  
  9. }); 

后面源碼學習的時候會再詳細說明為什么 Axios 可以實現兩種方式的使用。

取消請求

可以使用 CancelToken.source 工廠方法創建 cancel token: 

  1. const CancelToken = axios.CancelToken;  
  2. const source = CancelToken.source();  
  3. axios.get('/user/12345', {  
  4.   cancelToken: source.token  
  5. }).catch(function(thrown) {  
  6.   if (axios.isCancel(thrown)) {  
  7.     console.log('Request canceled', thrown.message);  
  8.   } else { 
  9.       // 處理錯誤  
  10.   }  
  11. });  
  12. // 取消請求(message 參數是可選的)  
  13. source.cancel('Operation canceled by the user.'); 

source 有兩個屬性:一個是 source.token 標識請求;另一個是 source.cancel() 方法,該方法調用后,可以讓 CancelToken 實例的 promise 狀態變為 resolved,從而觸發 xhr 對象的 abort() 方法,取消請求。

攔截

Axios 還有一個奇妙的功能點,可以在發送請求前對請求進行攔截,對相應結果進行攔截。結合業務場景的話,在中臺系統中完成登錄后,獲取到后端返回的 token,可以將 token 添加到 header 中,以后所有的請求自然都會加上這個自定義 header。 

  1. //攔截1 請求攔截  
  2. instance.interceptors.request.use(function(config){  
  3.     //在發送請求之前做些什么  
  4.     const token = sessionStorage.getItem('token');  
  5.     if(token){  
  6.         const newConfig = {  
  7.             ...config,  
  8.             headers: {  
  9.                 token: token  
  10.             }  
  11.         }  
  12.         return newConfig;  
  13.     }else{  
  14.         return config;  
  15.     }  
  16. }, function(error){  
  17.     //對請求錯誤做些什么  
  18.     return Promise.reject(error);  
  19. }); 

我們還可以利用請求攔截功能實現 取消重復請求,也就是在前一個請求還沒有返回之前,用戶重新發送了請求,需要先取消前一次請求,再發送新的請求。比如搜索框自動查詢,當用戶修改了內容重新發送請求的時候需要取消前一次請求,避免請求和響應混亂。再比如表單提交按鈕,用戶多次點擊提交按鈕,那么我們就需要取消掉之前的請求,保證只有一次請求的發送和響應。

實現原理是使用一個對象記錄已經發出去的請求,在請求攔截函數中先判斷這個對象中是否記錄了本次請求信息,如果已經存在,則取消之前的請求,將本次請求添加進去對象中;如果沒有記錄過本次請求,則將本次請求信息添加進對象中。最后請求完成后,在響應攔截函數中執行刪除本次請求信息的邏輯。 

  1. // 攔截2   重復請求,取消前一個請求  
  2. const promiseArr = {};  
  3. instance.interceptors.request.use(function(config){  
  4.     console.log(Object.keys(promiseArr).length)  
  5.     //在發送請求之前做些什么  
  6.     let source=null 
  7.     if(config.cancelToken){  
  8.         // config 配置中帶了 source 信息  
  9.         source = config.source;  
  10.     }else{  
  11.         const CancelToken = axios.CancelToken;  
  12.         source = CancelToken.source();  
  13.         config.cancelToken = source.token;  
  14.     }  
  15.     const currentKey = getRequestSymbol(config);  
  16.     if(promiseArr[currentKey]){  
  17.         const tmp = promiseArr[currentKey];  
  18.         tmp.cancel("取消前一個請求");  
  19.         delete promiseArr[currentKey];  
  20.         promiseArr[currentKey] = source;  
  21.     }else{  
  22.         promiseArr[currentKey] = source;  
  23.     }  
  24.     return config;  
  25. }, function(error){  
  26.     //對請求錯誤做些什么  
  27.     return Promise.reject(error);  
  28. });  
  29. // 根據 url、method、params 生成唯一標識,大家可以自定義自己的生成規則  
  30. function getRequestSymbol(config){  
  31.     const arr = [];  
  32.     if(config.params){  
  33.         const data = config.params;  
  34.         for(let key of Object.keys(data)){  
  35.             arr.push(key+"&"+data[key]);  
  36.         }  
  37.         arr.sort();  
  38.     }  
  39.     return config.url+config.method+arr.join("");  
  40.  
  41. instance.interceptors.response.use(function(response){  
  42.     const currentKey = getRequestSymbol(response.config);  
  43.     delete promiseArr[currentKey];  
  44.     return response;  
  45. }, function(error){  
  46.     //對請求錯誤做些什么  
  47.     return Promise.reject(error);  
  48. }); 

最后,我們可以在響應攔截函數中統一處理返回碼的邏輯: 

  1. // 響應攔截  
  2. instance.interceptors.response.use(function(response){  
  3.     // 401 沒有登錄跳轉到登錄頁面  
  4.     if(response.data.code===401){  
  5.         window.location.href = "http://127.0.0.1:8080/#/login" 
  6.     }else if(response.data.code===403){  
  7.         // 403 無權限跳轉到無權限頁面  
  8.         window.location.href = "http://127.0.0.1:8080/#/noAuth" 
  9.     }  
  10.     return response;  
  11. }, function(error){  
  12.     //對請求錯誤做些什么  
  13.     return Promise.reject(error);  
  14. }) 

文件下載

通常文件下載有兩種方式:一種是通過文件在服務器上的對外地址直接下載;還有一種是通過接口將文件以二進制流的形式下載。

第一種:同域名 下使用 a 標簽下載: 

  1. // httpServer.js  
  2. const express = require("express");  
  3. const path = require('path');  
  4. const app = express();  
  5. //靜態文件地址  
  6. app.use(express.static(path.join(__dirname, 'public')))  
  7. app.use(express.static(path.join(__dirname, '../')));  
  8. app.listen(8081, () => {  
  9.   console.log("服務器啟動成功!")  
  10. });  
  1. // index.html  
  2. <a href="test.txt" download="test.txt">下載</a> 

第二種:二進制文件流的形式傳遞,我們直接訪問該接口并不能下載文件,一定程度保證了數據的安全性。比較多的場景是:后端接收到查詢參數,查詢數據庫然后通過插件動態生成 excel 文件,以文件流的方式讓前端下載。

這時候,我們可以將請求文件下載的邏輯進行封裝。將二進制文件流存在 Blob 對象中,再將其轉為 url 對象,最后通過 a 標簽下載。 

  1. //封裝下載  
  2. export function downLoadFetch(url, params = {}, config={}) {  
  3.     //取消  
  4.     const downSource = axios.CancelToken.source();  
  5.     document.getElementById('downAnimate').style.display = 'block' 
  6.     document.getElementById('cancelBtn').addEventListener('click', function(){  
  7.         downSource.cancel("用戶取消下載");  
  8.         document.getElementById('downAnimate').style.display = 'none' 
  9.     }, false);  
  10.     //參數  
  11.     config.params = params;  
  12.     //超時時間  
  13.     configconfig.timeout = config.timeout ? config.timeout : defaultDownConfig.timeout;  
  14.     //類型  
  15.     config.responseType = defaultDownConfig.responseType;  
  16.     //取消下載  
  17.     config.cancelToken = downSource.token;  
  18.     return instance.get(url, config).then(response=> 
  19.         const content = response.data;  
  20.         const url = window.URL.createObjectURL(new Blob([content]));  
  21.         //創建 a 標簽  
  22.         const link = document.createElement('a');  
  23.         link.style.display = 'none' 
  24.         link.href = url 
  25.         //文件名  Content-Disposition: attachment; filename=download.txt  
  26.         const filename = response.headers['content-disposition'].split(";")[1].split("=")[1];  
  27.         link.download = filename 
  28.         document.body.appendChild(link);  
  29.         link.click();  
  30.         document.body.removeChild(link);  
  31.         return {  
  32.             status: 200,  
  33.             success: true  
  34.         }  
  35.     })  

手寫 Axios 核心代碼

寫了這么多用法終于到正題了,手寫 Axios 核心代碼。Axios 這個庫源碼不難閱讀,沒有特別復雜的邏輯,大家可以放心閱讀 😂 。

源碼入口是這樣查找:在項目 node_modules 目錄下,找到 axios 模塊的 package.json 文件,其中 "main": "index.js", 就是文件入口。一步步我們可以看到源碼是怎么串起來的。

模仿上面的目錄結構,我們創建自己的目錄結構: 

  1. axios-js  
  2. │  index.html  
  3. │    
  4. └─lib  
  5.         adapter.js  
  6.         Axios.js  
  7.         axiosInstance.js  
  8.         CancelToken.js  
  9.         InterceptorManager.js 

Axios 是什么

上面有提到我們使用的全局 Axios 對象其實也是構造出來的 axios,既可以當對象使用調用 get、post 等方法,也可以直接當作函數使用。這是因為全局的 Axios 其實是函數對象 instance 。源碼位置在 axios/lib/axios.js 中。具體代碼如下: 

  1. // axios/lib/axios.js  
  2. //創建 axios 實例  
  3. function createInstance(defaultConfig) {  
  4.   var context = new Axios(defaultConfig);  
  5.   //instance 對象是 bind 返回的函數  
  6.   var instance = bind(Axios.prototype.request, context);  
  7.   // Copy axios.prototype to instance  
  8.   utils.extend(instance, Axios.prototype, context);  
  9.   // Copy context to instance  
  10.   utils.extend(instance, context);  
  11.   return instance;  
  12.  
  13. // 實例一個 axios  
  14. var axios = createInstance(defaults);  
  15. // 向這個實例添加 Axios 屬性  
  16. axios.Axios = Axios;  
  17. // 向這個實例添加 create 方法  
  18. axios.create = function create(instanceConfig) {  
  19.   return createInstance(mergeConfig(axios.defaults, instanceConfig));  
  20. };  
  21. // 向這個實例添加 CancelToken 方法  
  22. axios.CancelToken = require('./cancel/CancelToken'); 
  23. // 導出實例 axios  
  24. module.exports.default = axios

根據上面的源碼,我們可以簡寫一下自己實現 Axios.js 和 axiosInstance.js: 

  1. // Axios.js  
  2. //Axios 主體  
  3. function Axios(config){ 
  4.  
  5. // 核心方法,發送請求  
  6. Axios.prototype.request = function(config){  
  7.  
  8. Axios.prototype.get = function(url, config={}){  
  9.     return this.request({url: url, method: 'GET', ...config});  
  10.  
  11. Axios.prototype.post = function(url, data, config={}){  
  12.     return this.request({url: url, method: 'POST', data: data, ...config})  
  13.  
  14. export default Axios; 

在 axiosInstance.js 文件中,實例化一個 Axios 得到 context,再將原型對象上的方法綁定到 instance 對象上,同時將 context 的屬性添加到 instance 上。這樣 instance 就成為了一個函數對象。既可以當作對象使用,也可以當作函數使用。 

  1. // axiosInstance.js  
  2. //創建實例  
  3. function createInstance(config){  
  4.     const context = new Axios(config);  
  5.     var instance = Axios.prototype.request.bind(context);  
  6.     //將 Axios.prototype 屬性擴展到 instance 上  
  7.     for(let k of Object.keys(Axios.prototype)){  
  8.         instance[k] = Axios.prototype[k].bind(context);  
  9.     }  
  10.     //將 context 屬性擴展到 instance 上  
  11.     for(let k of Object.keys(context)){  
  12.         instance[k] = context[k]  
  13.     }  
  14.     return instance;  
  15.  
  16. const axios = createInstance({});  
  17. axios.create = function(config){  
  18.     return createInstance(config);  
  19.  export default axios; 

也就是說 axios.js 中導出的 axios 對象并不是 new Axios() 方法返回的對象 context,而是 Axios.prototype.request.bind(context) 執行返回的 instance,通過遍歷 Axios.prototype 并改變其 this 指向到 context;遍歷 context 對象讓 instance 對象具有 context 的所有屬性。這樣 instance 對象就無敵了,😎 既擁有了 Axios.prototype 上的所有方法,又具有了 context 的所有屬性。

請求實現

我們知道 Axios 在瀏覽器中會創建 XMLHttpRequest 對象,在 node.js 環境中創建 http 發送請求。Axios.prototype.request() 是發送請求的核心方法,這個方法其實調用的是 dispatchRequest 方法,而 dispatchRequest 方法調用的是 config.adapter || defaults.adapter 也就是自定義的 adapter 或者默認的 defaults.adapter,默認defaults.adapter 調用的是 getDefaultAdapter 方法,源碼: 

  1. function getDefaultAdapter() {  
  2.   var adapter;  
  3.   if (typeof XMLHttpRequest !== 'undefined') {  
  4.     // For browsers use XHR adapter  
  5.     adapter = require('./adapters/xhr');  
  6.   } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {  
  7.     // For node use HTTP adapter  
  8.     adapter = require('./adapters/http');  
  9.   }  
  10.   return adapter;  

哈哈哈,getDefaultAdapter 方法最終根據當前的環境返回不同的實現方法,這里用到了 適配器模式。我們只用實現 xhr 發送請求即可: 

  1. //適配器 adapter.js  
  2. function getDefaultAdapter(){  
  3.     var adapter;  
  4.     if(typeof XMLHttpRequest !== 'undefined'){  
  5.         //導入 XHR 對象請求  
  6.         adapter = (config)=> 
  7.             return xhrAdapter(config);  
  8.         }  
  9.     }  
  10.     return adapter;  
  11.  
  12. function xhrAdapter(config){  
  13.     return new Promise((resolve, reject)=> 
  14.         var xhr = new XMLHttpRequest();  
  15.         xhr.open(config.method, config.url, true);  
  16.         xhr.send();  
  17.         xhr.onreadystatechange = ()=> 
  18.             if(xhr.readyState===4){  
  19.                 if(xhr.status>=200&&xhr.status<300){  
  20.                     resolve({  
  21.                         data: {},  
  22.                         status: xhr.status,  
  23.                         statusText: xhr.statusText,  
  24.                         xhr: xhr 
  25.                     })  
  26.                 }else{  
  27.                     reject({ 
  28.                          status: xhr.status  
  29.                     })  
  30.                 }  
  31.             }  
  32.         };  
  33.     })  
  34.  export default getDefaultAdapter; 

這樣就理順了,getDefaultAdapter 方法每次執行會返回一個 Promise 對象,這樣 Axios.prototype.request 方法可以得到執行 xhr 發送請求的 Promise 對象。

給我們的 Axios.js 添加發送請求的方法: 

  1. //Axios.js  
  2. import getDefaultAdapter from './adapter.js';  
  3. Axios.prototype.request = function(config){  
  4.     const adapter = getDefaultAdapter(config);  
  5.     var promise = Promise.resolve(config);  
  6.     var chain = [adapter, undefined];  
  7.     while(chain.length){  
  8.         promisepromise = promise.then(chain.shift(), chain.shift());  
  9.     }  
  10.     return promise;  

攔截器實現

攔截器的原理在于 Axios.prototype.request 方法中的 chain 數組,把請求攔截函數添加到 chain 數組前面,把響應攔截函數添加到數組后面。這樣就可以實現發送前攔截和響應后攔截的效果。

創建 InterceptorManager.js 

  1. //InterceptorManager.js   
  2. //攔截器  
  3. function InterceptorManager(){  
  4.     this.handlers = [];  
  5.  
  6. InterceptorManager.prototype.use = function(fulfilled, rejected){  
  7.     this.handlers.push({  
  8.         fulfilled: fulfilled,  
  9.         rejected: rejected  
  10.     });  
  11.     return this.handlers.length -1;  
  12.  
  13. export default InterceptorManager; 

在 Axios.js 文件中,構造函數有 interceptors屬性: 

  1. //Axios.js  
  2. function Axios(config){  
  3.     this.interceptors = {  
  4.         request: new InterceptorManager(),  
  5.         response: new InterceptorManager()  
  6.     }  

這樣我們在 Axios.prototype.request 方法中對攔截器添加處理: 

  1. //Axios.js  
  2. Axios.prototype.request = function(config){  
  3.     const adapter = getDefaultAdapter(config);  
  4.     var promise = Promise.resolve(config);  
  5.     var chain = [adapter, undefined];  
  6.     //請求攔截  
  7.     this.interceptors.request.handlers.forEach(item=> 
  8.         chain.unshift(item.rejected);  
  9.         chain.unshift(item.fulfilled);      
  10.      });  
  11.     //響應攔截  
  12.     this.interceptors.response.handlers.forEach(item=> 
  13.         chain.push(item.fulfilled);  
  14.         chain.push(item.rejected)  
  15.     });  
  16.     console.dir(chain);  
  17.     while(chain.length){  
  18.         promisepromise = promise.then(chain.shift(), chain.shift());  
  19.     }  
  20.     return promise;  

所以攔截器的執行順序是:請求攔截2 -> 請求攔截1 -> 發送請求 -> 響應攔截1 -> 響應攔截2

取消請求

來到 Axios 最精彩的部分了,取消請求。我們知道 xhr 的 xhr.abort(); 函數可以取消請求。那么什么時候執行這個取消請求的操作呢?得有一個信號告訴 xhr 對象什么時候執行取消操作。取消請求就是未來某個時候要做的事情,你能想到什么呢?對,就是 Promise。Promise 的 then 方法只有 Promise 對象的狀態變為 resolved 的時候才會執行。我們可以利用這個特點,在 Promise 對象的 then 方法中執行取消請求的操作。看代碼: 

  1. //CancelToken.js  
  2. // 取消請求  
  3. function CancelToken(executor){  
  4.     if(typeof executor !== 'function'){  
  5.         throw new TypeError('executor must be a function.')  
  6.     }  
  7.     var resolvePromise;  
  8.     this.promise = new Promise((resolve)=> 
  9.         resolveresolvePromise = resolve;  
  10.     });  
  11.     executor(resolvePromise)  
  12.  
  13. CancelToken.source = function(){  
  14.     var cancel;  
  15.     var token = new CancelToken((c)=> 
  16.         ccancel = c;  
  17.     })  
  18.     return { 
  19.          token,  
  20.         cancel  
  21.     };  
  22.  
  23. export default CancelToken; 

當我們執行 const source = CancelToken.source()的時候,source 對象有兩個字段,一個是 token 對象,另一個是 cancel 函數。在 xhr 請求中: 

  1. //適配器  
  2. // adapter.js  
  3. function xhrAdapter(config){  
  4.     return new Promise((resolve, reject)=> 
  5.         ...  
  6.         //取消請求  
  7.         if(config.cancelToken){  
  8.             // 只有 resolved 的時候才會執行取消操作  
  9.             config.cancelToken.promise.then(function onCanceled(cancel){  
  10.                 if(!xhr){  
  11.                     return;  
  12.                 }  
  13.                 xhr.abort();  
  14.                 reject("請求已取消"); 
  15.                  // clean up xhr  
  16.                 xhr = null 
  17.             })  
  18.         }  
  19.     })  

CancelToken 的構造函數中需要傳入一個函數,而這個函數的作用其實是為了將能控制內部 Promise 的 resolve 函數暴露出去,暴露給 source 的 cancel 函數。這樣內部的 Promise 狀態就可以通過 source.cancel() 方法來控制啦,秒啊~ 👍

node 后端接口

node 后端簡單的接口代碼: 

  1. const express = require("express");  
  2. const bodyParser = require('body-parser');  
  3. const app = express();  
  4. const router = express.Router();  
  5. //文件下載  
  6. const fs = require("fs");  
  7. // get 請求  
  8. router.get("/getCount", (req, res)=> 
  9.   setTimeout(()=> 
  10.     res.json({  
  11.       success: true,  
  12.       code: 200,  
  13.       data: 100  
  14.     })  
  15.   }, 1000)  
  16. })  
  17. // 二進制文件流 
  18. router.get('/downFile', (req, res, next) => {  
  19.   var name = 'download.txt' 
  20.   var path = './' + name;  
  21.   var size = fs.statSync(path).size;  
  22.   var f = fs.createReadStream(path);  
  23.   res.writeHead(200, {  
  24.     'Content-Type': 'application/force-download',  
  25.     'Content-Disposition': 'attachment; filename=' + name,  
  26.     'Content-Length': size  
  27.   });  
  28.   f.pipe(res);  
  29. })  
  30. // 設置跨域訪問  
  31. app.all("*", function (request, response, next) { 
  32.   // 設置跨域的域名,* 代表允許任意域名跨域;http://localhost:8080 表示前端請求的 Origin 地址  
  33.   response.header("Access-Control-Allow-Origin", "http://127.0.0.1:5500");  
  34.   //設置請求頭 header 可以加那些屬性  
  35.   response.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With');  
  36.   //暴露給 axios https://blog.csdn.net/w345731923/article/details/114067074  
  37.   response.header("Access-Control-Expose-Headers", "Content-Disposition");  
  38.   // 設置跨域可以攜帶 Cookie 信息  
  39.   response.header('Access-Control-Allow-Credentials', "true");  
  40.   //設置請求頭哪些方法是合法的  
  41.   response.header(  
  42.     "Access-Control-Allow-Methods",  
  43.     "PUT,POST,GET,DELETE,OPTIONS"  
  44.   );  
  45.   response.header("Content-Type", "application/json;charset=utf-8");  
  46.   next();  
  47. });  
  48. // 接口數據解析  
  49. app.use(bodyParser.json())  
  50. app.use(bodyParser.urlencoded({  
  51.   extended: false  
  52. }))  
  53. app.use('/api', router) // 路由注冊  
  54. app.listen(8081, () => {  
  55.   console.log("服務器啟動成功!")  
  56. }); 

git 地址

如果大家能夠跟著源碼敲一遍,相信一定會有很多收獲。 

 

責任編輯:龐桂玉 來源: 前端大全
相關推薦

2023-09-19 22:41:30

控制器HTTP

2021-01-18 05:13:04

TomcatHttp

2024-09-30 08:43:33

HttpgolangTimeout

2025-10-16 09:08:03

2021-04-22 05:37:14

Axios 開源項目HTTP 攔截器

2018-07-30 16:31:00

javascriptaxioshttp

2021-04-12 05:55:29

緩存數據Axios

2024-06-05 08:42:24

2021-04-06 06:01:11

AxiosWeb 項目開發

2024-03-19 08:36:19

2025-02-06 08:09:20

POSTGET數據

2021-05-26 05:18:51

HTTP ETag Entity Tag

2021-07-23 15:55:31

HTTPETag前端

2019-12-13 09:14:35

HTTP2協議

2024-04-30 09:53:12

axios架構適配器

2018-10-18 10:05:43

HTTP網絡協議TCP

2019-04-08 15:11:12

HTTP協議Web

2021-06-02 05:41:48

項目實踐Axiosaxios二次封裝

2019-07-02 08:24:07

HTTPHTTPSTCP

2024-08-12 12:32:53

Axios機制網絡
點贊
收藏

51CTO技術棧公眾號

久久综合色之久久综合| 亚洲茄子视频| 欧美一区二区三区四区五区| 色哟哟免费网站| 日本免费不卡视频| 蜜臀av性久久久久蜜臀av麻豆| 久久久999精品免费| 美女网站视频在线观看| 激情国产在线| 中文字幕一区二区三区四区| 国产精品一区二区免费看| youjizz在线视频| 中文无码久久精品| 国产视频丨精品|在线观看| 中文字幕第100页| 牛牛电影国产一区二区| 日本一区二区三区四区在线视频 | 国产sm精品调教视频网站| 97免费在线视频| 污软件在线观看| 国产a久久精品一区二区三区| 91精品国产高清一区二区三区蜜臀| 少妇无码av无码专区在线观看 | 亚洲男人天堂av| 精品国产免费一区二区三区| 国产精品免费无遮挡| 鲁大师成人一区二区三区| 久久成人精品电影| 中文字幕伦理片| 美女视频亚洲色图| 日韩色在线观看| 久热在线视频观看| 日本综合视频| 日韩欧美在线视频日韩欧美在线视频 | 舔着乳尖日韩一区| 免费国产成人看片在线| 国产福利小视频在线| 91麻豆高清视频| 国产精品日韩一区二区| 国产视频aaa| 精品影院一区二区久久久| 热久久99这里有精品| 日韩欧美三级在线观看| 欧美影院一区| 欧美巨乳在线观看| 91视频免费在线看| 99国产**精品****| 日韩在线观看免费高清| 国精产品一区二区三区| 国产精品探花在线观看| 国产午夜精品理论片a级探花| chinese麻豆新拍video| 91蝌蚪精品视频| 精品国产乱子伦一区| 国产免费a级片| 国产福利资源一区| 精品国产精品一区二区夜夜嗨| 两性午夜免费视频| 日韩最新av| 亚洲国产精品高清久久久| 丰满少妇一区二区三区专区| 免费一级欧美在线大片| 日韩精品一区二区三区视频播放| 亚洲五月激情网| 中文字幕亚洲在线观看| 亚洲第一精品夜夜躁人人躁| 欧美双性人妖o0| 天天躁日日躁狠狠躁欧美| 日韩电视剧在线观看免费网站 | 国产精品美女一区二区| 亚洲国产综合自拍| 麻豆视频免费在线观看| 一区二区三区四区中文字幕| 日韩精品综合在线| 老色鬼在线视频| 在线精品视频免费播放| 国产精品v日韩精品v在线观看| 亚洲天堂网站| 亚洲变态欧美另类捆绑| 久久久久国产精品区片区无码| 国产免费av一区二区三区| 日韩资源在线观看| 久久久久久久中文字幕| 欧美专区在线| 成人精品一区二区三区电影免费 | 亚洲精选一区二区| 欧美巨胸大乳hitomi| 五月久久久综合一区二区小说| 九九热视频这里只有精品| 六月丁香在线视频| 久久精品国产精品亚洲红杏| 97人人做人人人难人人做| 亚洲色图狠狠干| 国产精品天天看| 日本香蕉视频在线观看| 久久91导航| 日韩美女视频在线| 国产 欧美 在线| 自由日本语亚洲人高潮| 日本不卡高字幕在线2019| 国产又粗又长又大视频| 91丝袜呻吟高潮美腿白嫩在线观看| 欧美日韩精品综合| 日本性爱视频在线观看| 日韩欧美国产中文字幕| 天天色天天干天天色| 香蕉视频一区二区三区| 久久久极品av| 亚洲中文一区二区| 成人av在线资源网站| 亚洲欧洲中文| 末成年女av片一区二区下载| 91精品欧美一区二区三区综合在| 一起草在线视频| 亚洲综合专区| 国产精品视频一区二区高潮| 天天干,夜夜爽| 亚洲精品视频免费观看| www.亚洲高清| 蜜桃国内精品久久久久软件9| 久久高清视频免费| 在线视频欧美亚洲| 久久在线观看免费| 热99这里只有精品| 91精品国产自产精品男人的天堂| 日韩中文在线中文网在线观看| 69视频免费在线观看| 国产不卡高清在线观看视频| 做爰高潮hd色即是空| 国产成人精品123区免费视频| 亚洲精品国产精品国自产在线| 国模无码国产精品视频| 久久激情五月激情| 亚洲一区二区三区四区中文| 亚洲伦乱视频| 亚洲欧美日韩精品久久奇米色影视| www.99re7.com| 国产成人免费视频网站| 青青草免费在线视频观看| www.久久久.com| www.日韩av.com| 一级淫片免费看| 亚洲国产成人自拍| 亚洲 激情 在线| 成人写真视频| 国产在线观看精品| 五月香视频在线观看| 欧美色图片你懂的| 国产精品麻豆免费版现看视频| 久热精品在线| 神马欧美一区二区| 国产亚洲精彩久久| www.欧美三级电影.com| 国产又粗又猛又爽又黄的视频一 | 成人在线免费电影网站| 中文字幕综合在线| 亚洲一卡二卡在线观看| 亚洲欧洲韩国日本视频| 五月六月丁香婷婷| 韩国一区二区三区在线观看| 国产精品12| xx欧美视频| 一区二区三区国产视频| 亚洲综合五月天婷婷丁香| 亚洲天堂av老司机| 在线观看亚洲免费视频| 国产精品日韩欧美一区| 日本在线播放不卡| 日韩黄色在线| 欧美大片免费观看在线观看网站推荐| 超碰在线观看99| 天天操天天色综合| 中文字幕在线观看免费高清| 精品一区二区三区在线观看国产| 男女啪啪免费观看| 牛牛精品成人免费视频| 国产成人综合久久| 黄色精品在线观看| 亚洲第五色综合网| 天天综合久久综合| 亚洲三级在线观看| 艳妇乳肉亭妇荡乳av| 日本欧美一区二区| av日韩在线看| 欧美激情在线免费| 亚洲aa在线观看| 国模冰冰炮一区二区| 久久精品人人做人人爽| 色欲av伊人久久大香线蕉影院| 日韩欧美视频一区二区三区| 亚洲欧美精品aaaaaa片| av一二三不卡影片| 天天干天天av| 亚洲精品社区| 一区二区视频在线免费| 精品人人人人| 成人天堂噜噜噜| 成人免费看视频网站| 久久艳片www.17c.com| 天堂中文字幕在线| 欧美一级搡bbbb搡bbbb| 日韩三级一区二区| 亚洲一二三四区不卡| 人人艹在线视频| 91麻豆视频网站| 亚洲成a人片在线www| 日本大胆欧美人术艺术动态| 国产日本在线播放| 91一区二区| 欧美日韩一区二区三区在线观看免 | 污片在线观看一区二区| 男人操女人的视频网站| 久久九九影视网| 久久久久亚洲AV成人无码国产| 美腿丝袜一区二区三区| av免费观看网| 红桃视频欧美| 99中文字幕在线观看| 日韩黄色大片| 日韩国产在线一区| 伊人成综合网yiren22| 国产美女在线精品免费观看| 高清一区二区| 国产欧美精品在线播放| 韩国成人动漫| 国产91av在线| 国产激情视频在线看| 久久99亚洲热视| 精品黄色免费中文电影在线播放| 国产一级揄自揄精品视频| 四虎影视在线观看2413| 精品国产91乱码一区二区三区 | 国产欧美日韩网站| 欧美日韩国产成人精品| 国产成年人在线观看| 99re66热这里只有精品8| 先锋影音亚洲资源| 欧洲视频一区| 亚洲图片欧洲图片日韩av| 国产精品探花在线观看| 欧美专区一二三| 蜜臀av免费一区二区三区| 久久国产精品一区二区三区四区| 国产精品巨作av| 国产精品一区二区av| 国产精品美女在线观看直播| av在线不卡观看| 7m精品国产导航在线| 国产精品视频免费一区| 电影一区二区在线观看| 国产在线精品一区二区三区| 卡通动漫国产精品| 鲁鲁狠狠狠7777一区二区| 国产a久久精品一区二区三区 | 一本色道久久综合| 国产午夜伦鲁鲁| 久久最新视频| 亚洲一级免费观看| 国产乱淫av一区二区三区 | 久久久久国产精品麻豆| www.av天天| 1024成人网色www| 久草视频免费播放| 欧美午夜www高清视频| aaa在线视频| 69久久夜色精品国产69蝌蚪网 | 亚洲精品网站在线播放gif| 欧美在线一卡| 深夜福利亚洲导航| 怡红院在线播放| 97精品视频在线播放| 欧美色片在线观看| 91九色视频在线| 国产精品传媒| 午夜欧美一区二区三区免费观看| 91视频精品| 91免费黄视频| 日本美女视频一区二区| 国产人妻精品午夜福利免费| 99国产麻豆精品| 五月婷婷综合激情网| 亚洲激情图片小说视频| 成人毛片在线播放| 欧美精品少妇一区二区三区| 欧美熟女一区二区| 尤物精品国产第一福利三区| 中文字幕在线观看播放| 日本高清+成人网在线观看| 亚洲成人高清| 久久av一区二区| 日韩不卡一区| 欧美变态另类刺激| 精品一区二区三区免费观看| 亚洲蜜桃精久久久久久久久久久久| 国产视频一区二区在线观看| 人妻久久一区二区| 欧美性色xo影院| 国产夫妻性生活视频| 精品亚洲国产视频| 在线观看午夜av| 国产精品丝袜久久久久久高清 | 欧美污视频久久久| 欧美在线精品一区| 欧美激情精品久久久久久小说| 国产精品综合在线视频| 亚洲а∨天堂久久精品2021| 一区二区三区四区视频精品免费 | 亚洲精品在线视频| 怡红院av在线| 91精品国产综合久久香蕉最新版| 欧美电影完整版在线观看| 国产高清精品软男同| 久久一区国产| 精品无码在线视频| 亚洲在线一区二区三区| 国产精品毛片久久久久久久av| 亚洲视频999| av今日在线| 成人18视频| 欧美国产免费| 美女被艹视频网站| 国产精品久久久久四虎| 波多野结衣在线电影| 亚洲精品456在线播放狼人| www在线免费观看视频| 91精品国产综合久久久久久蜜臀| 伊人久久综合影院| 男人日女人bb视频| 99精品久久免费看蜜臀剧情介绍| 麻豆一区产品精品蜜桃的特点| 欧美日精品一区视频| 国产区在线视频| 全球成人中文在线| 九九久久成人| 白嫩少妇丰满一区二区| www激情久久| 久久亚洲精品国产| 日韩精品欧美国产精品忘忧草| 91探花在线观看| 国产视频一区二区三区四区| 欧美日韩免费| 中文字幕99页| 五月天视频一区| 日本天堂影院在线视频| 97国产精品免费视频| 日韩av系列| 成熟老妇女视频| 国产亚洲欧美激情| 尤物视频免费观看| 俺也去精品视频在线观看| 亚洲三级电影| 国产在线视频综合| 成人aa视频在线观看| 日韩在线视频免费播放| 日韩精品中文字幕在线| 国产精品极品美女在线观看| 日韩欧美在线电影| 狠狠色丁香久久婷婷综合_中| 搜索黄色一级片| 精品国产一二三| 中文一区一区三区高中清不卡免费 | 一级黄色片视频| 久久99精品久久久久久琪琪| 嗯用力啊快一点好舒服小柔久久| 无码精品a∨在线观看中文| 久久久久国产成人精品亚洲午夜| 中国女人真人一级毛片| 久久综合久中文字幕青草| ccyy激情综合| 人妻丰满熟妇av无码区app| 国产精品理伦片| 丰满人妻一区二区三区无码av| 91国自产精品中文字幕亚洲| 国产精品探花在线观看| 自拍一级黄色片| 精品福利在线视频| 91高清在线视频| 成人在线视频电影| 久久性色av| 一级黄色录像视频| 亚洲男人天堂网| 欧美经典影片视频网站| 国产精品一区二区免费在线观看| 国产午夜精品在线观看| jlzzjlzz亚洲女人18| 欧美专区在线视频| 91精品蜜臀一区二区三区在线| 黄色免费视频网站| 欧美精品三级在线观看| 91九色porn在线资源| 亚洲一区二区三区欧美| caoporn国产精品| 国产精品主播一区二区| 1769国内精品视频在线播放| 国产精品二区不卡| 三叶草欧洲码在线| 日韩一卡二卡三卡四卡| 亚洲伦乱视频| 成年人视频观看| 亚洲另类春色国产|