preface
This article is mainly for the front-end development engineers to explain how to use Node.js Framework Koa2 and mockjs build a set of data simulation services to provide simulation data for front-end services. Simple operation can greatly improve development efficiency.
Problems encountered in actual combat
- Scene 1: schoolmate A is happily writing CSS, and suddenly the interface is all 404. When he asks schoolmate B, he finds that schoolmate C secretly deploys the code of back-end branch C to the development environment. The branch A code of the original development environment is covered. Only the code of back-end branch A (small B) can provide the docking interface for students of small A.
- Scenario 2: little A wrote JS logic happily according to the data returned by little B. suddenly, the interface reported 500 and told the internal exception, and returned it to the back-end little B. little B calmly replied, "let me have A look.". The logical thinking just now was interrupted by the 500.
- Scene 3: little A gets A list rendering interface and returns everything is normal, just "list: []". So little A asked little B to add some data to the interface. Little B saw that each record had to add more than 30 data. There were more than 10 interfaces and more than 10 bug s that had not been changed. The most important thing was that it was only 5 minutes before 6:00. So little B replied to an expression pack
Little A has no choice but to send the expression bag below to solve the problem
All the above scenarios can be solved through mockserver. Let's get straight to the point.
Server introduction
The Server is built with Koa2. First look at the project structure
|-- app |-- controller // controller |-- route // route |-- validation // Parameter verification with joi |-- config |-- config.js // Project configuration |-- logs // journal |-- middleware // middleware |-- util // Public tools library |-- app.js // Project start up file |-- package.json // node project file description
Then let's pick a few key points
const Koa = require('koa'); const app = new Koa(); const config = require('./config/config.js'); var cors = require('koa-cors'); app.use(cors()); /** * Add message resolution Middleware */ const bodyParser = require('koa-bodyparser'); const xmlParser = require('koa-xml-body'); app.use(xmlParser()); app.use(bodyParser()); /** * Middleware for processing input message */ app.use(require('./middleware/input.js')); /** * Handle header Middleware */ app.use(require('./middleware/header.js')); /** * Request callback Processing Middleware */ app.use(require('./middleware/requestError.js')); /** * Load route * Route configuration in config/config.js In routes array */ let routes = config.routes; for (let key in routes) { if (routes.hasOwnProperty(key)) { let element = routes[key]; let router = require(element)(); app.use(router.routes()); } } app.listen(config.port); module.exports = app;
Koa2 introduces itself as an onion model. It uses context to package Request parameters, processes data through a layer of middleware, and then returns response data. Let me introduce the focus of this framework.
route
Koa router middleware is used in routing. An example folder is created in the route folder, corresponding to a front-end project. And then config.js Plus configuration. In this way, multiple projects can be managed in the form of folders.
// config/config.js /** * Apply route configuration to load */ "routes": [ './app/route/example/index.js' ] // app.js /** * Load route * Route configuration in config/config.js In routes array */ let routes = config.routes; for (let key in routes) { if (routes.hasOwnProperty(key)) { let element = routes[key]; let router = require(element)(); app.use(router.routes()); } }
When we need to add a corresponding mock interface, the following code
// app/route/example/index.js const Router = require('koa-router'); const activityController = require('../../controller/example/activity.js'); module.exports = function() { let router = new Router(); // Get record list router.get('/activity-parcel-service/parcel/lottery/winners/red', activityController.getResultList); }
router's methods follow the restful api, mainly including the following
get, post, put, delete
router takes two parameters: path (url) and multiple Middleware
activityController is a controller. getDefaultActivity is a method in the controller, and it is also a middleware. The controller will be mentioned later
controller
I created a new example project in the controller folder. In this project, I created a new project named activity.js In this file, a method named getResultList is written.
const response = require('../../../util/response.js'); const validator = require('../../../util/requestValidator.js'); const activityValidation = require('../../validation/example/activity.js'); const Mock = require('mockjs'); module.exports = { /** * @methods Get results page * @param {Object} ctx koa context **/ getResultList: async function(ctx, next) { // Judge whether the interface has an id await validator.validate( ctx.input, activityValidation.getResultList.schema, activityValidation.getResultList.options ) const Random = Mock.Random; Random.word() Random.name() let data = Mock.mock({ 'describeList|1-5': ['@word'], 'parcelRecordRedWinnerVoList|1-10': [{ 'avatarUrl': Random.image('100x100'), // If you want to specify a size for image 'bonusAmount|1-2.1-2': 1, 'nickname': '@name' }] }) return response.map(ctx, data); } }
As you can see from the code, we reference mockjs to the controller layer. After accessing the MockServer interface, first go to the controller layer through routing, and then process the logic return information.
The usage of mockjs is explained below.
The main purpose of the validator is to verify the parameters brought by the front-end request, because the interface itself needs to pass a parameter "id", and then a layer of processing is done through the validator. If the id is not legal, an exception will be thrown, indicating the following information.
<img src='https://images.yatsenglobal.com/uploads/20200610/339204068389621.png' />
Global exception capture
The reason why parameter errors can pop up above is that there is a middleware for global exception capture
/** * Request callback Processing Middleware */ app.use(require('./middleware/requestError.js'));
const response = require('../util/response.js'); module.exports = async function (ctx, next) { try { await next(); } catch(e) { ctx.status = status; let errCode = e.code || 1; let msg = e.message || e; if (!(parseInt(errCode) > 0)) { errCode = 1; } return response.output(ctx, {}, errCode, msg, status); } }
When we throw an exception, it will be caught and processed by the middleware, and the error information we need to throw will be returned to the front end.
Mockjs
Official website address: http://mockjs.com
advantage
Directly intercept the official website map
<img src='https://images.yatsenglobal.com/uploads/20200609/339202893984459.png' />
usage
Because of laziness, here I only talk about a few daily uses. For details, please see the official website
character string
'name|min-max': string
Mock.mock({ "string|1-10": "str" }) // "str" will be copied 1-10 times { string: "strstrstr" }
number
'name|min-max': number
Mock.mock({ "number|1-10": 1 }) // Returns the random number of objects 1-10 { number: 9 }
picture
Random.image( size?, background?, foreground?, format?, text? )
Size: image size, such as "100x100" Background: background color foreground: font color Format: format png|jpg text: text
object
'name|min-max': object
Mock.mock({ "data|2-4": { "id": 1, "id": 2, "id": 3, "id": 4 } }) // Return 2-4 defined elements in the object { id: 1, id: 2 }
array
'name|min-max': array
Mock.mock({ "array|1-4": [ { "name|+1": [ "Hello", "Mock.js", "!" ] } ] }) // Return 1-4 elements defined in the array { "array": [ { "name": "Hello" }, { "name": "Mock.js" } ] }
The above briefly introduces several types of mockjs, and then explains how to use mockserver.
How to use in a project
mockserver start
Step 1: download the project mockserver
https://github.com/FEA-Dven/m...
Step 2: install pm2 globally. pm2 is mainly used to manage node processes
cnpm install pm2 -g or npm install pm2 -g
Step 3:
cd mockserver & npm install
Step 4:
Add the URL in the routing file. The URL is the URL used in the actual project. Here I use the URL of the red packet project
const Router = require('koa-router'); const activityController = require('../controller/example/activity.js'); module.exports = function() { let router = new Router(); // Get default activity configuration router.get('/activity-parcel-service/parcel/lottery/getDefaultActivityId', activityController.getDefaultActivity); }
Step 5:
Add a file in the controller, and each file corresponds to the front-end project. Here I use example instead. I create a new example folder in the controller, and add a activity.js Documents of
const response = require('../../../util/response.js'); const Mock = require('mockjs'); module.exports = { /** * @methods Get default activity */ getDefaultActivity: async function(ctx, next) { // Parameters passed by the front end can be verified Mock.Random.id(); let data = Mock.mock({ 'dataStatus|0-1': 0, 'id': '@id', 'styleType': 1 }) return response.map(ctx, data); } }
Step 6:
Return to the root directory and use pm2 to turn on listening mode
pm2 start app.js --watch
pm2 usage
- Open pm2 process list
pm2 list
- View service log
PM2 logs 0 / / 0 represents the process ID
Using postman to request our URL will return MOCK data
<img src='https://images.yatsenglobal.com/uploads/20200610/339203858674326.png' />
Now that the service is set up, it needs to be connected with the front-end service
Apply to daily development
Let's take a look at our original encapsulation request function
// utils.request.js /** * @function Wechat request method encapsulation * @param {string} method Request type * @param {string} host Domain name address * @param {string||array} url Interface address * @param {object||array} data Parameter data * @param {boolean} showModal Display error pop-up * @return {object} Requested data */ export const requestFn = ({ method = 'GET', host = wx.$CONFIG.API_URL, url = '', data = {}, token = true, header = { 'content-type': 'application/json' }, showLoading = false, // Chrysanthemum or not showModal = false // Pop up or not }) => { return new Promise((resolve, reject) => { // Show loading animation if (showLoading) wx.showLoading(); // token verification if (token) { let _token = wx.$USER.getToken(); if (_token) { header[wx.$CONFIG.TOKEN_CACHE] = _token } else { return; } } // Start request wx.request({ method: method.toUpperCase(), url: host + url, // Global variable host header, data, success: res=> { resolve(res.data); }, fail: err => {} }) }) }
We see that there is a variable host in it. As long as you change the variable host to point to our local mock domain name, you can finish work.
Of course, I lied to you
In general, small and medium-sized projects, there will be dozens of interfaces, thousands of large-scale interfaces. If the host is directly changed to our Mock domain name, wouldn't our mockserver route also need to write hundreds of routing interfaces? This is an anti human operation. So we need to optimize the changes
Front end requests encapsulation changes
When requesting a service interface, we all have an api file. First, look at the original code
/** * Get default activity id */ function getDefaultActivityId (data = {}) { return requestFn ({ url: '/activity-parcel-service/parcel/lottery/getDefaultActivityId', method: 'get', data, showLoading: false, showModal: false }) }
Here we need to add a parameter to tell the encapsulation request file that the api needs to mock data, so we add a "isMock" variable
/** * Get default activity id */ function getDefaultActivityId (data = {}, isMock) { return requestFn ({ url: '/activity-parcel-service/parcel/lottery/getDefaultActivityId', method: 'get', data, showLoading: false, showModal: false, isMock }) }
Wechat request method encapsulation file should also be changed
// utils.request.js // Head definition MOCK_HOST const MOCK_HOST = 'http://10.154.68.180:8080'; /** * @function Wechat request method encapsulation * @param {string} method Request type * @param {string} host Domain name address * @param {string||array} url Interface address * @param {object||array} data Parameter data * @param {boolean} showModal Display error pop-up * @return {object} Requested data */ export const requestFn = ({ method = 'GET', host = wx.$CONFIG.API_URL, url = '', data = {}, token = true, header = { 'content-type': 'application/json' }, showLoading = false, // Chrysanthemum or not showModal = false, // Pop up or not isMock }) => { return new Promise((resolve, reject) => { // Show loading animation if (showLoading) wx.showLoading(); // token verification if (token) { let _token = wx.$USER.getToken(); if (_token) { header[wx.$CONFIG.TOKEN_CACHE] = _token } else { return; } } // Judge whether it is necessary to turn on the analog parameter form to return data host = `${isMock ? MOCK_HOST : host}`; // Start request wx.request({ method: method.toUpperCase(), url: host + url, header, data, success: res=> { resolve(res.data); }, fail: err => {} }) }) }
When calling the API, the parameter isMock is passed as true
wx.$API.getDefaultActivityId({ ...otherParams, isMock: true })
Look at the effect:
<img src='https://images.yatsenglobal.com/uploads/20200610/344271727146515.png' />
Comparison of other schemes
YApi
YApi, as a powerful API document management tool, can also use mock data naturally. If you want to use YApi for mock data, the simplest method is intranet deployment
The address is as follows
https://hellosean1025.github....
In my opinion, if you only need to use mock, YApi is too large and cumbersome, and you need to rely on mongodb environment, as well as account login. If you use your own built mockserver, you only need nodejs installation dependency to run.
Other students have better plans welcome to exchange
malpractice
After all, there are still some disadvantages of mockserver. Let's summarize.
- Need to wait for back-end students to provide return data fields and types
- I need to know nodejs to some extent, but I believe that all of you are good students who love learning.
summary
The front-end engineer builds a set of mockserver by himself. After defining the return data structure, he can develop in parallel with the back-end, and simulate the whole process by himself. In the process, he can find and solve problems in advance, complete the front-end and back-end joint debugging faster, and speed up the measurement and online.