3年磨一劍,個人前端數據 mock 庫 http-mock-middleware

很差意思,離開博客園4年多了,一回來就是爲本身打廣告,真是害羞啊。。。javascript

 

http-mock-middleware 是我最近完成的一個前端數據 mock 庫。它是我彙總近3年工做經驗而誕生的一個工具,使用很方便。廢話很少說,我粘貼一下部分 README,歡迎你們去 star。html

 

一個強大、方便的 http mock 庫。前端

目錄

介紹

http-mock-middleware 是一個 http mock 庫,或者說 ajax/websocket mock 庫,它接收來自 web 前端頁面的 ajax/websocket 請求,將請求映射到本地 mock 文件並通過一系列插件處理後返回給 web 前端頁面。http-mock-middleware 內建了多個插件以實現各類各樣的功能,好比:根據 query 參數等的不一樣響應不一樣的數據,按需將請求轉發給後端服務器,延遲響應,設置 cookie,主動向 websocket 客戶端發送數據等。vue

什麼是本地 mock 文件?就是用於存放對應請求的假數據文件,好比要將請求 /login 映射爲本地假數據文件 .data/login.json,就稱 .data/login.json 爲 mock 文件。java

http-mock-middleware 自己導出爲一個兼容 express middleware 的函數,所以你能夠很方便的集成到 webpack-dev-server, vue-cli-service, express 等現有服務器中。 webpack

特性

  • 支持任意 http 方法和任意 url 格式
  • 支持 mock 任意文件
  • mock json 文件時,支持 mockjs 語法, json5 語法
  • mock json 文件時,支持根據 query, body, headers, cookie 等信息按需響應
  • mock json 文件時,支持設置 cookie、http 頭、http 狀態碼
  • mock json 文件時,支持響應延時,殺掉請求,請求數據引用
  • 支持將 websocket onmessage 事件映射到本地 mock 文件
  • 支持主動發送 websocket 消息
  • 支持無重啓代理後端服務器,支持將代理的後端服務器內容保存爲本地 mock 文件
  • 無縫對接 webpack-dev-server, vue-cli-service, express 等
  • 支持一鍵導入 har 爲本地 mock 文件

安裝

npm i -D hm-middleware

或者ios

yarn add -D hm-middleware

http-mock-middleware 暴露了一個簡單的服務器命令: http-mock-server,讓你能夠無需任何配置便可快速的獲得一個 mock server,因此,若是你以爲方便的話可使用全局安裝的方式:git

npm i -g hm-middleware

API

middleware(options)

返回:兼容 express middleware 的函數,它接收 (request, response, next) 3 個參數。github

  • options 初始化選項
    • .mockRules mock 規則,若是指定了此選項,則忽略 mockrc.json,寫法參考 mockrc.json
    • .cors 是否跨域,默認爲 true。也能夠是一個 cors middleware 接受的配置對象
    • .parseBody 是否解析請求 body,默認爲 true。也能夠是一個 body-parser 接受的配置對象
    • .parseCookie 是否解析請求 cookie,默認爲 true。也能夠是一個 cookie-parser 接受的配置對象
    • .websocket 用於 websocket 消息處理的選項,若是啓用了 websocket , 這個選項是必須的。
      • .server http.Server 對象,當須要啓用 websocket 時,這個是必選項。
      • .serverOptions WebSocketServer 初始化選項,參考 ws api
      • .setupSocket(socket: WebSocket) 當有新的 websocket 鏈接時執行的鉤子函數。
      • .decodeMessage 函數。收到 websocket 消息後,須要將消息對象先映射爲 url,再映射爲本地 mock 文件。這個函數用於將消息對象解析爲 url,這個函數也能夠返回一個對象:{url: string: args: any},args 表示要傳遞給插件上下文 args 的數據。
      • .encodeMessage 函數。處理完本地 mock 文件後,須要將生成的內容轉換爲 websocket 客戶端能夠理解的消息格式。它接受三個參數:(error, data, decodedMsg)。若是在處理本地 mock 文件的過程當中發生任何錯誤,error 被設置爲該錯誤,此時 data 爲空;若是處理過程成功,則 data 對象被設置爲最終的生成數據,此時 error 爲空。注意:若是映射的本地 mock 文件是 json,則 data 對象爲 json 對象,若是映射的是非 json 對象,則 data 對象爲包含文件內容的 Buffer 對象;因爲 websocket.send() 方法僅僅接受 String, Buffer, TypedArray 等對象,所以你有必要返回正確的數據。第三個參數表示收到本次消息事件後 decodeMessage() 返回的數據。
    • .proxy 當收到的請求包含 X-Mock-Proxy 頭時,請求將被轉發到該頭所指向的服務器 url
      • .autoSave 是否自動將代理的內容保存爲本地 mock 文件,默認爲 false
      • .saveDirectory 若是須要自動保存,這個選項指定保存的目錄,通常使用 mockRules 配置的 dir 就能夠了。
      • .overrideSameFile 當自動保存時,若是發現文件已經存在,此選項指定如何處理。rename 現有文件被重命名,override 現有文件被覆蓋。

