你是如何構建 Web 前端 Mock Server 的?

大家肯定遇到過,當後端 API 沒有編寫完成時,前端無法進行調試,這就導致了前端會被後端阻塞的情況。而我所說的 Mock Server 是相當於構建假數據,然後把這些假數據存到 JSON 文件上,Mock Server 可以響應請求或者生成頁面,當然也可以順便生成 API 文檔。

然後,還會遇到的一個問題就是,介面的 URL 跟真正的 URL 是不同的,Mock Server 往往會使用 127.0.0.1:3000 這樣的 URL,而不是 http://www.foo.com/bar 這樣的的路徑,這會使得開發時與生產環境中 URL 不一樣的問題。

所以想看看大家是如何構建 Mock Server 的。


謝邀

為了更好的分工合作,讓前端能在不依賴後端環境的情況下進行開發,其中一種手段就是為前端開發者提供一個web容器,這個本地環境就是 mock server。

要完整運行前端代碼,通常並不需要完整的後端環境,我們只要在mock server中實現以下幾點就行了:

  1. 能渲染模板

  2. 實現請求路由映射

  3. 數據介面代理到生產或者測試環境

能渲染模板很簡單,在mock server中集成模板引擎就行了,然後提供模擬的頁面數據用於完整渲染頁面,不過有時候生產環境中的模板引擎可能有一些環境依賴的擴展,這個要單獨實現。

請求路由映射,實現原理就是要讓本地的mock server有一個router,能接收所有HTTP請求,然後在router中根據線上的路由約定,實現一套一樣的規則,這個也不難,不贅述了。

最後數據介面代理。與前端相關的HTTP請求一共就3種響應情況:

  1. 渲染頁面的請求;

  2. 靜態資源的請求;

  3. 獲取數據的請求。

由於實現了router,我們把渲染頁面的請求在mock server中處理掉,直接輸出本地模板的渲染結果;靜態資源的請求直接返迴文件內容;而把數據請求代理到測試或者生產環境,本地就不用mock了(當然,如果出現新的介面測試環境沒有的,可以追加router,在mock server想響應假數據)

至於題主說的url一致性問題,其實不存在的。你的這個 http://www.foo.com/bar 的數據請求,在js中應該這樣寫:

$.get("/bar", callback)

這種寫法,省略了host,在線下開發時,其最終結果是請求 http://127.0.0.1:3000/bar,而由於我們在mock server中實現了路由規則,這個請求實際上被代理到了測試/生產環境去獲取數據。而當你把代碼部署到線上時,其訪問真實請求地址又自動變成了你期望的 http://www.foo.bar,正常運行。

畫個圖總結一下:

補充一些Tips:

  • 由於Mock Server需要具備渲染模板的能力,因此可能需要一種輕量的服務端跨平台server實現方案,如果是java的後端,可以考慮使用jetty,一個1.8M的jar即可;如果是php的後端,可以考慮使用php 5.4以後內置的server,啟動命令是 php -S 127.0.0.1:3000 router.php;如果是Nodejs,那就很簡單了,估計都不需要Mock Server,本地也可以跑的
  • 當代理數據介面的生產/測試環境不具備新介面的時候,Mock Server要在本地製造假數據響應請求,可以使用 http://beta.json-generator.com/ 這類在線的JSON數據生成工具,非常方便。

很多前端工程師以為前端分離的唯一途徑是接入NodeJS作為UI層,其實不是的,還有一種方案就是這種Mock Server,前端工程師直接寫後端模板,效果有時候甚至更好,而且對已有前後端架構的改動成本最小。

====== 更新 ======

評論中 @相守鼎 給出了常用腳本語言下開啟簡易web server的方法,可以用於實現Mock Server:

  • ruby -run -e httpd . -p 9090

  • python -m SimpleHTTPServer 8000

  • php -S 127.0.0.1:8088 router.php


分享篇最近寫的文章:《如何處理好前後端分離的 API 問題》 phodal/fe

API 都搞不好,還怎麼當程序員?如果 API 設計只是後台的活,為什麼還需要前端工程師。

作為一個程序員,我討厭那些沒有文檔的庫。我們就好像在操縱一個黑盒一樣,預期不了它的正常行為是什麼。輸入了一個 A,預期返回的是一個 B,結果它什麼也沒有。有的時候,還拋出了一堆異常,導致你的應用崩潰。

因為交付周期的原因,接入了一個第三方的庫,遇到了這麼一些問題:文檔老舊,並且不夠全面。這個問題相比於沒有文檔來說,愈加的可怕。我們需要的介面不在文檔上,文檔上的介面不存在庫里,又或者是少了一行關鍵的代碼。

對於一個庫來說,文檔是多種多樣的:一份 demo、一個入門指南、一個 API 列表,還有一個測試。如果一個 API 有測試,那麼它也相當於有一份簡單的文檔了——如果我們可以看到測試代碼的話。而當一個庫沒有文檔的時候,它也不會有測試。

在前後端分離的項目里,API 也是這樣一個煩人的存在。我們就經常遇到各種各樣的問題:

  • API 的欄位更新了
  • API 的路由更新了
  • API 返回了未預期的值
  • API 返回由於某種原因被刪除了
  • 。。。

