前后端分離項(xiàng)目的Mock數(shù)據(jù)系統(tǒng)搭建

最近接手了一個Angular項(xiàng)目件炉,因?yàn)樵许?xiàng)目缺乏Mock機(jī)制,因此重新搭建了一套矮湘,記錄一下斟冕。

項(xiàng)目現(xiàn)狀:

該項(xiàng)目是使用angular框架進(jìn)行開發(fā)的MIS后臺管理系統(tǒng)。接手后缅阳,我首先對項(xiàng)目進(jìn)行了架構(gòu)上的調(diào)整宫静,主要參考了angular項(xiàng)目的最佳實(shí)踐,參考文章如下:
https://medium.com/@motcowley/angular-folder-structure-d1809be95542
https://itnext.io/planning-the-architecture-of-your-angular-app-a4840bfec13b
https://itnext.io/choosing-a-highly-scalable-folder-structure-in-angular-d987de65ec7
https://medium.com/dev-jam/5-tips-best-practices-to-organize-your-angular-project-e900db08702e
具體的不詳述券时,最終優(yōu)化后的項(xiàng)目結(jié)構(gòu)如下:

├── e2e
├── src
│   ├── app   
│   │   ├── core     
│   │   │   ├── models
│   │   │   └── services
│   │   ├── shared
│   │   │   ├── components                  # container of all general components
│   │   │   └── utils                       # container of all general functions
│   │   ├── views                           # container of all business pages
│   │   │   ├── alarm
│   │   │   └── ......                      
│   │   ├── app-routing.module.ts                                             
│   │   ├── app.component.css                                        
│   │   ├── app.component.less                                            
│   │   ├── app.component.html
│   │   ├──app.component.ts                                                               
│   ├── assets
│   │   ├── json                            # container of mock data assets     
│   │   ├── i18n                            # container of internationalization assets                      
│   │   └── images   
│   ├── environments
│   ├── favicon.ico                        
│   ├── index.html    
│   ├── style.css  
│   ├── style.less                         
│   ├── my-theme.css
│   ├── my-theme.less  
│   ├── main.ts  
│   ├── polyfill.ts   
│   ├── test.ts   
│   ├── tsconfig.app.json
│   ├── tsconfig.spec.json
│   ├── typing.d.ts
├── .angular-cli.json
├── CHANGELOG.md                            # recorder of all the important changes
├── karma.conf.js
├── localproxy.conf.json                    # config for mock server proxy
├── proxy.conf.json                         # config for server proxy
├── tsconfig.json
├── package.json
└── README.md

其中孤里,最重要的部分是app文件夾,承載了項(xiàng)目中最重要的業(yè)務(wù)代碼橘洞。除此之外捌袜,值得注意的是,原有項(xiàng)目將mock數(shù)據(jù)資源放在 /assets/json 路徑下進(jìn)行統(tǒng)一管理炸枣,每次調(diào)用文件時虏等,都需要從這個路徑下讀取數(shù)據(jù)。

優(yōu)化原因:

如上所述适肠,目前的項(xiàng)目將mock數(shù)據(jù)放在assets中進(jìn)行管理霍衫,通過改變各個模塊的service中的數(shù)據(jù)請求路徑來實(shí)現(xiàn)mock功能。因此侯养,每次需要mock數(shù)據(jù)時敦跌,都需要修改每個service中的路徑,引入本地的json文件逛揩。例如:

//home.service.ts -------------local代碼
…………
  baseUrl = "./assets/json/"
  url = {
    home_serviceData: this.baseUrl + "/home_serviceData.json",
    home_alarmData: this.baseUrl + "/home_alarmData.json",
    home_alarmChartData: this.baseUrl + "/home_alarmChartData.json",
    home_servicebarData:this.baseUrl + "/home_servicebar.json",
    sourceNames: this.baseUrl + "/SourceName.json",
    listSortMasters:this.baseUrl+"/listSortMsters.json",
  }

