react 高效高質量搭建後臺系統 系列 —— 請求資料

彭加李發表於2023-01-04

其他章節請看:

react 高效高質量搭建後臺系統 系列

請求資料

後續要做登入模組(主頁),需要先和後端約定JSON資料格式,將 axios 進行封裝,實現本地的資料模擬 mockjs

Tip:spug 中後端返回 json 通常有 data 和 error兩個 key。就像這樣:{data: [,…], error: ""}

axios

spug 中的 axios

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 引入 axios

建立 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 的相關程式碼

mock

詳細介紹請看 這裡

spug 預設沒有 mockjs,筆者將其加入 myspug 中,方便後續前端開發。

Tip: 內網可以使用 docker 方式快速搭建 yapi(高效、易用、功能強大的視覺化介面管理平臺)

大致步驟如下:

  • 安裝 mockjs 包,上文我們已經安裝完畢
  • 新建 src/mock/index.js
  • 最後在 src/index.js 中引入 mock

最後在 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')
    })
);

擴充套件

修改預設啟動埠 3000

由於 create-react-app 啟動埠預設是 3000,筆者為了方便研究,需要同時啟動 spug 和 myspug 兩個專案,這裡將 spug 的埠改為 3010

// package.json
"scripts": {
  - "start": "react-app-rewired start",
  + "start": "set PORT=3010 && react-app-rewired start",

其他章節請看:

react 高效高質量搭建後臺系統 系列

相關文章