API 的維護是一件煩人的事,所以最好能一次設計好 API。可是這是不可能的,API 在其的生命周期里,應該是要不斷地演進的。它與精益創業的思想是相似的,當一個 API 不合適現有場景時,應該對這個 API 進行更新,以滿足需求。也因此,API 本身是面向變化的,問題是這種變化是雙向的、單向的、聯動的?還是靜默的?

API 設計是一個非常大的話題,這裡我們只討論:演進、設計及維護。

前後端分離 API 的演進史

剛畢業的時候,工作的主要內容是用 Java 寫網站後台,業餘寫寫自己喜歡的前端代碼。慢慢的,隨著各個公司的 Mobile First 戰略的實施,項目上的主要語言變成了 JavaScript。項目開始實施了前後端分離,團隊也變成了全功能團隊,前端、後台、DevOps 變成了每個人需要提高的技能。於是如我們所見,當我們完成一個任務卡的時候,我們需要自己完成後台 API,還要編寫相應的前端代碼。

儘管當時的手機瀏覽器性能,已經有相當大的改善,但是仍然會存在明顯的卡頓。因此,我們在設計的時候,儘可能地便將邏輯移到了後台,以減少對於前端帶來的壓力。可性能問題在今天看來,差異已經沒有那麼明顯了。

如同我在《RePractise:前端演進史》中所說,前端領域及 Mobile First 的變化,引起了後台及 API 架構的一系列演進。

最初的時候,我們只有一個網站,沒有 REST API。後台直接提供 Model 數據給前端模板,模板處理完後就展示了相關的數據。

當我們開始需要 API 的時候,我們就會採用最簡單、直接的方式,直接在原有的系統里開一個 API 介面出來。

為了不破壞現有系統的架構,同時為了更快的上線,直接開出一個介面來得最為直接。我們一直在這樣的模式下工作,直到有一天我們就會發現,我們遇到了一些問題:

  • API 消費者:一個介面無法同時滿足不同場景的業務。如移動應用,可能與桌面、手機 Web 的需求不一樣,導致介面存在差異。
  • API 生產者:對接多個不同的 API 需求,產生了各種各樣的問題。

於是,這時候就需要 BFF(backend for frontend)這種架構。後台可以提供所有的 MODEL 給這一層介面,而 API 消費者則可以按自己的需要去封裝。

API 消費者可以繼續使用 JavaScript 去編寫 API 適配器。後台則慢慢的因為需要,拆解成一系列的微服務。

系統由內部的類調用,拆解為基於 RESTful API 的調用。後台 API 生產者與前端 API 消費者,已經區分不出誰才是真正的開發者。

瀑布式開發的 API 設計

說實話,API 開發這種活就和傳統的瀑布開發差不多:未知的前期設計,痛苦的後期集成。好在,每次這種設計的周期都比較短。

新的業務需求來臨時,前端、後台是一起開始工作的。而不是後台在前,又或者前端先完成。他們開始與業務人員溝通,需要在頁面上顯示哪些內容,需要做哪一些轉換及特殊處理。

然後便配合著去設計相應的 API:請求的 API 路徑是哪一個、請求里要有哪些參數、是否需要鑒權處理等等。對於返回結果來說,仍然也需要一系列的定義:返回哪些相應的欄位、額外的顯示參數、特殊的 header 返回等等。除此,還需要討論一些異常情況,如用戶授權失敗,服務端沒有返回結果。

整理出一個相應的文檔約定,前端與後台便去編寫相應的實現代碼。

最後,再經歷痛苦的集成,便算是能完成了工作。

可是,API 在這個過程中是不斷變化的,因此在這個過程中需要的是協作能力。它也能從側面地反映中,團隊的協作水平。

API 的協作設計

API 設計應該由前端開發者來驅動的。後台只提供前端想要的數據,而不是反過來的。後台提供數據,前端從中選擇需要的內容。

我們常報怨後台 API 設計得不合理,主要便是因為後台不知道前端需要什麼內容。這就好像我們接到了一個需求,而 UX 或者美工給老闆見過設計圖,但是並沒有給我們看。我們能設計出符合需求的界面嗎?答案,不用想也知道。

因此,當我們把 API 的設計交給後台的時候,也就意味著這個 API 將更符合後台的需求。那麼它的設計就趨向於對後台更簡單的結果,比如後台返回給前端一個 Unix 時間,而前端需要的是一個標準時間。又或者是反過來的,前端需要的是一個 Unix 時間,而後台返回給你的是當地的時間。

與此同時,按前端人員的假設,我們也會做類似的、『不正確』的 API 設計。

因此,API 設計這種活動便像是一個博弈。

使用文檔規範 API

不論是異地,或者是坐一起協作開發,使用 API 文檔來確保對接成功,是一個「低成本」、較為通用的選擇。在這一點上,使用介面及函數調用,與使用 REST API 來進行通訊,並沒有太大的區別。

先寫一個 API 文檔,雙方一起來維護,文檔放在一個公共的地方,方便修改,方便溝通。慢慢的再隨著這個過程中的一些變化,如無法提供事先定好的介面、不需要某個值等等,再去修改介面及文檔。

可這個時候因為沒有一個可用的 API,因此前端開發人員便需要自己去 Mock 數據,或者搭建一個 Mock Server 來完成後續的工作。

因此,這個時候就出現了兩個問題:

  • 維護 API 文檔很痛苦
  • 需要一個同步的 Mock Server

而在早期,開發人員有同樣的問題,於是他們有了 JavaDoc、JSDoc 這樣的工具。它可以一個根據代碼文件中中注釋信息,生成應用程序或庫、模塊的API文檔的工具。

同樣的對於 API 來說,也可以採取類似的步驟,如 Swagger。它是基於 YAML語法定義 RESTful API,如:

swagger: "2.0"

info:
version: 1.0.0
title: Simple API
description: A simple API to learn how to write OpenAPI Specification

schemes:
- https
host: simple.api
basePath: /openapi101

paths: {}

它會自動生成一篇排版優美的API文檔,與此同時還能生成一個供前端人員使用的 Mock Server。同時,它還能支持根據 Swagger API Spec 生成客戶端和服務端的代碼。

然而,它並不能解決沒有人維護文檔的問題,並且無法及時地通知另外一方。當前端開發人員修改契約時,後台開發人員無法及時地知道,反之亦然。但是持續集成與自動化測試則可以做到這一點。

契約測試:基於持續集成與自動化測試

當我們定好了這個 API 的規範時,這個 API 就可以稱為是前後端之間的契約,這種設計方式也可以稱為『契約式設計』。(定義來自維基百科)

這種方法要求軟體設計者為軟體組件定義正式的,精確的並且可驗證的介面,這樣,為傳統的抽象數據類型又增加了先驗條件、後驗條件和不變式。這種方法的名字里用到的「契約」或者說「契約」是一種比喻,因為它和商業契約的情況有點類似。

按傳統的『瀑布開發模型』來看,這個契約應該由前端人員來創建。因為當後台沒有提供 API 的時候,前端人員需要自己去搭建 Mock Server 的。可是,這個 Mock API 的準確性則是由後台來保證的,因此它需要共同去維護。

與其用文檔來規範,不如嘗試用持續集成與測試來維護 API,保證協作方都可以及時知道。

在 2011 年,Martin Folwer 就寫了一篇相關的文章:集成契約測試,介紹了相應的測試方式:

其步驟如下:

  • 編寫契約(即 API)。即規定好 API 請求的 URL、請求內容、返回結果、鑒權方式等等。
  • 根據契約編寫 Mock Server。可以彩 Moco
  • 編寫集成測試將請求發給這個 Mock Server,並驗證

如下是我們項目使用的 Moco 生成的契約,再通過 Moscow 來進行 API 測試。

[
{
"description": "should_response_text_foo",
"request": {
"method": "GET",
"uri": "/property"
},
"response": {
"status": 401,
"json": {
"message": "Full authentication is required to access this resource"
}
}
}
]

只需要在相應的測試代碼里請求資源,並驗證返回結果即可。

而對於前端來說,則是依賴於 UI 自動化測試。在測試的時候,啟動這個 Mock Server,並藉助於 Selenium 來訪問瀏覽器相應的地址,模擬用戶的行為進行操作,並驗證相應的數據是否正確。

當契約發生髮動的時候,持續集成便失敗了。因此相應的後台測試數據也需要做相應的修改,相應的前端集成測試也需要做相應的修改。因此,這一改動就可以即時地通知各方了。

前端測試與 API 適配器

因為前端存在跨域請求的問題,我們就需要使用代理來解決這個問題,如 node-http-proxy,並寫上不同環境的配置:

這個代理就像一個適配器一樣,為我們匹配不同的環境。

在前後端分離的應用中,對於表單是要經過前端和後台的雙重處理的。同樣的,對於前端獲取到的數據來說,也應該要經常這樣的雙重處理。因此,我們就可以簡單地在數據處理端做一層適配。

寫前端的代碼,我們經常需要寫下各種各樣的:

if(response response.data response.data.length &> 0){}

即使後台向前端保證,一定不會返回 null 的,但是我總想加一個判斷。剛開始寫 React 組件的時候,發現它自帶了一個名為 PropTypes 的類型檢測工具,它會對傳入的數據進行驗證。而諸如 TypeScript 這種強類型的語言也有其類似的機制。

我們需要處理同的異常數據,不同情況下的返回值等等。因此,我之前嘗試開發 DDM 來解決這樣的問題,只是輪子沒有造完。諸如 Redux 可以管理狀態,還應該有個相應的類型檢測及 Adapter 工具。

除此,還有一種情況是使用第三方 API,也需要這樣的適配層。很多時候,我們需要的第三方 API 以公告的形式來通知各方,可往往我們不會及時地根據這些變化。

一般來說這種工作是後台去做代碼的,不得已由前端來實現時,也需要加一層相應的適配層。

小結

總之,API 使用的第一原則:不要『相信』前端提供的數據,不要『相信』後台返回的數據。


2017年10月26日評估

今天發現eolinker這家的服務還是很全的,基本覆蓋了介面的Mock的需求點,整體界面風格也不錯,過幾天上手看看。

_________________

2016年8月25日評估

原來Postman可以保存結果,那用Postman就可以搞定介面文檔了。

直接消除Swagger的書寫成本,而且介面修改再不用維護文檔了。^ 0 ^

用同一個賬號,就可以實現同步了。

_________________

2016年2月28日評估

根據前端和後台同學的反饋,還是用回Swagger了。

理由:

1.寫一遍Swagger代碼,相當於做了一次介面測試,有助於提升介面的穩定性。

2.Swagger難寫是經驗性的問題,一個非常複雜介面的Swagger也只需要花上10mins。而且介面變動不是一個經常性的事件,時間成本上可以接受。

3.Swagger嵌入到項目中,即開即用,比再啟動一個軟體要方便太多。

__________________

2016年1月10日評估

據前端的同學反饋,Mock Server並沒有顯著提高生產力。前後台分離的並行開發只是一種理想化的結果!出於這個角度考慮,我們放棄了對前後台並行開發的YY,考慮如何提高後台開發效率,減少非代碼類的工作。比如介面文檔的編寫,以及介面測試。

介面文檔解決方案

  1. Swagger,寫起來太費勁,修改介面的同時還需要修護文檔,增加工作量。

    PS:Swagger有提供通過編寫注釋生成介面的方案,但我覺得學習成本比較重。
  2. SosoApi,簡化了Swagger的書寫,但是把介面文檔放在網上會有一定的安全隱患。
  3. RAP,介面書寫非常簡單,非常容易上手,但沒有在線測試介面的功能。
  4. Postman,測試就是文檔,文檔就是測試,無需維護成本。缺點是無法精確定義欄位用處,只能靠欄位名進行推測。

目前我們團隊採用的是RAP+Postman的過渡方案,RAP來定義介面,Postman來測試介面。

如果RAP能夠實現介面測試那就再好不過了。

__________________

Mock Server評估(2015年11月25~26日)

  1. Mock.js,僅用於單一數據模擬,沒有生成文檔的功能

  2. Swagger Editor,npm install 時候的幾個包弄不定。文檔編寫難度大。
  3. FMS - FMS,輕量級, 使用數據模擬時還有些彆扭。

  4. RAP v0.11,結合了文檔、Mock.js、可視化、Rest、介面過渡、文檔修改提醒、支持本地部署。0.11.5版發布後,一騎絕塵。


如果你用的是restful架構,居然沒人說這貨?

pretenderjs/pretender · GitHub 比express輕便 誰用誰知道啊

var PHOTOS = {
"10": {
id: 10,
src: "http://media.giphy.com/media/UdqUo8xvEcvgA/giphy.gif"
},
"42": {
id: 42,
src: "http://media0.giphy.com/media/Ko2pyD26RdYRi/giphy.gif"
}
};

var server = new Pretender(function(){
this.get("/photos", function(request){
var all = JSON.stringify(Object.keys(PHOTOS).map(function(k){return PHOTOS[k]}))
return [200, {"Content-Type": "application/json"}, all]
});

this.get("/photos/:id", function(request){
return [200, {"Content-Type": "application/json"}, JSON.stringify(PHOTOS[request.params.id])]
});
});

啥你不想自己手動弄數據?各種copypaste累的慌?

我擦,居然還有這麼神奇的一個庫。

Marak/faker.js · GitHub

console.log(faker.fake("{{name.lastName}}, {{name.firstName}} {{name.suffix}}"));
// outputs: "Marks, Dean Sr."

這神奇的庫 能生成各種數據 簡直炫酷

弄完了怎麼結合 proxy tool那麼多 整一個唄

emberjs

ember server --proxy http://localhost:5000

webpack

devServer: {
hot: true,
inline: true,
//其實很簡單的,只要配置這個參數就可以了
proxy: {
"/api/*": {
target: "http://localhost:5000",
secure: false
}
}
},


背景:我們的後端全部都只有 JSON API。整個公司當時只有兩個後端碼農,而 webapp 和 Android app 是我一個人寫。

所以今年年初的時候我搞了這麼一茬:

  1. 首先,寫了一個 api.dtd。

  2. 然後,讓後端的同事按照 api.dtd 規定的格式寫 api.xml 作為介面文檔(IDE 會自動根據 DTD 提示錯誤的)。

  3. 之後就可以:
    • 在瀏覽器里可以直接看文檔(通過 XSLT)。
    • 根據 api.xml 自動生成 Android 版 app 中的實體類和請求入口函數,並自動生成 webapp 中的 TypeScript interfaces 和請求入口函數。
    • 既然有了 api.xml,用 Node.js 解析好它,就可以自動或半自動地對後端介面進行測試了。
    • 而也就可以有前端的 mock server 了。

在此以後,我自己的生活舒服了好多。

當然,我要感謝後端同事陪我玩,我要感謝後端同事陪我玩,我要感謝後端同事陪我玩。

P.S. 當時本來想用 RAML 而不是自己定義 XML 格式的,然而 RAML 本身的 API 不友好,不方便裝輪子。


前後端分離的開發模式中,數據 mock 最成熟的做法是搭建一個本地的 web server 。像 fis 等工具的中就已經自帶 web server 功能。如果不用 fis 可以使用 nginx 自己搭建,也可以使用對前端相對友好的 nodejs server,我自己就寫了一個,組裡部分同學在用,上手十分容易:使用 imitator 實現前後端分離開發中的數據模擬與靜態資源映射 |

安·記

至於題主說的 url 問題,其實很好解決。web server 都有路由配置的功能,開發的時候,url 只要寫成和線上地址一致,再通過路由配置把 url 打到需要 mock 的數據就可以了。舉個 imitator 的栗子:

imitator("/api", imitator.file("./test/api.json"));

然後前端你的 ajax 可能是這樣:

$.get("/api", callback);

就搞定了。


Mock server:
whq731/swagger-express-middleware-with-chance

我們組對應後端使用JAVA+Swagger

前端使用lincolnlau/swagger-jsblade 生成API調用文件和Mock

-----2017.06.24 新增:

lincolnlau/swagger-jsblade 中的Mock 2.0版近期會發布


我一般都是用代理伺服器加代理配置本地模擬開發,都是nodejs自己做的,最近打算做個mock server 學習一下大神們的做法。瀉藥。

在fds中我直接支持了.node這種擴展名,可以直接寫nodejs代碼,這樣就不需要額外配置路由了,但缺點就是只能用在開發環境,或者再配置一個rewrite,不過…

都差不多了…


弄個mock server , 其核心作用是提高生產力,給前端和後端都帶來方便,如果本身因為規範,流程而帶來副作用,那不如不用.

我覺最好的方式是用代碼是生成 mock json 和 文檔, 然後一鍵發布部署,開發人員每天接觸最多的還是IDE, 最好能集成在IDE上,然後通過IDE插件代碼反射生成.

大家也說過了,這個玩意項目開始剛開始生產力確實能提高,並發進行,都是全新開始,但是到後面不停地迭代更改依賴等問題就沒有那麼好使了.所以能依賴代碼的更改,一鍵同步到位,那真的會很不錯.

本人已開發了一套基於代碼註解"一鍵" (這裡目前還需要幾個步驟)發布mock data 和介面文檔,插件還沒有來得及做.

下面給個已用於生產的成果圖

這個是給定註解code

這個是介面文檔

再來個流程圖


你需要的不是Mock Server,而是在前端設計高質量的測試機制,實現真正的前後端分離開發。

function loadProducts(options) {
var options = options || {};
if (options.fixture) {
// 無後台返回杜撰的數據
return new Promise(function(resolve, reject) {
resolve(fixture);
});
} else {
// 有後台就正常請求伺服器
return $.get("/products", ....);
}
}

上面只是實現的一種,比較土,你也可以用DI,隨你,另外,有些庫和框架本身就提供類似功能。


直接用http://Apiary.io,不管是mock server還是開發文檔都給你搞定了!


前面有人提到了,阿里媽媽MUX出品的RAP

地址:https://github.com/thx/RAP

用了很久了,感覺棒棒噠

java的部署在內網,對接調試神器。滿足你的需求!

送上圖一張


推薦使用server-mock,可做為靜態伺服器,可適用絕大多數需要mock數據的場景,包括模版數據和ajax數據的mock, 關鍵是用起來極其簡單,還能生成範例供模仿。

1. 安裝

npm install -g server-mock

2. 創建範例文件(可選)

mock init

3. 把當前文件夾作為伺服器目錄

mock start


自己這段時間在做前後端分離開發的工作,從頭體驗了自己的虛擬數據到虛擬服務的過程。

在過程中對比使用了三個工具分別是:apiary,swagger,alibaba-rap

apiary:Apiary a€」 Home

是swagger的一種優化,只可以在線使用,不可以本地化。免費用戶的團隊協作不是特別好,因為沒有文檔鎖這種模式。文檔寫起來比較複雜,但是能夠更好的組織好項目的整體架構。mock server 可以在線,但是如果不翻牆的話會很慢。

swagger:Swagger – The World"s Most Popular Framework for APIs.

可以本地化,提供功能很多,自己僅僅使用了swagger-editor。文檔寫起來比較複雜,mock server需要自己生成代碼進行運行,不是很方便。

alibaba-rap:歡迎使用RAP

阿里巴巴的一個產品,現在還沒有正式版本,但是很好用,團隊協作,產品管理,書寫介面文檔都很方便,而且可以本地化。推薦使用


之前是用gulp-webserver實現的,添加了渲染模板,處理ajax請求,轉發url的中間件,後來脫離gulp用connect實現了一個一樣的。

關於題主的問題,如果使用的都是本站的資源,確實在開發階段直接使用根目錄就可以了,但是如果引用的靜態資源來自http://static.xxx.com或者代碼有跨域的請求怎麼辦,我提供一個比較笨的思路,比如可以把資源的引用寫成&,然後單獨定義$!{staticRoot},開發階段為"/",上線時修改為「http://static.xxx.com」,當然可以寫個腳本,每次上線的時候自動的去改,這個是我們之前的解決辦法...

近期會改為fis,開發階段使用相對路徑,發布時自動替換為絕對路徑,並且可以指定domin。


圖片來自網路

前端Mock的常見解決方案

