其他章節請看:
後續要做登入模組
(主頁),需要先和後端約定JSON資料格式,將 axios
進行封裝,實現原生的資料模擬 mockjs
。
Tip:spug 中後端返回 json 通常有 data 和 error兩個 key。就像這樣:{data: [,…], error: ""}
spug 中對 axios 的封裝主要在 http.js
檔案中。核心是請求攔截器
和返回攔截器
。原始碼如下:
// spug\src\libs\http.js
// 引入 axios
import http from 'axios'
// 對 history 包最簡單的封裝,用於下面執行 `history.push`來切換 Url
import history from './history'
// X_TOKEN 登入標識,登入成功後後端返回,存在 localStorage 中
import { X_TOKEN } from './functools';
// 使用者錯誤提示
import { message } from 'antd';
// response處理
function handleResponse(response) {
let result;
// 返回失敗。例如 401、404
if (response.status === 401) {
result = '對談過期,請重新登入';
if (history.location.pathname !== '/') {
// 重新登入,登入成功後再回到當前頁(from)
history.push('/', {from: history.location})
} else {
return Promise.reject()
}
// 返回成功。例如 200
} else if (response.status === 200) {
// 後端攜帶錯誤資訊
// 後端返回 json 通常有 data 和 error兩個 key。就像這樣:{data: [,…], error: ""}
if (response.data.error) {
result = response.data.error
} else if (response.data.hasOwnProperty('data')) {
return Promise.resolve(response.data.data)
// 返回二進位制資料
} else if (response.headers['content-type'] === 'application/octet-stream') {
return Promise.resolve(response)
// 不是內部(url 不是以 /api/ 開頭)
} else if (!response.config.isInternal) {
return Promise.resolve(response.data)
} else {
result = '無效的資料格式'
}
} else {
result = `請求失敗: ${response.status} ${response.statusText}`
}
// 報錯
message.error(result);
return Promise.reject(result)
}
// 請求攔截器
http.interceptors.request.use(request => {
request.isInternal = request.url.startsWith('/api/');
// 對內部 url 增加 X-Token 標識。初次登陸 X-Token 為 null
if (request.isInternal) {
request.headers['X-Token'] = X_TOKEN
}
// 請求超時設定為 30 秒
request.timeout = request.timeout || 30000;
return request;
});
// 返回攔截器
http.interceptors.response.use(response => {
return handleResponse(response)
}, error => {
if (error.response) {
return handleResponse(error.response)
}
const result = '請求異常: ' + error.message;
message.error(result);
return Promise.reject(result)
});
export default http;
用法大致就像這樣(請看 Dashboard 模組中的 store.js
):
import http from 'libs/http';
http.get('/api/cicd/gitlab/')
.then(res => this.gitlabList = res)
建立 myspug\src\libs\http.js
檔案,內容和 spug 相同
建立 history.js
檔案,內容和 spug 相同
// myspug\src\libs\history.js
import {createBrowserHistory} from 'history';
export default createBrowserHistory()
建立 functools.js
,目前只需要匯出 X_TOKEN 即可。spug 中的 functools.js 涉及許可權,後續我們可能會用上。
// myspug\src\libs\functools.js
export let X_TOKEN;
疑惑
:在研究 react 路由時,我們自己實現了一個路由,使用 history 時發現它會導致瀏覽器 url 的變化,我們會通過 history.listen
來監聽地址變化,而在 spug 官網中執行 history.push 不僅可以切換url,而且路由也發生了變化,但筆者沒有在原始碼中找到 history.listen 的相關程式碼
詳細介紹請看 這裡
spug 預設沒有 mockjs,筆者將其加入 myspug 中,方便後續前端開發。
Tip: 內網可以使用 docker 方式快速搭建 yapi(高效、易用、功能強大的視覺化介面管理平臺)
大致步驟如下:
最後在 App.js 中測試:
// myspug\src\App.js
// import http from 'libs/http';
import http from '@/libs/http';
export default function App() {
http.post('/api/account/login/', {})
.then(data => console.log('data', data))
return (
<div className="App">...</div >
);
}
控制檯輸出:
data {id: 1, access_token: '5bb076db06fd4001b85d12e44ab96c56', nickname: '管理員', is_supper: true, has_real_ip: true, …}
注:spug 中引入 http 直接是 import http from 'libs/http';
,在 vscode 中按住 ctrl 並將滑鼠移至 libs/http
能進入該檔案,而筆者的 myspug 卻報錯,提示./lib/http
。找不到原因,只能求其次,通過增加別名 @ 來避免相對符號 ../../../
// config-overrides.js
const { override, fixBabelImports,addWebpackAlias } = require('customize-cra');
const path = require('path')
module.exports = override(
...,
addWebpackAlias({
'@': path.resolve(__dirname, './src')
})
);
由於 create-react-app 啟動埠預設是 3000,筆者為了方便研究,需要同時啟動 spug 和 myspug 兩個專案,這裡將 spug 的埠改為 3010
。
// package.json
"scripts": {
- "start": "react-app-rewired start",
+ "start": "set PORT=3010 && react-app-rewired start",
其他章節請看: