当前位置:网站首页>Koa middleware implementation
Koa middleware implementation
2022-06-24 05:55:00 【Uncertainty】
We introduced it earlier ,Koa The core of is the middleware mechanism , The service is the same . The middleware determines the execution order from top to bottom , We can do our own operations such as authority authentication before routing , Share with me koa Implementation of several middleware , That is the use The callback function of is proposed separately to rewrite , Because we pass parameters , So it doesn't return a function directly , It is a Higher order function .
static middleware
This function is used to read the local static file , Provide a directory , When the service is started, you can directly access the internal files in the browser , Mainly used fs modular
const path = require('path') // Path processing
// fs Method can be used directly promise call , Let's share next time node Implementation of internal chain function
// If it helps you , You can like it 、 Pay attention to ha
const fs = require('fs').promises
// Higher order function return function ( Closure ), The exported function can pass parameters . If you export directly async(ctx, next) => {} There is no expansion operation
module.exports = function static(staticPath) {
return async (ctx, next) => {
// The path entered may not exist
try {
// Splicing , There may be... In the path / ,node The default root path , Use here join
let filePath = path.join(staticPath, ctx.path)
// Don't use it here fs.exists Judge the file , Use stat perhaps lstat
let statObj = awiat fs.stat(filePath)
if (statObj.isDirection()) {
// By default, the folder accesses the internal index.html
filePath = path.join(filePath, 'index.html')
}
It can be in the form of flow , Here is the direct reading
ctx.body = await fs.readFile(filePath, 'utf-8')
} catch(error) {
return await next()
}
}
}- Use
app.use(static(__dirname))
http://localhost:3000/form.html
koa-rotuer middleware
Let's take a look at the official use
const Router = require('koa-router')
// Exported is a class
let router = new Router()
// Yes get post Method, etc.
router.get('/login', async (ctx, next) => {
await next()
})
// A higher order function
app.use(router.routes()) Realize our demo edition
// Base frame
class Router {
constructor() {}
get(path) {}
routes() {
return async (ctx, next) => {
}
}
}When we use routing a method, we can call... Multiple times
router.get('/', fn)
router.get('/', fn2)
router.get('/', fn3)
router.post('/', fn1)
So we think of using stack storage , When executing, traverse , We think of the execution mechanism of the onion model , Finally, unified call . Then what is stored in the stack is an object
{
path: '',// Traversal execution to match
callback: , // Match to be executed in sequence
method: , // The same path may have different methods
}
Here we use the form of class rewrite
// Define the format of a storage object
// Simply use Objects can be
class Layer {
constructor(path, method, callback) {
this.path = path
this.method = method
this.callback = callback
}
// Filter the current match from the stack . Method calls are bound to themselves , Avoid exposing attributes , Poor expansibility , Just expose a method to pass parameters , In case of special circumstances, directly in This method can be added internally
match(path, method) {
return this.path == path && this.method == method.toLowerCase()
}
}
class Router {
constructor() {
// Store all routes , Filter when executing
this.stack = []
}
compose(layers, ctx, next) {
const dispatch = i => {
if (i === layers.length) reutrn next()
let cb = layers[i].callback
return Promise.resolve(cb(ctx, () => dispatch(i + 1)))
}
return dispatch(0)
}
// In the entrance call
routes() {
// Real middleware execution
return async (ctx, next) => {
let path = ctx.path
let method = ctx.method
// Screening
let layers = this.stack.filter(layer => {
return layer.match(path, method)
})
// Filter out the layer 了 , Follow the middleware execution pattern , Execute sequentially
this.compose(layers, ctx, next)
}
}
}
// Here you can download methods library , It contains All method names , You don't have to configure methods in classes alone , Because the internal structure is the same . Direct circulation
['get', 'post', ...].forEach(method => {
Router.prototype[method] = function(path, callback) {
let layer = new Layer(path, method, callback)
this.stack.push(layer)
}
})body-parser middleware
get Request that we all know from url Get parameters in , Use url.parse After the parsing , Directly in query Parameters in , Use ctx.query Can get . about post This parameter is used in body In the form of , We need to read... As a stream buffer Make a splice , And for the form of uploading pictures , And understand mulpart/form-data This form .
Submission format type
Briefly introduce the following common types
- application/x-www-form-urlencoded
Form data transferred
- application/json
What is passed is ordinary json data
- mulpart/form-data
Upload files
Content-Type: multipart/form-data; boundary= Your customization boundary
Sandwiched between the separator is the transmitted data , Remember ours form The form submission will have a name attribute
<input name="username" /> , Match found username The corresponding value is converted to object form
Some head types are
application/json;charset=utf-8, There is a description after the semicolon , For the convenience of judging the data type , We use head matching to judgestartsWith
receive data
Use what we wrote above static middleware , Visit a simple local page , Write a form , Prepare for the back , Let's write a post Request ordinary objects
script Execute our request directly in the script
fetch('/json', {
method: 'post',
headers: {
// The default is text/plain
'content-type': 'application/json'
},
body: JSON.stringify({a: 456})
}).then(res => res.json()).then(data => {
console.log(data)
})Server receives data
router.post('/json', (ctx, next) => {
let arr = []
// Native data listening is used here , Flow situation
ctx.req.on('data', (chunk) => {
// Binary form
arr.push(chunk)
})
ctx.req.on('end', () => {
console.log(Buffer.concat(arr).toString())
})
ctx.body = '456'
})Interface printing
But the delivery of our listener stream is asynchronous , When we return ctx.body when , I haven't got , So it needs to be changed to await promise situation , Then we aim at different content-type, Different sets of data processing , Take what you get body The value in , Bound to the ctx.req.body On , In this way, the middleware executed later can obtain .
Realization bodyParser middleware
The use of comments is marked in the code , You can look down from the top , It should be well understood
// dir If you send a file Storage directory
function bodyParser({ dir } = {}) {
return async (ctx, next) => {
// Synchronous processing returns results , It's good to assign values , The middleware in the back can be obtained
ctx.request.body = await new Promise((resolve, reject) => {
// obtain Data flow on data get data
let arr = []
ctx.req.on('data', function(chunk){
arr.push(chunk) // chunk buffer data
})
// Data received , Try not to use Native req, res operation
ctx.req.on('end', () => {
// Get the data format passed by the user Content-Type
let type = ctx.get('content-type') // ctx It's packaged in , perhaps res.headers['content-type']
// assemble data
let body = Buffer.concat(arr)
// Data type judgment
if (type === 'application/x-www-form-urlencoded') {
// Common form data
// Set the response header data type The settings returned according to the actual situation
ctx.set('Content-Type', 'application/json;charset-utf-8')
// Return to object , querystring node The built-in library comes with
resolve(querystring.parse(body.toString()))
} else if (type.startsWith('text/plain')) {
// Text type
resolve(body.toString())
} else if (type.startsWith(application/json)) {
resolve(JSON.parse(body.toString()))
} else if (type.startsWith('multipaer/form-data')) {
// We introduced above multipaer Type of request data format , Use boundary Division , The head and body of each group are divided in \r\n\r\n Do the segmentation (http Under the agreement , We can directly find and replace ) ( Look at the figure below, our page and node Received results )
let boundary = '--' + type.split('=')[1]
// Get number of groups . This is because of our body yes buffer Format ,buffer No built-in split Method , We need to expand something similar Array of split Method , Look below
let lines = body.split(boundary).slice(1, -1) // break off both ends , After printing, you can see , Marking the beginning and end has no practical significance
// Define the data we want to get
let formData = {}
lines.forEach(line => {
let [head, body] = line.split('\r\n\r\n')// The prescribed division method
head = head.toString()
// We can see from the screenshot below The format is name=xxx
let key = head.match(/name="(.+?)"/)[1]
// file
if (head.includes('filename')) {
// If you receive file , We save it on the server
// Get file content
let content = line.slice(head.length + 4, -2) // +4 because \r\n\r\n Segmented , Remove the tail \r\n -2
// Create upload directory The directory can be uploaded during execution , Default upload Catalog
dir = dir || path.join(__dirname, 'upload')
// Randomly generate file names
let filePath = uuid.v4() //uuid Third party Library Generate random
let uploadUrl = path.join(dir, filePath)
fs.writeFileSync(uploadUrl, content)
formData[key] = {
filename: uploadUrl,
size: content.length,
..... You can also add the required attributes
}
} else {
let value = body.toString()
// Get rid of the back \r\n
formData[key] = value.slice(0, -2)
}
})
resolve(formData)
} else {
// The default is null
resolve({})
}
})
})
await next() // Continue to execute the following middleware , The value of the request body has been stored
}
}
// expand buffer Of split Method
Buffer.prototype.split = function(sep) { // Separator
let arr = []
let offset = 0
// Separator may be Chinese , Or special symbols , So we unified into buffer To obtain the length of the
let len = Buffer.from(sep).length
// Use indexof obtain sep The location of , Put it in the array , Returns an array of
while (-1 !== (index = this.indexOf(sep, offset))) { // indexOf The second parameter identifies where to start the search , Not every time from the index 0 Go back and forth
arr.push(this.slice(offset, index))
offset = index + len
}
// The last paragraph may not have a separator Put as much as you have left a|b|c discharge c
arr.push(offset)
return arr
}( Print body.toString())
This article shares three koa Common middleware in , In fact, the forms of middleware are universal , Higher order functions return , It's all simple versions , If you are interested, you can look at the source code and learn more about . Next time I plan to share with you express Implementation mechanism , More complicated , I will sort it out smoothly and then write an article to share with you . If you have any questions about this article, you can comment and leave a message .
If you are interested, you can pay attention to bo !
边栏推荐
- Typora software installation
- How to build a website with a domain name? What are the precautions for website construction?
- Interpretation of PNG files (1): past and present lives of png/apng format
- [dry goods] flexible force control grinding tools promote the "smart" transformation of automatic grinding production!
- Oceanus practice consumption CMQ subject model data source
- The instrument industry builds the supplier SRM mode to manage the development of manufacturers + users
- How to apply for a domain name? Why should domain names be filed in advance?
- Micro build low code supports PC application construction
- test
- Script updates CLB type ingress Certificate in tke cluster
猜你喜欢
随机推荐
How to build a website after successfully registering a domain name? Can I build a website without registering a domain name?
MySQL optimization
The basic concept of network is the relationship among services, protocols, processes and ports.
How to build a website after registering a domain name? Can individuals register domain names to build websites?
Load balancing on Tencent cloud
What is domain name registration? Do you still need to purchase ECS after domain name registration?
Spirit breath development log (6)
How to register a secondary domain name? What are the precautions when registering?
Sub process call - process arrangement in complex O & M scenarios
Tencent Anxin platform was officially released, and the "Anxin agricultural product plan" was launched at the same time, supporting the growth of 100 landmark agricultural product brands in three year
What is a domain name? How to use a domain name?
Realization of data transmission between a and B computers by using single chip microcomputer serial port
Supply chain innovation of industrial Internet -- supply chain +5g Technology
Hacking with Golang
How to resolve the primary domain name and how to operate it
How to build a website after registering a domain name? Do you need maintenance later?
How to buy a website domain name? How to choose a website domain name?
It is necessary to do the industry of waiting insurance evaluation. Let's see if you are on the list
"Yi Jian Xing" was "Internet stormed". What countermeasures does the game company have other than "rather be broken jade"?
How to apply for web domain name what is the role of domain name