Mock數據進行調試是前端構建中不可或缺的一步,常見的前端Mock方案分為4種:

  1. 在代碼層硬編碼

    比如有一個函數負責請求某個介面,獲得的回返數據,並對該回返數據進行操作。那麼可以通過硬編碼的方式,直接定義一個數據變數在代碼中,該變數保存了回返的Mock數據。這種Mock方法操作比較簡單,但缺點也很明顯,就是Mock更改了代碼邏輯,和代碼耦合性太強。而且並不能模擬真實的網路請求的過程,局限性強,覆蓋面窄。
  2. 在前端JS中攔截

    典型的解決方案就是Mock.js,通過在業務代碼前掛載該JS文件,就可以無痛攔截Ajax請求。這種Mock方式相較於硬編碼,雖然實現了Mock與代碼的部分解耦,但無法完全和業務代碼解耦(因為必須掛載一個JS並進行Mock配置)。雖然提供了大量的Mock API,但是也仍然無法發出真實的網路請求,模擬真實度不夠。另外這種方式還存在一些不足,因為是對XHR對象的改寫,有些情況下兼容性並不好,比如IE8等低版本瀏覽器,還有較新的Fetch API也攔截不到。
  3. 代理軟體(Fiddler、Charles)攔截

    Fiddler和Charles可以對網路請求進行攔截,將其替換為我們需要的Mock數據,這也不失為一種Mock方式。其優點主要是真實性強,但這種方式操作步驟比較繁瑣,不方便統一配置,Mock成本較高。
  4. Mock-Server

    最合適的方案無外乎搭建獨立的Mock-Server,開發的前期階段,所有的介面都會指向該Mock-Server。因為可能存在跨域的情況,所以一般都需要在開發環境搭配一套介面代理做搭配。這種方案對業務代碼完全不具有侵入性,並且通用性強。缺點也很明顯,成本高(還需要另起一個Mock-Server服務,並對其進行管理)。

介面定義與Mock數據相結合的方案

其實常見的解決方案便是上面提到的幾種,或是在這類的基礎上進行調整,但整體思路基本一致。Mock的本質是為了能讓介面消費者脫離介面生產者進行開發,但是這個脫離並不是指脫離生產者的介面定義隨意開發,介面定義與Mock數據的一致性也是我們必須考慮的一個問題,我們必須要確保Mock出的數據符合介面定義中的要求。所以實際上我們還需要多考慮一個問題:如何讓介面定義的維護和Mock結合起來?

很慶幸的是,我們有RAP這項工具。RAP是一款非常棒的工具,它是一個可視化介面管理工具,將所有的介面開發定義以其要求的格式進行管理,並能根據這些定義來產生Mock數據,提供給消費者。

既然有了RAP為我們提供Mock-Server服務,那麼我們下一步欠缺的就是如何讓代碼中的網路請求能直接訪問到RAP上。RAP有提供一種插件JS,是在Mock.js的基礎上擴展的,可以攔截頁面上的請求,但暫時僅支持Kissy和jQuery。為此我們在Koa的基礎上,結合RAP提供的NodeJS插件(rap-node-plugin)開發了一組工具,能夠將配置中要求攔截的所有請求轉發到RAP上,並將RAP生成的數據返回給請求方。另外,考慮到Mock調試中,RAP的Mock規則有限和介面定義文檔頻繁變更的問題,我們還加入了本地mock解決方案,可以隨時隨地的更改本地local-mock.js文件,避免頻繁的RAP文檔編輯。本地mock還支持以函數的形式動態返還Mock數據,大大提升了Mock數據生成的能力。另外,在網路請求Mock中,網路延時有時必不可少,我們通過在介面定義中添加_delay欄位,可以決定讓該介面多長時間後返回,其它下劃線欄位可以按需添加。RAP中的配置:

RAP介面配置

Mock方案實現

方案主要由以下4個組件構成:

mock工具

  • config.js

    包含一些對mock工具的配置

/**
* 默認配置
*/
export default {
PATH: "/pc/*", // 默認攔截的路徑
USE_RAP: true, // 默認為true,會去尋找並調用RAP上的介面
RAP_CONFIG: {
host: "rap.fe.yeshj.com", //啟動的服務主機
port: 80, //埠號
projectId: 115, //RAP配置的項目ID
mock: "/mockjsdata/", //RAP前綴
wrapper: "" //不需要包裝
}
}

  • filter.js

    KOA中間件,會對config中配置的PATH進行請求攔截,並根據相關配置獲得請求回返數據,進行一定程度的包裝,回返為Mock數據。與介面相關的_delay操作也在該中間件內實現
  • local_mock.js

    負責本地mock規則及數據的存放。_delay關鍵字在此配置,header支持自定義回返頭部,body回返體數據支持以函數的形式進行生成。

module.exports = {
"POST /pc/check": {
"_delay": 1000,
"header": {
"Content-Type": "application/json;charset=utf-8"
},
"body": function (ctx) {
return {
"status": 0,
"data": {
"ctx": ctx
}
}
}
},
"POST /pc/info": {
"_delay": 0,
"header": {
"Content-Type": "application/json;charset=utf-8"
},
"body": {
"data": {
"id": "12121212"
},
"message": "成功",
"status": 0
}
}
}

  • rap_connector.js

    該組件是對rap-node-plugin的封裝,方便調用。

開發環境集成

下面就是如何將mock集成到我們的開發環境中去了。

  • 建立mock目錄

    該Mock方案因為涉及到配置的調整和本地Mock數據的改寫,不適於做成單獨的node_module進行封裝,所以我們選擇將mock方案集成到項目中的構建工具中去,目錄層級如下:

