axios妙用技巧
axios妙用技巧
前言
應用開發中,各服務的呼叫使用最多的就是HTTP的形式,使用的HTTP client也從request --> superagent --> axios。axios
中的各類函式都是基於promise
的形式,雖然我也鍾情於superagent
的鏈式呼叫,但axios
的各類promise
:transform
,interceptor
等特性,只能擁抱無法拒絕~
create
建立一個新的例項,此例項的公共配置獨立與其它例項。一般在後端開發會經常需要對接各類不同的服務,而各服務使用單獨的例項是較合理的方法,如下面例子初始化一個用於呼叫百度服務的例項:
const axios = require('axios'); const baiduService = axios.create({ // 設定介面路徑(相對路徑將拼接此路徑) baseURL: 'https://www.baidu.com/', // 根據不同的應用設定預設的超時 timeout: 3 * 1000, }); async function main() { try { const res = await baiduService.get('/'); console.info(res.status); } catch (err) { console.error(err); } } main();
transformRequest
在傳送請求前,可以對傳送的資料做轉換處理,預設的transformRequest
中會將提交的資料轉換為對應的字串(json或者querystring),具體程式碼可檢視transformRequest。
我的應用中有一個統計服務,使用的是批量傳送統計指標(設定為每次傳送200個指標),對頻寬的佔用較大,因此希望傳送指標時做資料壓縮,下面看看怎麼針對需求實現自定義的transform
。
const axios = require('axios'); const zlib = require('zlib'); const localService = axios.create({ baseURL: 'http://127.0.0.1:3000/', timeout: 3 * 1000, transformRequest: [ // 複用原有的轉換,實現json --> json string axios.defaults.transformRequest[0], (data, header) => { // 如果已處理過資料,則跳過 if (!header['Content-Encoding']) { return data; } // 如果資料長度1KB(如字元資料並不一定小於1KB),不壓縮 if (data.length < 1024) { return data; } // 將資料壓縮(可根據需要,只壓縮長度大於多少的資料) // 設定資料型別 header['Content-Encoding'] = 'gzip'; const w = zlib.createGzip(); w.end(Buffer.from(data)); return w; }, ], }); async function main() { try { const arr = []; for (let index = 0; index < 100; index++) { // 模擬生成統計資料 arr.push({ category: 'login', account: 'vicanso', value: Math.round(Math.random() * 100), ip: '127.0.0.1', }); } const res = await localService.post('/', { data: arr, }); console.info(res.status); } catch (err) { console.error(err); } } main();
服務端程式碼:
const Koa = require('koa'); const Router = require('koa-router'); const bodyParser = require('koa-bodyparser'); const app = new Koa(); const router = new Router(); // body parser中可以解壓資料 // 如果希望支援再多型別的壓縮資料,可參考https://github.com/stream-utils/inflation調整 app.use(bodyParser()); router.post('/', async (ctx) => { console.dir(ctx.request.body); ctx.body = 'OK'; }); app .use(router.routes()) .use(router.allowedMethods()); app.listen(3000);
通過上面的自定義gzip的transform,頻寬的佔用節約了70%左右,當然這裡會增加了CPU的損耗,根據各自的應用場景選擇不壓縮或者使用snappy等壓縮速度優先的演算法。
transformResponse
在接收到響應資料時,可以對響應資料做轉換處理,預設的transform
是呼叫JSON.parse
轉換為對應的Object。其的使用方法與transformRequest
類似,不再舉例細說。
adapter
可實現自定義的請求處理,axios
實現了基於瀏覽器的xhr
以及nodejs
的兩種處理,使其適應於兩種執行環境。一般我們不需要自己去實現adapter,主要的使用場景是在測試中mock資料,如下:
const axios = require('axios');
const baiduService = axios.create({
// 設定介面路徑(相對路徑將拼接此路徑)
baseURL: 'https://www.baidu.com/',
// 根據不同的應用設定預設的超時
timeout: 3 * 1000,
});
function mockAdapter(ins, fn) {
const {
adapter,
} = ins.defaults;
ins.defaults.adapter = fn;
return () => {
ins.defaults.adapter = adapter;
};
}
async function main() {
const done = mockAdapter(baiduService, (config) => {
// mock response,只返回狀態碼與data
return Promise.resolve({
status: 200,
data: 'OK',
});
});
try {
const res = await baiduService.get('/');
// 恢復adapter
console.info(res.status);
} catch (err) {
console.error(err);
} finally {
done();
}
}
main();
http(s)Agent
指定在nodejs環境中的http(s)的agent,如maxSockets,timeout等。下面例子中啟用keepAlive,複用TCP連線,提升效能(預設是未啟用)。
const axios = require('axios');
const http = require('http');
const https = require('https')
const localService = axios.create({
baseURL: 'http://127.0.0.1:3000/',
timeout: 3 * 1000,
httpAgent: new http.Agent({
keepAlive: true,
}),
httpsAgent: new https.Agent({
keepAlive: true
}),
});
async function main() {
try {
// 兩次順序呼叫,複用同樣的tcp連線
let res = await localService.get('/');
console.info(res.status);
res = await localService.get('/');
console.info(res.status);
} catch (err) {
console.error(err);
}
}
main();
服務端的程式碼,展示是否使用同一TCP連線:
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
router.get('/', async (ctx) => {
// 生成socke id,用於標記TCP連線
if (!ctx.socket._id) {
ctx.socket._id = Math.floor(Math.random() * 1000);
}
console.info(ctx.socket._id);
ctx.body = 'OK';
});
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000);
Interceptors
Interceptors
是axios
的一大特色,使用攔截器可以對傳送請求、接收資料做各類的操作(非同步的也支援)。如請求重試,前置認證等等。
request interceptor
後端服務部署,為了高可用,避免單點故障,一般而言都會部署多節點。各服務之間的呼叫,簡單的方式則是使用nginx或haproxy之類做反向代理,應用程式只接入反向代理的節點,這樣簡單方便,實際上反向代理則成為單點,達不到高可用的目標(實際情況對於大部分公司,訪問量不大,反向代理穩定,基本也不出狀況)。下面我們來討論如果在客戶端實現高可用的接入方式(如有完善的微服務體系,接入sidecar更簡單便捷,無程式碼入侵性):
const axios = require('axios');
class Backends {
constructor(backends) {
this.backends = backends.map((url) => {
return {
url,
healthy: false,
};
});
}
// 選擇其中可用的backend
get(policy) {
let backend = null;
switch (policy) {
case 'first':
this.backends.forEach((item) => {
if (!backend && item.healthy) {
backend = item;
}
});
break;
// 可實現更多的選擇策略,如round robin等
default:
break;
}
return backend;
}
doHealthCheck() {
// 可以根據需要調整為更完善的檢測方法,
// 如檢測5次,3次通過則認為healthy
this.backends.forEach((backend) => {
axios.get(`${backend.url}/ping`).then((res) => {
const {
status,
} = res;
if (status === 200) {
backend.healthy = true;
} else {
backend.healthy = false;
}
}).catch(() => {
backend.healthy = false;
})
});
}
startHealthCheck() {
setInterval(() => {
this.doHealthCheck();
}, 5000).unref();
this.doHealthCheck();
}
}
const localServiceBackends = new Backends([
'http://127.0.0.1:3000',
'http://127.0.0.1:3001',
]);
localServiceBackends.startHealthCheck();
const localService = axios.create({
timeout: 3 * 1000,
});
localService.interceptors.request.use((config) => {
const backend = localServiceBackends.get('first');
if (!backend) {
return Promise.reject(new Error('無可用的服務'))
}
config.baseURL = backend.url;
return config;
})
async function main() {
try {
const res = await localService.get('/');
console.info(res.status);
} catch (err) {
console.error(err);
}
}
// 延時執行,等待首次health check
setTimeout(main, 1000);
服務端程式碼:
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
router.get('/', async (ctx) => {
ctx.body = 'OK';
});
router.get('/ping', (ctx) => {
ctx.body = 'pong';
});
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000);
response interceptor
函式調用出錯統一基於Error
物件擴充套件,後端各服務都限定了標準的出錯返回,以JSON的形式返回出錯資料{"message": "出錯資訊", ...},其中message
則是出錯資訊,因此需要調整axios
以相容介面的出錯響應(預設返回的Error.message為http狀態碼的描述)。
const axios = require('axios');
const localService = axios.create({
baseURL: 'http://127.0.0.1:3000/',
timeout: 3 * 1000,
});
localService.interceptors.response.use(null, (err) => {
if (err.response && err.response.data) {
const {
data,
} = err.response;
if (data.message) {
// 可以根據後端出錯資料的標準,往error中新增再多的屬性
err.message = data.message;
}
}
return Promise.reject(err);
});
async function main() {
try {
await localService.get('/');
} catch (err) {
console.error(err.message);
}
}
main();
服務端程式碼:
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
// 公共的出錯處理
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
ctx.body = {
message: err.message,
};
}
});
router.get('/', async (ctx) => {
ctx.throw(400, '出錯了')
});
router.get('/ping', (ctx) => {
ctx.body = 'pong';
});
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000);
介面分析
組合使用request
與response
的interceptors
,可以無入侵式的增加介面分析,如效能、介面響應、資料等統計分析。
const axios = require('axios');
const localService = axios.create({
baseURL: 'http://127.0.0.1:3000/',
timeout: 3 * 1000,
});
const stats = (response) => {
// 未考慮各類異常場景
const {
config,
} = response;
const {
method,
url,
_start,
} = config;
// 可輸出更多的引數,如post資料,響應資料等
console.info(`${method} ${url} ${Date.now() - _start}ms status:${response.status}`);
};
localService.interceptors.request.use((config) => {
config._start = Date.now();
return config;
});
localService.interceptors.response.use((response) => {
stats(response);
}, (err) => {
stats(err.response);
return Promise.reject(err);
});
async function main() {
try {
await localService.post('/');
await localService.post('/error');
} catch (err) {
console.error(err.message);
}
}
main();
服務端程式碼:
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
// 公共的出錯處理
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
ctx.body = {
message: err.message,
};
}
});
router.post('/', async (ctx) => {
ctx.body = {
foo: 'bar',
};
});
router.post('/error', async (ctx) => {
ctx.throw(400, '出錯啦');
});
router.get('/ping', (ctx) => {
ctx.body = 'pong';
});
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(3000);
小結
在認真瞭解axios
之前,我一直不解為什麼其star的數量這麼高,比superagent
高了那麼多,當時自己沒去研究,理所當然認為因為vue推薦使用它
,所以才那麼火,深入瞭解之後,發現它的確有著過人之處。不要讓自己的見識誤解世界,要以實踐瞭解世界。
axios妙用技巧
前言
應用開發中,各服務的呼叫使用最多的就是HTTP的形式,使用的HTTP client也從request -
C/C++語言提供的位運算子有:
運算子
含義
功能
&
按位與
兩個二進位制位都為1,則該位的結果值為1;否則為0。
|
按位或
兩個二進位制 lena 維護 data 查詢語句 self source 結果 one 通用方法 Delphi做三層開發時,很多人都會在客戶端放一個TClientDataSet,中間層遠程數據模塊就對應放一個TDataSetProvider,然後再連起來.其實這種方法很煩瑣,而且程序癰腫
dup和dup2都可用來複制一個現存的檔案描述符,使兩個檔案描述符指向同一個file結構體。如果兩個檔案描述符指向同一個file結構體,File
Status Flag和讀寫位置只儲存一份在file結構體中,並且file結構體的引用計數是2。如果兩次open同一檔案得到兩個檔案描述符,則每個描述符對應一個 在 Visual Studio 中有一個視窗叫 **Immediate** 視窗,中文版本應該叫**即時視窗**。預設會在你啟動除錯時在 VS 編輯器中彈出來。你也可以通過 `Debug | Windows | Immediate` 或者使用快捷鍵 `Ctrl+Alt+I` 手動把它調出來。
![](htt 要掌握 lin while循環 inpu 步驟 過程 猜想 有時 技術 當我們在學習程序設計的過程中,我們會發現有很多重復的步驟,變化的僅僅是當中的某一個變量,這就要引入我們的一個重要的知識——“循環”。循環就是重復執行語句,這是個很方便又很有意思的技術,可以重復操作任 list 建議 表達式 博文 環境 dem 完整 定期 mat
eval()函數十分強大,官方demo解釋為:將字符串str當成有效的表達式來求值並返回計算結果。
so,結合math當成一個計算器非常好用。
其它使用方法,能夠把list,tuple 建議 logs 網頁布局 mar 500px 實現 hidden .html order
在各式各樣的網頁中,經常會看到形狀特別的布局,比如說下面的這種排版方式:
這種視覺上的效果,體驗十分好。那麽他是如何來實現的呢 com alt apply all 如何使用 name 深入 期待 單體模式 網上文章雖多,大多復制粘貼,且晦澀難懂,我希望能夠通過這篇文章,能夠清晰的提升對apply、call、bind的認識,並且列出一些它們的妙用加深記憶。
apply、call
在 ja images 關系 其中 一次 容易 line 數組 最大 最小 利用KMP的next數組的性質,我們可以找到next數組的循環節。先說結論:設字符串長n,則若其 i % ( i – next[n] ) == 0 ,則其有循環節(循環節數目大於1),其循環節數目為 i / fcm 每次 weight 不同 int 文件夾 保存 程序設計 必須
今天我在看代碼的時候突然看到在一個.c文件裏包括了#include "*.c"代碼,這個讓我非常詫異,然後google了一下。才發現是這麽回事情。以下我寫了一個測試代碼。 真的 src 樣式 不讓 nbsp lin image lock cnblogs p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px ".PingFang SC"; color: #454545 }
p.p2 { mar pos turn jsb world div fprintf cpp efault code
為什麽說do while(0) 妙?由於它的確就是妙,並且在linux內核中實現是相當的妙,我們來看看內核中的相關代碼:
#define db_error(fmt, .. 其中 blog 語言 插入 目的 bcd odi ise fse 1)字典: 有序性
collections模塊:
1.OrderedDict: 有序字典
2.defaultdict: 帶有默認值的字典
OrderedDict:
使用dict時,Key是無序的。在對d java ng- pop execute size 釋放資源 and var text
使用goto的優雅並避免結構的混亂
將要跳轉到的語句用do{…}while(0) 包起來就可以。
reference
#defien N 10
bool body 原理 value 協議 span print int ger world
class aa():
def bb(self):
print("hhhh")
return "hello world"
de param 我們 als 分塊 csr tle 能夠 更新 val 定義
在指定的延遲時間之後調用一個函數或執行一個代碼片段
這個是setTimeout最主要的功能,但也是很坑的地方,首先javascript其實是運行在單線程的環境下,意味者定時器會在未來的某個時間支持,但 ews public turn false div role 自我 cnblogs static Dictionary<TKey, TValue> 類是常用的一個基礎類,但用起來有時確不是很方便。本文逐一討論,並使用擴展方法解決。
向字典中添加鍵和值
添加鍵和值 clas png toolbar new plain date rest 小明 空字符 轉載:http://www.cnblogs.com/kevinCoder/p/4554960.html
Javascript:字符串分割split()妙用
概述:
split() win32 ron 最小 可執行文件 es2017 都是 輸入 版本控制工具 原理 如何在Windows中使用Linux命令?
網上有很多說是安裝CygwinPortable 在cmd 窗口下是用linux 命令,但是還有一些缺陷。
其實對於程序員來說有一個非常 相關推薦
axios妙用技巧
位運算的妙用技巧
Delphi三層開發小技巧:TClientDataSet的Delta妙用
【Linux除錯技巧----標準輸出重定向到檔案】dup2和dup的妙用
Visual Studio 除錯技巧之即時視窗的妙用
循環的妙用
Python:eval的妙用和濫用
css實現梯形(各種形狀)的網頁布局——transform的妙用
深入淺出 妙用Javascript中apply、call、bind
KMP的妙用(利用next數組尋找字符串的循環節)
#include "*.c"文件的妙用
font-szie=0的妙用
C語言在linux內核中do while(0)妙用之法
集合妙用的收集
C語言中do...while(0)的妙用-避免goto
python with 語句妙用
關於setTimeout的妙用
c# 擴展方法奇思妙用基礎篇五:Dictionary<TKey, TValue> 擴展
Javascript:字符串分割split()妙用
Git Bash的妙用 - 使用Linux命令