1. KOA2—— Web server framework based on Node . js platform
Built by the original team of Express
Both Express Koa and Koa 2 are frameworks for web servers , and the differences and relationships between them can be expressed in the table below
framework name |
effect |
asynchronous processing |
Express |
web framework |
callback function |
So |
web framework |
Generator+yield |
Koa2 _ |
web framework |
async/await |
The environment depends on Node v7.6.0 and above
Since Koa2 supports async and await , it has requirements for the version of Node . It requires the version of Node to be at least level 7.6 , because the syntactic sugar async and await is only supported after Node 7.6
1.1 Middleware of the onion model
As shown in the figure below , for the server, it is actually to process one request after another. After the web server receives one request after another sent by the browser , it forms one response after another and returns it to the browser . The request to reach our server needs to be processed by the program . After the program is processed, a response will be formed and returned to the browser. The program that our server processes the request is called middleware in the world of Koa 2 .
This kind of middleware may not only have one, but there may be multiple ones. For example, as shown in the figure above , it has three The process of processing requests and the order of its calls are as follows :
When a request arrives at our server, the first layer of middleware is the first to process the request
After the first-tier middleware processes the request, it will pass the request to the second-tier middleware
After the second-tier middleware processes the request, it will send the request to the third-tier middleware
There is no middleware inside the third- tier middleware , so after the third-tier middleware has processed all the codes, the request returns
It will reach the second-level middleware, so the second-level middleware has processed this request twice
After processing the request, the middleware of the second layer reaches the middleware of the first layer , so the middleware of the first layer also responds to this request.
requests were processed
This call sequence is the onion model . The middleware has a first-in-last-out feeling in processing requests. The request first reaches the first-level middleware , and finally the first-level middleware processes the request again.
2. Quick start of KOA2
Check the version of Node
The node - v command can help us check the version of Node . The use of Koa 2 requires Node version 7.6 and above
Install Koa 2
This command can quickly create a package.json file , which can maintain the information of the third-party package in the project
npm install koa
This command can download the latest version of koa online to the current project . Since the latest version of koa online is koa 2 , we don't need to execute npm install koa 2
Write the entry file app.js and create an instance object of Koa
// 1. Create koa object _ const Koa = require ( ' koa ' ) // import constructor const app = new Koa () // Create an instance object through the construction method |
Write a response function ( middleware )
The response function can only be effective through use . This function has two parameters , one is ctx and the other is next ctx :
Context refers to the web container where the request is located . We can get the request object through ctx.request , or get the response object through ctx.response
next : the entry point for inner middleware execution
// 2. Write the response function ( middleware ) app.use((ctx, next) => { console.log(ctx.request.url) ctx.response.body = 'hello world' }) |
Specify the port number: You can specify a port number through app.listen
// 3. Bind port number 300 0 app.listen(3000) |
Start the server: You can start the server through node app . js
Then open the browser , enter 127.0.0.1:3000/ in the browser, you will see the string of hello world appear in the browser , and you can also see the requested url in the terminal of the server
3. Features of KOA2middleware
The instance object of Koa 2 adds a middleware through the use method. A middleware is a function . This function has two parameters , namely ctx and next . The execution of middleware conforms to the onion model. Whether the inner middleware can be executed depends on whether the next function of the outer middleware is called. Call the next function to get the Promise object . If you want to get the data wrapped by the Promise , you can combine await and async
app.use(async (ctx, next) => { // Just enter what the middleware wants to do await next() // What you want to do after all the middleware in the inner layer ends }) |
4. Development of background
4.1. The goal of the background project
1. Calculate the total time spent by the server processing the request
Calculate the total time spent by the server on all middleware for this request
2. Add the mime type of the response content to the response header
Adding the mime type allows the browser to better handle the data returned by the server .
If the response to the front-end browser is data in json format, then we need to add Type in our response header and its value is application/json , which is the mime type of json data type
3. Read the content of the file in the specified directory according to the URL
In order to simplify the code of the background server, the data required by the front-end chart does not exist in the database , but will be stored in the file. This operation is only to simplify our background code . So we need to read a certain directory The content of the file below .
Each goal is a function that a middleware needs to implement , so there need to be three middleware in the background project
4.2. Development steps of the background
Create a new folder called koa_server , which is the folder of the background project
1. Installation package
npm init -y
npm install koa
2. Create the file and directory structure
Code directory structure
app.js is the entry file of the background server
The data directory is used to store the json file data of all modules
middleware is used to store all middleware code
koa_response_data.js is business logic middleware _ _ _
koa_response_duration.js is a middleware for calculating server processing time
koa_response_header.js is a middleware used to specifically set the response header
Then copy the json data files of each module to the data directory , and then write the code in the app.js file as follows :
// server entry file // 1. Create an instance object KOA const Koa = require ( ' koa ' ) const app = new Koa ( ) // 2. Bind middleware // Bind the first layer middleware // Bind the second layer middleware // Bind the third layer middleware // 3. Bind port number 888 8 app.listen(8888) |
4.3. Total time-consuming middleware
1. Layer 1 middleware
The function of the total time-consuming middleware is to calculate the total time-consuming of all middleware on the server, which should be located in the first layer, because the middleware in the first layer is the middleware that processes the request first When entering our middleware for the first time, record a start time. After all other middleware are executed, record the end time and subtract the two to get the total time.
3. Set the response header
将计算出来的结果,设置到响应头的 X-Response-Time 中, 单位是毫秒 ms
具体代码如下:
app.js
// 绑定第一层中间件 const respDurationMiddleware = require('./middleware/koa_response_duration') app.use(respDurationMiddleware) |
koa_response_duration.js
// 计算服务器消耗时长的中间件 module.exports = async (ctx, next) => { // 记录开始时间 const start = Date.now() // 让内层中间件得到执行 await next() // 记录结束的时间 const end = Date.now() // 设置响应头 X-Response-Time const duration = end - start // ctx.set 设置响应头 ctx.set('X-Response-Time', duration + 'ms') } |
4.4 响应头中间件
1.第2层中间件
这个第2层中间件没有特定的要求
2.获取 mime类型
由于响应给前端浏览器当中的数据都是 json 格式的字符串,所以 mime 类型可以统一
的给它写成 application/json , 当然这一块也是简化的处理了,因为 mime 类型有几十几百 种,,所以这里简化处理一下
3.设置响应头
响应头的key是 Content-Type ,它的值是 application/json , 顺便加上 charset=utf-8
告诉浏览器,我这部分响应的数据,它的类型是 application/json ,同时它的编码是具体代码如下:
app.js
// 绑定第二层中间件
const respHeaderMiddleware = require('./middleware/koa_response_header')
app.use(respHeaderMiddleware)
koa_response_header.js
// 设置响应头的中间件
module.exports = async (ctx, next) => {
const contentType = 'application/json; charset=utf-8'
ctx.set('Content-Type', contentType)
await next()
}
4.5 业务逻辑中间件
1.第3层中间件
第三层中间件处理实际的业务逻辑,处理前端请求并返回
2.读取文件内容
获取 URL 请求路径
const url = ctx.request.url |
根据URL请求路径,拼接出文件的绝对路径
let filePath = url.replace('/api', '') filePath = '../data' + filePath + '.json' filePath = path.join(__dirname, filePath) |
这个 filePath就是需要读取文件的绝对路径
读取这个文件的内容
使用 fs模块中的 readFile方法进行实现
3.设置响应体
ctx.response.body
具体代码如下:
app.js
// 绑定第三层中间件 const respDataMiddleware = require('./middleware/koa_response_data') app.use(respDataMiddleware) |
koa_response_data.js
// 处理业务逻辑的中间件 ,读取某个json文件的数据
const path = require('path')
const fileUtils = require('../utils/file_utils')
module.exports = async (ctx, next) => {
// 根据url
const url = ctx.request.url // /api/seller ../data/seller.json
let filePath = url.replace('/api', '') // /seller
filePath = '../data' + filePath + '.json' // ../data/seller.json
filePath = path.join(__dirname, filePath)
try {
const ret = await fileUtils.getFileJsonData(filePath)
ctx.response.body = ret
} catch (error) {
const errorMsg = {
message: '读取文件内容失败 , 文件资源不存在', status: 404
}
ctx.response.body = JSON.stringify(errorMsg) }
console.log(filePath)
await next()
}
file_utils.js
// 读取文件的工具方法
const fs = require('fs')
module.exports.getFileJsonData = (filePath) => {
// 根据文件的路径, 读取文件的内容
return new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf-8', (error, data) => {
if(error) {
// 读取文件失败
reject(error)
} else {
// 读取文件成功
resolve(data)
}
})
})
}
4.6 允许跨域
设置响应头koa_response_header.js 添加
// 设置响应头的中间件
module.exports = async (ctx, next) => {
const contentType = 'application/json; charset=utf-8'
ctx.set('Content-Type', contentType)
ctx.set("Access-Control-Allow-Origin", "*")
ctx.set("Access-Control-Allow-Methods", "OPTIONS, GET, PUT, POST, DELETE")
await next()
}
5.引入webScoket
WebSocket可以保持着浏览器和客户端之间的长连接, 通过 WebSocket可以实现数据由后端推送到前 端,保证了数据传输的实时性. WebSocket涉及到前端代码和后端代码的改造
5.1. WebSocket 的使用
安装 WebSocket包
npm i ws -S
创建 service\web_socket_service.js 文件
创建 WebSocket实例对象
const WebSocket = require('ws')
// 创建WebSocket服务端的对象, 绑定的端口号是9998
const wss = new WebSocket.Server({
port: 9998
})
wss.on("connection", client => {
console.log("有客户端连接 ...")
client.on("message", msg => {
console.log("客户端发送数据过来了")
// 发送数据给客户端
client.send('hello socket')
})
})
监听事件
在 app.js 中引入 web_socket_service.js 这个文件, 并调用 listen 方法
const webSocketService = require('./service/web_socket_service')
webSocketService.listen()
5.2 约定客户端之间数据交互格式
客户端和服务端之间的数据交互采用 JSON 格式
客户端发送数据给服务端的字段如下:
{
"action": "getData",
"socketType": "trendData",
"chartName": "trend",
"value": ""
}
或者
{
"action": "fullScreen",
"socketType": "fullScreen",
"chartName": "trend",
"value": true
}
或者
{
"action": "themeChange",
"socketType": "themeChange",
"chartName": "",
"value": "chalk"
}
其中:
action : 代表某项行为,可选值有
- getData 代表获取图表数据
- fullScreen 代表产生了全屏事件
- themeChange 代表产生了主题切换的事件
socketType : 代表业务模块类型, 这个值代表前端注册数据回调函数的标识, 可选值有:
- trendData
- sellerData
- mapData
- rankData
- hotData
- stockData
- fullScreen
- themeChange
chartName : 代表图表名称, 如果是主题切换事件, 可不传此值, 可选值有:
- trend
- seller
- rank
- stock
value : 代表 具体的数据值, 在获取图表数据时, 可不传此值, 可选值有
如果是全屏事件, true代表全屏, false代表非全屏
如果是主题切换事件, 可选值有 chalk或者 vintage
服务端发送给客户端的数据如下:
{
"action": "getData",
"socketType": "trendData",
"chartName": "trend",
"value": "",
"data": "从文件读取出来的json文件的内容"
}
或者
{
"action": "fullScreen",
"socketType": "fullScreen",
"chartName": "trend",
"value": true
}
或者
{
"action": "themeChange", "socketType": "themeChange", "chartName": "",
"value": "chalk"
}
注意, 除了 action为 getData 时, 服务器会在客户端发过来数据的基础之上, 增加 data字段, 其他的情况, 服务器会原封不动的将从某一个客户端发过来的数据转发给每一个处于连接状态 的客户端
5.3.代码实现 service\web_socket_service.js
const path = require('path')
const fileUtils = require('../utils/file_utils')
const WebSocket = require('ws')
// 创建WebSocket服务端的对象, 绑定的端口号是9998
const wss = new WebSocket.Server({
port: 9998
})
// 服务端开启了监听
module.exports.listen = () => {
// 对客户端的连接事件进行监听
// client:代表的是客户端的连接socket对象
wss.on('connection', client => {
console.log('有客户端连接成功了...')
// 对客户端的连接对象进行message事件的监听
// msg: 由客户端发给服务端的数据
client.on('message',async msg => {
console.log('客户端发送数据给服务端了: ' + msg)
let payload = JSON.parse(msg)
const action = payload.action
if (action === 'getData') {
let filePath = '../data/' + payload.chartName + '.json'
// payload.chartName // trend seller map rank hot stock
filePath = path.join(__dirname, filePath)
const ret = await fileUtils.getFileJsonData(filePath)
// 需要在服务端获取到数据的基础之上, 增加一个data的字段
// data所对应的值,就是某个json文件的内容
payload.data = ret
client.send(JSON.stringify(payload))
console.log(payload.data)
} else {
// 原封不动的将所接收到的数据转发给每一个处于连接状态的客户端
// wss.clients // 所有客户端的连接
wss.clients.forEach(client => {
console.log("客户端触发"+action+"事件")
// client.send(msg)
payload.data = JSON.parse(msg)
client.send(JSON.stringify(payload) )
})
}
// 由服务端往客户端发送数据
// client.send('hello socket from backend')
})
})
}