Paste_Image.png

  • package.json中增加新腳本mock:

"scripts": {
"start": "npm run mock",
"mock": "babel-node ./tools/build/server.js --dev --mock"
}

  • 在koa中注入mock中間件:

if (argv.mock config.mockEnable) {
let mockFilter = require("../mock/filter.js");
app.use(mockFilter());
}

最後一步的工作很簡單,就是在mock/config.js中進行一些簡單配置,然後在bash中輸入npm start就可以享受我們整套的mock解決方案了。


Mock Server 只是開發當中的一個環節,看了題目的描述,並沒有展開說假數據是如何構造出來的,如果是人肉構造的話,那麼誰也不能保證後端最終提供的介面數據和前端自己構造的是一致的,這是一個令人頭疼的問題。

我想大家都需要這麼一個平台,它可以定義和管理項目中的所有介面資源,還要有團隊協作的功能。有了介面定義,那介面文檔、Mock Server、介面測試等功能實現起來就會很容易。

所以,首先我們需要定義好介面,這樣前後端才能真正的並行開發:前端使用 Mock Server,後端按照介面定義實現,最後對接也只有切換環境(即 host 地址)的成本。至於 Mock Server ,需要提供渲染模板、請求路由映射、介面代理到其他環境等功能,也不會存在 URL 不一致的問題,這些 @張雲龍 都已經詳細地解釋過了,不再贅述。

業界也有不少的介面管理工具,也有開源的系統。網易杭研內部(也有很多兄弟部門)使用的是 NEI - 介面管理平台,它具備了以上所有功能。NEI 現在免費對外開放使用,之前一直只給內部人員使用,據說已經有了近 10 年的歷史。

和 NEI 平台配套的構建工具 GitHub - NEYouFan/nei-toolkit: NEI 介面管理平台配套自動化工具 集成了 Mock Server 的功能,使用方式可以看下這篇文章:nei-toolkit/使用NEI進行前後端並行開發.md at master · NEYouFan/nei-toolkit · GitHub,下面我介紹下大致的過程:

  1. 在 NEI 平台上定義好介面以及頁面
  2. 在 NEI 平台上創建自定義的工程規範(可以先研究下系統內置的規範是如何定義的)

  3. 使用構建工具生成腳手架文件和 Mock 數據。注意,此時會生成一份本地的介面 Mock 數據,方便開發的時候修改數據。也可以打開 server.config.js 裡面的 online 開關,使用 NEI 平台提供的在線 Mock 數據介面。
  4. 使用命令 `nei server` 啟動 Mock Server

細心的讀者已經發現,只要定義好了介面,Mock Server 就是自然而然的功能了。

但 Mock Server 並不是萬能的,回答中已經有用戶提到:

Mock Server並沒有顯著提高生產力。前後台分離的並行開發只是一種理想化的結果!

我覺得這要看項目進行到哪個程度,如果是在新項目的開始階段,生產力的提高還是挺明顯的,如果已經到了後期,由於業務邏輯的需要,單純的 Mock 數據必然會力不從心:介面之間的依賴、特定的發送數據需要返回預期的響應等等。也就是說,Mock Server 的功能是有限的。

除了 Mock Server,介面測試、用例管理、批量回歸測試等也是現在開發流程中不可缺少的功能。圍繞介面這個資源,我們還有很多功能可以挖掘。


數據量小的時候怎麼都好辦;

數據量大、且應用對數據要求較高時,造數據會花較長時間;

我一般用sinatra起個mock server,然後設置Access-Control-Allow-Origin、Access-Control-Allow-Methods。

在action里讀json、讀sqlite、讀網路,都是幾句話的事情。

這個階段就像大橋合龍,需要對接的無非就是名字的不一致、介面理解的不一致。

提前與後端開發人員約定協議的欄位名。

如果後端已經設計好了資料庫,最好把表結構要過來。

「名字一致」會使你的代碼簡化很多。否則代碼里就充斥各種if else、各種mapping、同一個東西的兩種描述。


最近也遇到了類似的問題, 並有了自己一些想法.

很多項目依然是前端編寫後端模板(比如我廠使用的Freemarker), 這時, 如果沒有後端的環境和數據, 就得乾等著了. 不過總有些工具可以幫到我們, 比如FMPP(FreeMarker-based file PreProcessor)http://fmpp.sourceforge.net/. 提供了前端編譯Freemarker模板到HTML的方法. 通過FMPP擺脫後端環境的束縛, 原理和後端渲染一樣, 也是HTML = Freemarker + Data. 這樣開啟一個靜態伺服器即可調試了.

非同步介面也是一樣, 大多數介面都是類似/rest/user/這種格式, 前端可以本地開啟伺服器請求mock數據, 當後端環境準備好以後, 可以不用修改代碼即可和後端對接, 上面很多答案提到了很多方法和工具. 我還是使用了express. 路由足夠用.

最後, 還有一些和後端無關的工作, 比如修改文件自動刷新, 以及後續的構建工作, 我們可以使用Gulp, 並將上述的FMPP和express也集成進Gulp里(這就是基於編程的Gulp的優勢吧).