使用方法:web

// webpack.config.js
const middleware = require("hm-middleware");

module.exports = {
    devServer: {
        after: function(app, server){
            // 若是僅僅使用 http mock,這樣寫就能夠了
            app.use(middleware({
                mockRules: {
                    "/": ".data",
                    "/ws/app1": {
                        type: "websocket",
                        dir: ".data/app1"
                    }
                },
                // 若是須要支持 websocket,須要提供下面的選項
                websocket: {
                    server: server || this,
                    encodeMessage: function(){},
                    decodeMessage: function(){}
                }
            }));
        }
    }
};

文檔

http-mock-middleware 是如何工做的?

http-mock-middleware 按照以下順序工做:

  1. 收到請求,將請求 url 和 mockrc.json 指定的規則進行匹配
  2. 使用匹配的規則將 url 映射爲本地 mock 文件並獲取文件內容
  3. 將文件內容丟給插件處理
  4. 返回生成的數據

注意:若是在初始化時指定了 mockRules 參數,則 http-mock-middleware 忽略查找 mockrc.json。

配置文件 mockrc.json

mockrc.json 指定了 url前綴 和 本地 mock 目錄的對應關係,如:

{
    "/oa": ".data/oa-app",
    "/auth": ".data/auth-app",
    "/ws/app1": {
        "type": "websocket",
        "dir": ".data/websocket-app1"
    }
}

上面的配置說明:

全部 url 前綴爲 /oa/ 的 http 請求在 .data/oa-app 目錄查找 mock 文件,如:請求 GET /oa/version 優先映射爲 .data/oa-app/oa/get-version

全部 url 前綴爲 /auth/ 的 http 請求在 .data/auth-app 目錄查找 mock 文件,如:請求 POST /auth/login 優先映射爲 .data/auth-app/auth/post-login

在 url /ws/app1 上監聽 websocket 請求,並在收到 onmessage 事件後將收到的數據映射爲 url, 而後在 .data/websocket-app1 目錄查找 mock 文件。

上面提到了優先映射,你能夠在下一章節找到優先映射的含義。

注意:url 前綴在匹配時,默認認爲它們是一個目錄,而不是文件的一部分。如:/oa 表示 mock 目錄下的 oa 目錄,而不能匹配 /oa-old

http-mock-middleware 初始化時會在當前目錄查找 mockrc.json 文件,若是找不到則讀取 package.jsonmock 字段,若是還沒找到,就默認爲:

{
    "/": ".data"
}

即:全部的 http 請求都在 .data 目錄中查找 mock 文件。

如何查找 mock 文件?

