axios 在項目中的應用

提到 axios 都不陌生,由之前的 XMLHttpRequest -> $.ajax -> Fetch 發展,看來 Promise 是趨勢,而項目中也是由 vue-resource 的 this.$http "淨化" 成了 axios ,但實際項目中不只是引用 axios 發送請求那麼簡單,往往還需要進行封裝,以下就分享下我們項目中使用 axios 都做了哪些事。

當然不涉及到項目任何隱私,只是純粹的技術,對,很純粹。

封裝 Service 層

不直接引用 axios 發送請求,而是統一調用 service.js ,在 Service 層裏會做很多事。

創建 Service 

  • const service = axios.create({
    timeout: 10000,
    headers: {
    post: {
    'Content-Type': 'application/json;charset=UTF-8',
    },
    },
    emulateJSON: true,
    withCredentials: true,
    });

使用 axios.create 創建一個實例,並且配置一些 JSON 、超時、跨域之類的屬性。

國際化

統一使用 service.interceptors.request.use 攔截,並注入國際化標識,比如 request.headers['Accept-Language'] 或者 request.params.lang 等。

規範化後端數據結構

在 service.interceptors.response.use 裏統一後端返回結構,比如最終會序列化成:

{
success: Boolean,
code: Number,
message: String,
data: Any,
}

這樣好處是說業務層不需要關心各個服務端返回的是否正確,而規範化後端數據時,需要跟各個服務端溝通好返回的字段標識,以方便來轉成規範化結構,比如A端認爲 code === 200 纔是成功,B端認爲 code=0 纔是成功。

接口容錯

對於前端服務而言,我們認爲不應該出現報錯到控制檯,而應該更友好的展示出來,甚至於把錯誤信息上報到監控中心,在 Service 對外暴露 POST 、GET 方法時,統一使用 try 攔截,如:​​​​​​​

/**
* 重構 service 輸出,爲了兼容接口異常,導致js崩潰
*/
export default {
async get(...options) {
try {
const res = await service.get(...options);
return res;
} catch (e) {
return Promise.resolve({
success: false,
code: 500,
message: '服務出錯,請稍候重試',
});
}
},
async post(...options) {
try {
const res = await service.post(...options);
return res;
} catch (e) {
return Promise.resolve({
success: false,
code: 500,
message: '服務出錯,請稍候重試',
});
}
},
};

這樣在業務層就可以直接使用數據判斷,而不用再 try 或者 .catch 處理,如:

const res = await service.get(uri);

// 絕對是成功
if (res.success) {

} else {
alert(res.message);
}

當然這一偉大的邏輯是 @遠高 添加,非常厲害。

統一鑑權

因爲有規範化後端數據結構,並且結合 service.interceptors.request.use 攔截,可以很方便的對後端鑑權進行處理,比如:A端的A接口返回失敗則自動跳轉到登錄頁。甚至可以統一處理帳戶的黑、白名單。

URI 統一化

經過以上處理後整個 Service 整個都縱享絲滑,但項目應用時發現:

  1. 針對某些接口處理事情時比較繁瑣,需要不斷的向攔截層添加代碼
  2. 接口鏈接更換時,很依賴全局查找

針對以上的痛點,我們添加了 URI 前綴統一化,處理邏輯是添加一層配置層,把 URI 的前綴使用統一字符替換,如:@a/ 、@b/ 這些分別代表不同的接口方,而在使用時可以直接使用這些標識去請求, Service 層會統一處理替換,如:

export const api = {
'@a/': {
url: 'https://www.demo.com/api/a/',
// 自定義統一 headers
headers(service.config) {
return {
};
},

// 自定義統一參數
params(service.config) {
return {
};
},

// 自定義統一返回值
response(service.config, response) {},
},
};

這樣處理後在使用時只需要 service.get('@a/user/mm'); 即可,甚至配置裏還可以根據當前的環境進行分發到不同的接口中,萌萌噠~

Node.js 代理

Service 層處理之後,如果使用 CORS 即可直接生效,但現在很多服務都是反向代理,可以使用 webpack 的 devServer.proxy ,如:

// config/proxy.js

/**
* 本地開發、測試配置
*
* @type {Object}
*/
const dev = {
'/api/a/': {
target: '目標開發環境',
changeOrigin: true,
onProxyReq(req) {
req.setHeader('origin', '目標開發環境');
},
},
};

/**
* 線上生產模式
*
* @type {Object}
*/
const prod = {
'/api/a/': {
target: '目標生產環境',
changeOrigin: true,
onProxyReq(req) {
req.setHeader('origin', '目標生產環境');
},
},
};

// 暴露 dev 代理
exports.dev = dev;

// 暴露生產代理
exports.prod = prod;

// 根據環境暴露不同的代理
exports.proxy = IS_PROD ? prod : dev;

在 devServer.proxy 直接引用對應的環境配置即可。

你會發現還配置了 onProxyReq ,是因爲 changeOrigin 只是會修改請求的 host ,代碼見:node-http-proxy ,而很多後端服務是有源 origin 限制,這樣就可以穿透了。

注意:Service 是運行在瀏覽器端,而 Node.js 代理只是本地開發時爲了接口方便進行的代理,Service 把 @a/ 標識替換成請求本地的絕對鏈接 /api/a/xxx (假如不是 CORS 外鏈域名),然後進入 Node.js 代理,而線上大部分都是 Nginx 做的代理 。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章