最後數據怎麼來的, 自己編太費勁, 我廠內剛整了一套前端介面管理平台, 自帶數據模擬的功能, 配合Gulp的工作流, 簡直完美.

我根據一些場景, 編寫了一些gulpfile, 放到了Github上:zjzhome/Rapid-Dev-Activity-Page · GitHub. (最初只是做市場活動頁面時對於前端調試不滿而寫).

最後推薦下 @鄭海波 的puer: 超簡單工具puer——「低碳」的前後端分離開發, 開啟伺服器, 自動刷新, mock請求, 集成了weinre, 親測好用. gulp集成進express也是從puer受到啟發. 同時puer本身也提供api可以集成進gulp可以作為express的中間件.

以上.


分享下去哪兒的經驗,我們自研了 yapi介面管理平台 ,前端基於這個規範生成 mock數據,後端根據這個規範調試和測試介面

以下內容引用此文章:前端模擬介面數據(mock)實踐

前言

越來越多的公司將前端和後端徹底分離,以便能夠支持後端一套介面,提供給 web, ios, android 使用,大大提高了開發的效率。但與此同時,也帶來了前端 ui 依賴後端數據的問題,在後端的介面沒有開發完成之前,前端需要根據介面定義的規範模擬介面數據。這個問題看似簡單,但實際上在開發過程中,會是一個比較頭疼的問題。

以往的做法

有基於前端和後端兩種做法,前端大多數都是在業務代碼裡面先根據後端的介面定義,寫一套模擬數據,也有基於 mockjs 通過攔截 xhr 方式。後端大多是先寫一套測試數據的介面,提供給前端調用,等介面開發完成之後,在切換過來。無論是哪一種方式,都不可避免的有以下問題:

1.影響了業務的代碼,經常能見到下面這種代碼,注釋了原有的代碼邏輯,改成測試的模擬數據

getUserData = (uid) =&>{
//return axios.get("/api/user/" + uid);
return new Promise((resolve, reject)=&> {
setTimeout(()=&>{
return resolve({
data: {
errcode: 0,
data: {
uid: 1,
username: "tom"
}
}
})
}, 100)
})
}

2. 後端介面數據結構發生變動,增加欄位、修改欄位名稱了,無法及時反饋給前端開發者,更新滯後。

3. 不容易模擬複雜情況,例如介面響應時間,生成各類模擬數據,如郵箱、手機號等等

{
email: "abc@163.com",
phone: "18000803800"
}

4. 在實際的開發過程中,我們不僅需要模擬正常情況下 UI,還需要模擬數據出錯,數據為空時的 UI。

去哪兒前端介面 Mock 實踐

我們研發了 YApi介面管理平台 管理我們前端介面數據的模擬, 只需要前後端去維護在 YApi 平台定義的響應數據,就可以生成需要的模擬數據,下面這段代碼就是定義的生成數據模板:

{
"errcode": 0,
"errmsg": "@string",
"data": {
"type":"@pick(1,2,3)",
"list|1-10": [{
"uid": "@id",
"username": "@name"
}]
}
}

可生成如下的模擬數據:

{
"errcode": 0,
"errmsg": "^*!SF)R",
"data": {
"type": 2,
"list": [
{
"uid": "370000200707276255",
"username": "Ruth Clark"
},
{
"uid": "650000200211185728",
"username": "Anthony Martin"
},
{
"uid": "370000199201143855",
"username": "Laura Rodriguez"
},
{
"uid": "610000198704072775",
"username": "Anthony Perez"
}
]
}
}

使用方法

介面預覽頁面可看到 mock 地址,通過直接調用或者伺服器代理方式,就可獲取到隨機生成的數據,不會影響業務邏輯代碼

高級 Mock

基礎的 Mock 工具已經能滿足大部分的需求了,但有些複雜場景是無法實現的。例如:當我做一個數據列表頁面,需要測試某個欄位在各種長度下的 ui 表現,還有當數據為空時的 ui 表現。YApi 提供了期望和自定義腳本的功能。

Mock 自定義腳本

自定義腳本可根據請求的參數,cookie 信息,使用 js 腳本自定義返回的數據,推薦基於 cookie 生成不同的測試數據,這樣就能通過控制瀏覽器 cookie 值,獲取到不一樣的模擬數據。

if(cookie._type == "error"){
mockJson.errcode = 400;
}

if(cookie._type == "empty"){
mockJson.data.list = [];
}

Mock 期望

期望就是需要根據不同的請求參數、IP 返回不同的 HTTP Code、HTTP 頭和 JSON 數據,Mock 期望主要用於 ui 的自動化測設和後端介面自動化測試。

後記

YApi介面管理平台 已在去哪兒公司內部大面積使用,獲得了很多贊,為了讓 YApi 能夠服務更多小夥伴和使 YApi 變的更好,現已經開源到 https://github.com/ymfe/yapi,歡迎大家訪問 yapi.qunar.com 使用和提出寶貴的意見。


推薦閱讀:

有哪些函數式編程在前端的實踐經驗?
Vue.js 如何添加全局函數或變數?
什麼樣的前端技術 leader 是稱職的?
為什麼優酷在PC端瀏覽器依然用Flash?
為什麼現在很多網站都不用原生的input輸入,而是用div模擬?

TAG:前端開發 | 前端架構 | 前端工程化 |