當 http-mock-middleware 收到 http 請求時,首先將請求 url 分割爲兩部分:目錄 + 文件。下面是一個例子:

// 收到
GET /groups/23/user/11/score
// 分割爲
目錄: /groups/23/user/11
文件: score

而後,以 .data 爲根目錄,逐級驗證 groups/23/user/11 是否存在:

.data/groups
.data/groups/23
.data/groups/23/user
.data/groups/23/user/11

若是上面每個目錄都是存在的,則進行下一步,若是某個目錄不存在,則查找失敗,前端頁面將收到 404。

一般來講,url 中的某些部分沒有必要硬編碼爲目錄名,好比 .data/groups/23, 23 僅僅表明數據庫裏面的 id,它能夠是任何整數,若是咱們寫死爲 23 那麼就只能匹配 23 這個 group,若是咱們要 mock 這個 url 路徑,這個作法顯然是很愚蠢的。

http-mock-middleware 容許對整數作特殊處理,像這樣:[number]。若是目錄名是 [number] 就表示這個目錄名能夠匹配任何整數,這樣,上面的匹配過程將變成這樣:

.data/groups
.data/groups/23 => .data/groups/[number]
.data/groups/23/user
.data/groups/23/user/11 => .data/groups/23/user/[number]

=> 表示若是左邊的 url 路徑匹配失敗,則嘗試右邊的 url 路徑。能夠看到,這種方式經過對 url 中的某些部分模糊化,達到了通用匹配的目的。

http-mock-middleware 支持下面的模糊匹配:

模式 示例
[number] 1, 32, 3232
[date] 2019-03-10
[time] 11:29:11
[ip] 127.0.0.1, 192.168.1.134
[email] xx@yy.com
[uuid] 45745c60-7b1a-11e8-9c9c-2d42b21b1a3e

一個 url 裏面能夠有任意多個模糊匹配,若是請求 url 裏面的目錄部分所有匹配成功,則開始匹配文件名部分。匹配文件名時首先將目錄下面全部的文件都列出來,而後使用下面的格式進行匹配:

<method>-<filename><.ext>

匹配的結果若是多餘 1 個,優先使用以請求方法爲前綴的文件,如:

GET /groups/23/user/11/score
優先匹配的文件名是:get-score

文件名後綴並不做爲判斷依據,若是一個 url 同時匹配了幾個文件,除了請求方法爲前綴的文件外,其它文件的優先級是同樣的,誰是第一個優先使用誰,不過這種狀況應該不多,不須要考慮。

上面就是收到 http 請求時的匹配過程, websocket 的匹配過程基本一致,但與 http 不一樣的是,websocket 並不存在 url 一說,當咱們收到 onmessage 事件時,咱們收到的多是任意格式的數據,它們不是 url,所以在 http-mock-middleware 初始化時提供了將收到的數據轉換爲 url 的選項:

const middleware = require("hm-middleware");
middleware({
    server: currentServer,
    websocket: {
        // 收到的數據爲 json,將其中的字段組合爲 url
        // 具體如何組合取決於你的業務邏輯實現
        decodeMessage: function(msg){
            msg = JSON.parse(msg);
            return `/${msg.type}/${msg.method}`;
        }
    }
});

websocket 收到 onmessage 事件並將收到的數據解析爲 url 後,剩下的過程就和 http 一致了。

查找到本地 mock 文件後,文件內容和一些請求參數會丟給插件處理。

插件和指令

插件是一段有特殊功能的代碼,如多是設置 http 頭,多是解析 json 內特殊標記等。插件被設計爲是可插拔的,所以新增插件是很容易的。插件運行時接受一個共享的上下文環境對象。

注意:插件僅僅對 json 或 json5 文件生效。

http-mock-middleware 將多個核心功能丟給插件來完成。好比須要爲 response 設置 http 頭時,headers 插件就會在 json 文件內容裏面查找 #headers# 指令,並將指令的內容設置到 http 頭。

指令是插件可識別的特殊 json 鍵名,指令默認使用 #<name># 格式命名,這是爲了不和 json 鍵名衝突,指令的值就是對應的鍵值。

http-mock-middleware 支持的插件和指令以下:

cookies 插件

支持的指令: #cookies#

cookies 插件的主要功能是爲 response 設置 cookie http 頭。

#cookies# 的值爲對象或者對象數組,若是你但願對 cookie 作更爲精細的控制,則須要使用對象數組的形式。

假設 http 請求 GET /x 匹配的本地 mock 文件爲 .data/x.json,當使用了 #cookies# 指令後:

// file: .data/x.json
{
    // 對象形式
    "#cookies#": {a: 3, b: 4}
    // 也能夠是對象數組的形式
    // "#cookies#": [{name: a, value: 3, options: {path: "/"}}]
}

響應的 http 頭包括:

Set-Cookie: a=3
Set-Cookie: b=4

若是但願使用對象數組的格式,請參考 express response.cookie()

headers 插件

支持的指令: #headers#

headers 插件的主要功能是爲 response 設置自定義 http 頭,除此以外,它爲每一個 response 添加了一個 X-Mock-File 頭用以指向當前請求匹配到的本地 mock 文件,若是本地 mock 文件是 json ,則主動添加 Content-Type: application/json

#headers# 的值爲對象。

假設 http 請求 GET /x 匹配的本地 mock 文件爲 .data/x.json,當使用了 #headers# 指令後:

// file: .data/x.json
{
    "#headers#": {'my-header1': 3, 'my-header2': 4}
}

響應的 http 頭包括:

my-header1: 3
my-header2: 4

if 插件

支持的指令: #if#, #default#, #args#

if 插件的主要功能是根據請求參數條件響應,請求參數以下:

query          請求 url 中的查詢字符串構成的對象,即 request.query
body           請求體,如過請求體是 json,則 body 爲 json 對象,即 request.body
headers        請求頭對象,包含了當前請求的全部 http 頭,即 request.headers
cookies        當前請求所附帶的 cookie 信息,是一個對象,即 request.cookies
signedCookies  當前請求所附帶的加密 cookie 信息,是一個對象,即 request.signedCookies
args           #args# 指令的值
env            當前環境變量對象,即 process.env

#if# 指令的使用形式爲:#if:<code>#,code 是一段任意的 javascript 代碼,它運行在一個以請求參數爲全局對象的沙盒裏,當這段代碼求值結果爲真值,則表示使用它的值做爲 response 內容,若是全部的 #if# 求值結果均爲假值,則使用 #default# 指令的值,若是多個 #if# 指令求值結果爲真值,默認取第一個 #if# 指令的值,看下面的例子:

假設 http 請求 GET /x?x=b 匹配的本地 mock 文件爲 .data/x.json,當使用了 #if# 指令後:

// file: .data/x.json
{
    "#if:query.x == 'a'#": {
        "result": "a"
    },
    "#if:query.x == 'b'#": {
        "result": "b"
    },
    "#default#": {
        "result": "none"
    }
}

response 的內容爲:

{"result": "b"}

變量替換插件

支持的指令:#args#

變量替換插件主要的功能是遍歷輸出內容對象的值,將包含有變量的部分替換爲變量對應的值,變量的聲明格式爲 #$<var>#,其中 var 就是變量名,變量名是一個或多個變量的引用鏈,如:query, query.x, body.user.name

變量可引用的全局變量也是請求參數,同 #if# 指令同樣。

默認狀況,變量聲明若是是字符串的一部分,則替換後的結果也是字符串的一部分,若是變量聲明是一個字符串的所有,則使用替換後的值覆蓋字符串值,看下面的例子:

假設有 http 請求 POST /x?x=b,其請求體爲:

{
    "x": "b",
    "y": [1, 2, 3]
}

匹配的本地 mock 文件爲 .data/x.json