//home.service.ts -------------線上代碼
…………
  url = {
    home_serviceData: this.baseUrl + "/uui-lcm/serviceNumByCustomer",
    home_alarmData: this.baseUrl + "/alarm/statusCount",
    home_alarmChartData: this.baseUrl + "/alarm/diagram",
    home_servicebarnsData: this.baseUrl + "/uui-lcm/ns-packages",
    sourceNames: this.baseUrl + "/alarm/getSourceNames",
    listSortMasters: this.baseUrl + "/listSortMasters",
  }

如上所示柠傍,如果需要實(shí)現(xiàn)home模塊的mock功能,需要編寫線上和本地兩套代碼辩稽,在本地開發(fā)時將線上代碼刪除惧笛,而線上聯(lián)調(diào)時,又需要將local地址刪除逞泄。
這樣的開發(fā)方式使mock數(shù)據(jù)十分麻煩患整。為了減輕負(fù)擔(dān),原有的開發(fā)方式是將項(xiàng)目拆為線上和線下兩套代碼喷众,這樣省去了反復(fù)注釋和刪除代碼的麻煩各谚。但是這種做法同樣存在很大弊端:1. 在兩套代碼中來回粘貼代碼使得開發(fā)效率降低,且很容易因?yàn)檎`粘貼或是漏粘貼出現(xiàn)功能錯誤侮腹;2. 每次mock均需要大量修改service代碼嘲碧,service模塊是angular項(xiàng)目的核心模塊,一旦誤刪會導(dǎo)致嚴(yán)重的問題父阻;3. 這種做法使無關(guān)代碼參與到mock數(shù)據(jù)中來愈涩,不夠優(yōu)雅望抽;
因此,需要建立一套mock數(shù)據(jù)機(jī)制履婉,改變現(xiàn)有的兩套代碼的開發(fā)方式煤篙。

優(yōu)化目標(biāo):

為解決現(xiàn)有mock方式存在的弊端,同時滿足項(xiàng)目開發(fā)的新需求毁腿,新的mock方式需要達(dá)成以下目標(biāo):

  1. 建立一鍵啟動mock服務(wù)機(jī)制辑奈,使一套代碼可以同時兼容線上和線下兩種開發(fā)環(huán)境,廢棄之前兩套代碼間相互粘貼的開發(fā)模式已烤;
  2. 隔離業(yè)務(wù)代碼和mock數(shù)據(jù)功能鸠窗,使mock數(shù)據(jù)部分的代碼成為一個獨(dú)立的模塊,從而避免對業(yè)務(wù)代碼的干擾胯究;
  3. 通過mock數(shù)據(jù)實(shí)現(xiàn)真正的前后端分離稍计,使項(xiàng)目既能兼容原有的mock數(shù)據(jù),又能滿足新功能快速開發(fā)的需求裕循;

技術(shù)選型:

json-server+faker.js

這兩個都是好東西臣嚣,具體不詳述,可以看官網(wǎng)文檔和參考文章:
https://github.com/typicode/json-server
https://github.com/marak/Faker.js/
https://www.npmjs.com/package/faker
https://segmentfault.com/a/1190000008574028
https://juejin.im/post/5b06e6426fb9a07aa34aaa8d

項(xiàng)目優(yōu)化難點(diǎn):

雖然json-server可以解決大部分的請求數(shù)據(jù)返回剥哑,但是本項(xiàng)目還是有些特殊的情況硅则,這給mock數(shù)據(jù)造成了困難:

  1. 項(xiàng)目的請求路徑中存在變量,因此無法通過本地json文件直接模擬
  2. 項(xiàng)目原有接口遵循RESTful規(guī)范株婴,即:相同api怎虫,不同請求方式,則返回的數(shù)據(jù)不同督暂,實(shí)現(xiàn)的功能也不同

解決方法:

采用如下方法可以解決項(xiàng)目優(yōu)化中出現(xiàn)的難點(diǎn):

rewrite揪垄,routes,json-server接口攔截機(jī)制(post to get)

項(xiàng)目優(yōu)化思路:

以下是使用json-server優(yōu)化項(xiàng)目的思路:

json-server項(xiàng)目優(yōu)化項(xiàng)目思路

解釋一下思路逻翁。為了達(dá)到我們的優(yōu)化目標(biāo),解決項(xiàng)目中的難點(diǎn)捡鱼,采用了如圖所示的五個步驟進(jìn)行八回。

  1. 配置package.json 文件,安裝json-server驾诈,設(shè)置啟動服務(wù)的命令缠诅,同時配置代理,將本地服務(wù)代理至mock服務(wù)器上乍迄;
  2. 創(chuàng)建server.js 文件管引,用來豐富mock服務(wù)的配置內(nèi)容;
  3. 引入faker.js闯两,用來支持新老功能同時開發(fā)褥伴;
  4. 配置routes.js谅将,用來支持多字段接口轉(zhuǎn)發(fā)和可變請求路徑;
  5. 攔截接口重慢,對非GET類的請求實(shí)現(xiàn)路徑重拼和接口轉(zhuǎn)發(fā)饥臂;

ok,可以開搞了…...

具體做法:

具體做法分為基礎(chǔ)配置和自定義配置兩部分似踱。其中隅熙,自定義配置是針對我們項(xiàng)目的特有情況對json-server進(jìn)行的配置。

基礎(chǔ)操作
  1. package.json文件配置
    安裝json-server:
npm install -g json-server

在package.json文件中使用json-server核芽,添加serve的運(yùn)行命令囚戚,指定mock服務(wù)接口并啟動本地代理服務(wù):

"mockproxy": "ng serve --proxy-config localproxy.conf.json”, //代理本地請求至mock服務(wù)器
"mockconfig": "node ./src/app/mock/server.js --port 3002”,  //啟動本地mock服務(wù)器
"mock": "npm run mockconfig | npm run mockproxy",

其中,npm run mock命令是啟動mock服務(wù)和代理服務(wù)的集合轧简。
以上弯淘,實(shí)現(xiàn)了通過npm命令一鍵啟動mock服務(wù)的需求。接下來吉懊,需要對server.js文件進(jìn)行配置庐橙。

  1. server配置
    在app目錄下,創(chuàng)建mock文件夾借嗽,用來管理mock的配置文件态鳖。這樣,實(shí)現(xiàn)了項(xiàng)目mock數(shù)據(jù)與業(yè)務(wù)代碼的隔離恶导。
    在mock文件夾中浆竭,創(chuàng)建server.js文件,用作對json-server服務(wù)做自定義配置惨寿,這也是整個mock系統(tǒng)中最終要的一個文件邦泄。
    server.js文件的基礎(chǔ)編寫思路是:導(dǎo)入原有mock數(shù)據(jù)文件,之后將請求路徑通過json-server的use方法進(jìn)行使用裂垦,同時對接口進(jìn)行轉(zhuǎn)發(fā)顺囊。具體的編寫步驟,不詳述蕉拢,上代碼:
const jsonServer = require('json-server');
const server = jsonServer.create();
const middlewares = jsonServer.defaults();
const customersRouters = require('./routes');
const baseUrl = "/usecaseui-server/v1”;
// Set default middlewares
server.use(middlewares);
// Get mock data
const fs = require('fs');
const path = require('path');
let localJsonDb = {};  //import mock datas
const mockFolder = './src/app/mock/json'; //mock json path folder
const filePath = path.resolve(mockFolder);
fileDisplay(filePath);
function fileDisplay(filePath) {
    let fileList = [];
    // Return filelist on based of filePath
    const files = fs.readdirSync(filePath);
    files.forEach((filename) => {
        // Get filename's absolute path
        let filedir = path.join(filePath, filename);
        // Get the file information according to the file path and return an fs.Stats object
        fs.stat(filedir, (err, stats) => {
            if (err) {
                console.warn('Get files failed......');
            } else {
                let isFile = stats.isFile(); // files
                let isDir = stats.isDirectory(); //files folder
                if (isFile) {
                    fileList.push(path.basename(filedir, '.json'));
                    fileList.forEach(item => {
                        localJsonDb[item] = getjsonContent(item);
                    })
                }
                if (isDir) {
                    fileDisplay(filedir);
                }
                Object.keys(fakeoriginalData).map(item => {
                    localJsonDb[item] = fakeoriginalData[item];
                })
            }
        })
    })
    setTimeout(() => {
        runServer(localJsonDb);
    }, 100)
}

function getjsonContent(path) {
    let newpath = `./src/app/mock/json/${path}.json`;
    let result = JSON.parse(fs.readFileSync(newpath));
    return result;
}
function serverRewrite() {
    server.use(jsonServer.rewriter(customersRouters))
}

function runServer(db) {
    server.use(jsonServer.router(db));
}
server.listen(3002, () => {
    console.log('Mock Server is successfully running on port 3002 ??')
});

有幾點(diǎn)需要說明:
第一特碳,為了盡量減小修改成本,本項(xiàng)目直接讀取了mock文件夾中的數(shù)據(jù)晕换,同時午乓,將這些文件的文件名直接引用為接口的轉(zhuǎn)發(fā)路徑:

………    
    let fileList = [];
    // Return filelist on based of filePath
    const files = fs.readdirSync(filePath);
    files.forEach((filename) => {
        // Get filename's absolute path
        let filedir = path.join(filePath, filename);
        // Get the file information according to the file path and return an fs.Stats object
        fs.stat(filedir, (err, stats) => {
            if (err) {
                console.warn('Get files failed......');
            } else {
                let isFile = stats.isFile(); // files
                let isDir = stats.isDirectory(); //files folder
                if (isFile) {
                    fileList.push(path.basename(filedir, '.json'));
                    fileList.forEach(item => {
                        localJsonDb[item] = getjsonContent(item);
                    })
                }
                if (isDir) {
                    fileDisplay(filedir);
                }
                Object.keys(fakeoriginalData).map(item => {
                    localJsonDb[item] = fakeoriginalData[item];
                })
            }
        })
    })
………
function getjsonContent(path) {
    let newpath = `./src/app/mock/json/${path}.json`;
    let result = JSON.parse(fs.readFileSync(newpath));
    return result;
}
………

上面這段代碼做的事情,就是文件名轉(zhuǎn)路徑闸准。通過調(diào)用node中的fs模塊中的readdirSync和stat方法益愈,讀取文件夾內(nèi)容,判斷文件狀態(tài)夷家,之后將符合條件的文件push到fileList數(shù)組中蒸其,同時敏释,通過readFileSync方法讀取文件內(nèi)容,并組成如下對象枣接,供server啟動服務(wù)用:

{  
………
  'uui-sotn_getPinterfaceByVpnId': { 'vpn-binding': [ [Object] ] },
  'uui-sotn_getPnfInfo': {
    'pnf-name': 'pnf1000',
    'pnf-id': '79',
    'in-maint': true,
    'resource-version': '195',
    'admin-status': 'up',
    'operational-status': 'up',
    'relationship-list': { relationship: [Array] }
  },
  'uui-sotn_getSpecificLogicalLink': {
    'link-name': 'nodeId-79-ltpId-4_nodeId-78-ltpId-4',
    'in-maint': false,
    'link-type': 'some type',
    'speed-value': 'some speed',
    'resource-version': '13031',
    'operational-status': 'up',
    'relationship-list': { relationship: [Array] }
  },
  xuran_test_data: {
    'esr-system-info-id': 'xuran',
    'service-url': 'http://10.10.10.10:8080/',
    'user-name': 'demo',
    password: 'demo123456!',
    'system-type': 'ONAP',
    'resource-version': '18873'
  }
………
}

如上例颂暇,通過配置,將json文件的文件名作為導(dǎo)入對象的key值但惶,將文件內(nèi)容作為導(dǎo)入文件的value值耳鸯。經(jīng)過這樣配置的對象,就是可以被json-server所使用的數(shù)據(jù)了膀曾。
值得注意的是县爬,這樣的操作方式需要將原有的mock文件進(jìn)行重命名,使其符合接口的規(guī)則添谊〔圃基本要求是:文件名=下劃線連接的請求路徑名。這樣做有些麻煩斩狱,但是只需要修改一次耳高,日后有新的數(shù)據(jù)修改需求就變得很方便,同時對于其他開發(fā)者來說所踊,只需要做在json文件夾中添加符合規(guī)范的文件就可以實(shí)現(xiàn)mock數(shù)據(jù)功能泌枪,性價(jià)比還是可以的。
第二秕岛,本項(xiàng)目引入了json-server中間件碌燕,使用了router將拼接好的路徑對象應(yīng)用起來,搭建項(xiàng)目服務(wù)继薛。將上述對象導(dǎo)入進(jìn)來就可以實(shí)現(xiàn)接口的轉(zhuǎn)發(fā)了修壕。

function runServer(db) {
    server.use(jsonServer.router(db));
}

第三,導(dǎo)入的對象的key值是用下劃線連接的遏考,但是真實(shí)請求下各路徑間使用/連接的慈鸠,因此,導(dǎo)入服務(wù)的接口需要通過一層轉(zhuǎn)發(fā)才能真正達(dá)到模擬請求的目的诈皿×质可以通過在mock文件夾下添加routes.js文件來配置路徑轉(zhuǎn)發(fā)方式。同時稽亏,使用json-server的另外一個中間件 rewriter 來對重新配置后的路徑進(jìn)行轉(zhuǎn)發(fā):

//導(dǎo)入routes已經(jīng)重寫路徑
const customersRouters = require('./routes’);
………
function serverRewrite() {
    server.use(jsonServer.rewriter(customersRouters))
}
//routes.js路徑配置
…………
module.exports =
{
   ///////<-------------general interface--------->/////
        "/api/*": "/$1",
        "/*/*": "/$1_$2",
        "/*/*/*": "/$1_$2_$3",
        "/*/*/*/*": "/$1_$2_$3_$4",
        /////////////////////////
}

經(jīng)過以上的配置,一個初步可用的server服務(wù)器已經(jīng)搭建起來了缕题,這時運(yùn)行npm run mock追迟,可以發(fā)現(xiàn)大部分原有的接口已經(jīng)運(yùn)行起來了官脓,大吉大利。

  1. fake數(shù)據(jù)
    當(dāng)然铐拐,一個前后端分離的項(xiàng)目是要支持前端同學(xué)在脫離后端接口支持的情況下進(jìn)行獨(dú)立開發(fā)的。為了使項(xiàng)目能夠?qū)崿F(xiàn)敏捷開發(fā)浙值,我們還需要借助一些工具。mock.js是很常見的工具,但是對于json-server來說记罚,似乎最佳搭配還是faker.js。faker.js是個很強(qiáng)大的創(chuàng)建mock數(shù)據(jù)的工具壳嚎。雖然對中文的支持還不夠好桐智,但是由于本項(xiàng)目屬于國際化的開源項(xiàng)目,因此這個工具很適合此項(xiàng)目的開發(fā)烟馅。
    首先安裝faker.js:
npm install faker

接著说庭,創(chuàng)建mck/fake文件夾用于管理faker.js生成的fake數(shù)據(jù)。本項(xiàng)目將fake數(shù)據(jù)按功能分成了兩部分郑趁。
第一部分是fake數(shù)據(jù)生成功能刊驴。


const faker = require("faker");
const _ = require("lodash");
faker.locale = "en";
module.exports = {
    customer: _.times(20, function (n) {
        return {
            id: n,
            name: faker.name.findName(),
            phone: faker.phone.phoneNumber(),
            address: faker.address.streetAddress(),
            avatar: faker.internet.avatar()
        }
    }),
    language: { language: faker.random.locale() }
}

另一部分是fake對象生成功能。

const fakeData = require('./fakedata.js');
module.exports = {
    //Mock json
    'customer_info': fakeData.customer,
    'alarm_formdata_multiple': fakeData.customer,
    'home': fakeData.home,
    'language': fakeData.language,
}

最后一步是將生成的fake數(shù)據(jù)導(dǎo)入到現(xiàn)有的server服務(wù)中來寡润。

// 引入fake數(shù)據(jù)對象捆憎,并合并入生成的數(shù)據(jù)對象中
const fakeoriginalData = require('./fake/mock.js');  //import datas created in fakedata.js
………..
   files.forEach((filename) => {
          …………….
          if (isDir) {
             fileDisplay(filedir);
          }
          Object.keys(fakeoriginalData).map(item => {
              localJsonDb[item] = fakeoriginalData[item];
          })
    })

總言之,思路就是首先通過faker.js生成所需數(shù)據(jù)梭纹,然后將其封裝成符合規(guī)范的api對象躲惰,最后引入到server.js中,并將其和本地的json文件一同處理為router可識別的路徑栗柒。
通過上述三步操作礁扮,基礎(chǔ)操作部分已經(jīng)完成,一個前后端分離的json-server服務(wù)器基本搭建完成瞬沦,可以撒花了太伊。

自定義配置

之所有還有這一節(jié),是因?yàn)樵趯?shí)際使用的過程中逛钻,遇到了問題僚焦,即之前提到的優(yōu)化過程中出現(xiàn)的難點(diǎn)。所以需要在原有基礎(chǔ)上再進(jìn)行一些自定義的配置來解決曙痘。
第一個問題是自定義資源請求
json-server可以自動讀取規(guī)范的資源請求路徑芳悲,但是,如果請求路徑中存在變量边坤,基礎(chǔ)的功能就不能滿足這個要求了名扛。比如這種類型的請求:

createServiceType: this.baseUrl + "/uui-lcm/customers/*_*/service-subscriptions/*+*", 
 getCustomerresourceVersion: this.baseUrl + "/uui-lcm/customers/*_*", 
 getServiceTypeResourceVersion: this.baseUrl + "/uui-lcm/customers/*_*/service-subscriptions/*+*",

路徑中的_+代表變量。而上述路徑中茧痒,無論是中間還是尾部都存在變量肮韧,這種路徑無法被json-server自動檢測,需要手動配置。因此弄企,我們需要在routes中進(jìn)行配置:

//routes.js
"/uui-lcm/serviceNumByServiceType/:customer": "/CustomersColumn",
"/uui-lcm/customers/:customer": "/getCustomerresourceVersion”,
"/uui-lcm/customers/:customer/service-subscriptions/:id": "/getServiceTypeResourceVersion",

如上所示超燃,將特殊的路徑在routes中進(jìn)行配置,冒號后面是需要代理到的地址拘领。這樣就可以實(shí)現(xiàn)存在變量的api地址的資源轉(zhuǎn)發(fā)了意乓。
第二個問題是post put delete請求的轉(zhuǎn)換
項(xiàng)目中存在相同url不同請求方式的接口,且參數(shù)拼接方式相同约素,單純使用json-server無法滿足需求届良。比如:

   customers: this.baseUrl + "/uui-lcm/customers", /* get */
    deleteCustomer: this.baseUrl + "/uui-lcm/customers", /* delete */
    createCustomer: this.baseUrl + "/uui-lcm/customers/", /* put */
    ……………
    getAllCustomers() {
        return this.http.get<any>(this.url.customers);
    }
    createCustomer(customer, createParams) {
        let url = this.url.createCustomer + customer;
        return this.http.put(url, createParams);
    }
    deleteSelectCustomer(paramsObj) {
        let url = this.url.deleteCustomer;
        let params = new HttpParams({ fromObject: paramsObj });
        return this.http.delete(url, { params });
    }

如上述所示,/uui-lcm/customers這個地址通過請求方式和參數(shù)的不同业汰,實(shí)現(xiàn)了不同的功能伙窃。json-server有特定的語法,對post样漆、put等請求有既定的返回?cái)?shù)據(jù)格式的規(guī)范为障。但是在本項(xiàng)目中,卻需要返回既有數(shù)據(jù)放祟,因此鳍怨,需要對數(shù)據(jù)請求接口進(jìn)行攔截,改造成需要的路徑跪妥,同時更改這些接口的請求方式鞋喇,來需要解決相同url但不同請求方式的問題:

const fs = require('fs');
const path = require('path');
const jsonServer = require('json-server');
const server = jsonServer.create();
const middlewares = jsonServer.defaults();
const customersRouters = require('./routes');
const baseUrl = "/usecaseui-server/v1";
…………
server.post(`${baseUrl}/*`, (req, res, next) => {
    const prefix = req.url.replace(baseUrl, "");
    req.url = `${baseUrl}/POST${prefix}`;
    req.method = 'GET';
    next();
})
server.put(`${baseUrl}/*`, (req, res, next) => {
    const prefix = req.url.replace(baseUrl, "");
    req.url = `${baseUrl}/PUT${prefix}`;
    req.method = 'GET';
    next();
})
server.delete(`${baseUrl}/*`, (req, res, next) => {
    const prefix = req.url.replace(baseUrl, "");
    req.url = `${baseUrl}/DELETE${prefix}`;
    req.method = 'GET';
    next();
})
……………

上述代碼使用了json-server來攔截不同類型的服務(wù),攔截之后改變了請求地址和請求方式眉撵。通過在地址中添加PUT侦香、DELETE等字段,將這些請求與原有的請求隔離開纽疟,之后將請求方式改為GET罐韩,最后再在routes文件和json文件中進(jìn)行配置即可滿足需求。之所以這樣操作污朽,是因?yàn)閷τ谇岸碎_發(fā)來說散吵,在項(xiàng)目的開發(fā)階段,需要關(guān)注的只有請求參數(shù)和返回結(jié)果蟆肆,換句話說矾睦,在這個階段,無論前后端采用什么樣的請求方式炎功,都與前端關(guān)系不大枚冗,前端只需要知道是向什么接口發(fā)了什么參數(shù)并返回了什么結(jié)果就可以了,因此蛇损,這里將其他類型的請求全部轉(zhuǎn)換為GET就是基于這個原因官紫。

//routes.js
"/PUT/uui-lcm/customers/:name/service-subscriptions/:id": "/PUT_uui-lcm_customers_service-subscriptions”,
"/DELETE/uui-lcm/customers/:customer/service-subscriptions/:id": "/DELETE_uui-lcm_customers_service-subscriptions",
本地mock數(shù)據(jù)文件命名

如此這般肛宋,就解決了項(xiàng)目在mock數(shù)據(jù)過程中的全部問題州藕,一個比較完整的Mock數(shù)據(jù)系統(tǒng)也搭建完成了束世。自此,可以徹底摒棄之前兩套代碼的開發(fā)模式床玻,通過一個簡單的npm run mock命令來啟動本地mock開發(fā)了毁涉。
最后上一遍server.js的完整代碼:

const jsonServer = require('json-server');
const server = jsonServer.create();
const middlewares = jsonServer.defaults();
const customersRouters = require('./routes');
const baseUrl = "/usecaseui-server/v1";

// Set default middlewares (logger, static, cors and no-cache)
server.use(middlewares);

// Get mock data
const fs = require('fs');
const path = require('path');

let localJsonDb = {};  //import mock datas
const fakeoriginalData = require('./fake/mock.js');  //import datas created in fakedata.js
const mockFolder = './src/app/mock/json'; //mock json path folder
const filePath = path.resolve(mockFolder);

fileDisplay(filePath);
function fileDisplay(filePath) {
    let fileList = [];
    // Return filelist on based of filePath
    const files = fs.readdirSync(filePath);
    files.forEach((filename) => {
        // Get filename's absolute path
        let filedir = path.join(filePath, filename);
        // Get the file information according to the file path and return an fs.Stats object
        fs.stat(filedir, (err, stats) => {
            if (err) {
                console.warn('Get files failed......');
            } else {
                let isFile = stats.isFile(); // files
                let isDir = stats.isDirectory(); //files folder
                if (isFile) {
                    fileList.push(path.basename(filedir, '.json'));
                    fileList.forEach(item => {
                        localJsonDb[item] = getjsonContent(item);
                    })
                }
                if (isDir) {
                    console.warn("=====> DO NOT support mock data in folder");
                    fileDisplay(filedir);
                }
                Object.keys(fakeoriginalData).map(item => {
                    localJsonDb[item] = fakeoriginalData[item];
                })
            }
        })
    })
    setTimeout(() => {
        serverRewrite();
        runServer(localJsonDb);
    }, 100)
}

function getjsonContent(path) {
    let newpath = `./src/app/mock/json/${path}.json`;
    let result = JSON.parse(fs.readFileSync(newpath));
    return result;
}
//only multi router data needs jsonServer.rewriter
function serverRewrite() {
    server.use(jsonServer.rewriter(customersRouters))
}

function runServer(db) {
    server.use(jsonServer.router(db));
}
server.post(`${baseUrl}/*`, (req, res, next) => {
    const prefix = req.url.replace(baseUrl, "");
    req.url = `${baseUrl}/POST${prefix}`;
    req.method = 'GET';
    next();
})
server.put(`${baseUrl}/*`, (req, res, next) => {
    const prefix = req.url.replace(baseUrl, "");
    req.url = `${baseUrl}/PUT${prefix}`;
    req.method = 'GET';
    next();
})
server.delete(`${baseUrl}/*`, (req, res, next) => {
    const prefix = req.url.replace(baseUrl, "");
    req.url = `${baseUrl}/DELETE${prefix}`;
    req.method = 'GET';
    next();
})

server.listen(3002, () => {
    console.log('Mock Server is successfully running on port 3002 ??')
});

拜拜。今晚回家吃雞??

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末锈死,一起剝皮案震驚了整個濱河市贫堰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌待牵,老刑警劉巖其屏,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異缨该,居然都是意外死亡偎行,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門贰拿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛤袒,“玉大人,你說我怎么就攤上這事膨更∶钫妫” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵荚守,是天一觀的道長珍德。 經(jīng)常有香客問我,道長矗漾,這世上最難降的妖魔是什么锈候? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮缩功,結(jié)果婚禮上晴及,老公的妹妹穿的比我還像新娘。我一直安慰自己嫡锌,他們只是感情好虑稼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著势木,像睡著了一般蛛倦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上啦桌,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天溯壶,我揣著相機(jī)與錄音及皂,去河邊找鬼。 笑死且改,一個胖子當(dāng)著我的面吹牛验烧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播又跛,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼碍拆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了慨蓝?” 一聲冷哼從身側(cè)響起感混,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎礼烈,沒想到半個月后弧满,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡此熬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年庭呜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摹迷。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡疟赊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出峡碉,到底是詐尸還是另有隱情近哟,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布鲫寄,位于F島的核電站吉执,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏地来。R本人自食惡果不足惜戳玫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望未斑。 院中可真熱鬧咕宿,春花似錦、人聲如沸蜡秽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芽突。三九已至试浙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寞蚌,已是汗流浹背田巴。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工钠糊, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人壹哺。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓抄伍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親斗躏。 傳聞我的和親對象是個殘疾皇子逝慧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容