{
    "result": {
        "x": "#$body.x#",
        "y": "#$body.y#",
        "xy": "#$body.x##$body.y#"
    }
}

當通過變量替換後,內容以下:

{
    "result": {
        "x": "b",
        "y": [1, 2, 3],
        "xy": "b1,2,3"
    }
}

delay 插件

支持的指令: #delay#

delay 插件的主要功能是延遲 response 結束的時間。

#delay# 的值是一個整數,它表示延遲的毫秒值。

status code 插件

支持的指令: #code#, #kill#

status code 插件的主要功能是設置 response 狀態碼。

#code# 的值是一個有效的 http 狀態碼整數。

#kill# 的值是一個布爾值,它表示是否殺掉請求,殺掉請求後,後續的插件不會被調用。

ws-notify 插件

支持的指令: #notify#

注意:該插件僅對 websocket 生效

ws-notify 插件的主要功能是通過固定延遲時間後主動發起一個服務器端 websocket 消息

#notify# 的值 value 若是是字符串,等同於 [{url: value, delay: 0}] #notify# 的值 value 若是是對象,等同於 [value] #notify# 的值 value 若是是對象數組,則爲數組中的每一項建立一個 delay 毫秒後解析併發送 url 指向的本地 mock 文件的服務器端 websocket 消息任務

mockjs 插件

mockjs 插件的主要功能是爲 json 內容提供數據模擬支持,mockjs 的語法請參考這裏

websocket

若是你但願在 url 上使用 websocket ,務必要在 mock 規則裏面爲 url 添加 "type": "websocket",不然沒法生效。

因爲 websocket 收發消息沒有統一的標準,能夠是二進制,也能夠是字符串,http-mock-middleware 沒法準確的知道消息格式,所以你有必要告訴 http-mock-middleware 如何解析、封裝你的 websocket 消息。參考 API 獲取有關配置的詳細信息。

動態後端代理

在前端開發階段,有 mock 數據支持就夠了,在先後端聯調過程當中,後端服務器的數據更真實,mock 數據反而不那麼重要了。所以在必要的時候將數據代理到後端服務器就顯得頗有必要了。

http-mock-middleware 主要經過 X-Mock-Proxy 頭來判斷是否須要代理,下面使用 axios 庫演示如何使用 localStorage 控制動態代理:

const axios = require("axios");

axios.interceptors.request.use(function(config){
    if(process.env.NODE_ENV === "development") {
        let mockHeader = localStorage.proxyUrl;
        if(mockHeader) {
            config.headers = config.headers || {};
            config.headers["X-Mock-Proxy"] = mockHeader;
        }
    }
    // other code
    return config;
});

使用上面的代碼,開發時不設置 localStorage.proxyUrl 使用假數據,聯調時設置 localStorage.proxyUrl 指向後端服務器使用真數據。

注意:X-Mock-Proxy 的值是一個 url, 由於 http-mock-middleware 沒法肯定你的服務器是否是 https。一般你須要只設置爲 http://host:port/ 就能夠了

FAQ

數據遷移?

http-mock-middleware 提供了幾種數據遷移的方式:

  1. 使用 proxy,並將實際的後端數據保存爲 mock 文件,這種方式最簡單,後面須要對數據作 mock 的時候,稍微修改一下就能夠了
  2. 使用導入命令導入 har 格式的數據,chrome devtools 能夠將捕獲的請求保存爲 har 格式的文件,保存後導入就能夠了,好比:
http-mock-import -f har -d .data some.har

TODO

  • 添加更多插件,好比添加運行在沙盒的 js 插件?
  • 變量替換獲取更多的數據源,如:git 分支名稱?
  • 讓數據關聯起來
  • 每一個 url 有成功、失敗等狀態,添加一個 web 小工具,可讓用戶動態選擇使用哪一個文件做爲 url 的默認文件
相關文章
相關標籤/搜索