Read the koA source code, only to find that it is so simple, really achieve a small and beautiful framework. In a word, amazing!
Koa is small in that it is simple to provide Web services directly using nodeJS native HTTP modules.
The beauty of KOA is that complex functionality is implemented through middleware (the middleware is connected through the context variable CTX);
Native HTTP services versus KOA
Native HTTP service
let http = require('http')
let server = http.createServer((req, res) = >{
res.end('hello world.')
})
server.listen(3000)
Copy the code
koa
const Koa = require('koa')
const app = new Koa()
app.use((ctx, next) = >{
ctx.body = 'hello world.'
})
app.listen(3000)
Copy the code
In the USE function, koA collects the middleware;
In the LISTEN function, KOA calls http.createserver to create the Web Server service (handling the context variable CTX in its callback, handling calls to the middleware);
The code structure
├─ lib │ ├─ application.js // Get out of the application class. │ ├─ ├─ Context.js // Handle middleware Such as CTX. The body is the agent of CTX. Response. The body │ ├ ─ ─ request. Js / / request object │ └ ─ ─ the response. The js / / response objects └ ─ ─ package. The jsonCopy the code
Code link: Mykoa link on Github
app.js
let Koa = require('./lib/application')
let app = new Koa()
app.use(async (ctx, next) => {
console.log(1);
await next();
console.log(6);
});
app.use(async (ctx, next) => {
console.log(2);
await next();
console.log(5);
});
app.use(async (ctx, next) => {
console.log(3);
ctx.body = "hello world";
console.log(4);
});
app.listen(3000, () = > {console.log('listening on 3000');
})
Copy the code
When accessing localhost:3000 in the browser, the output printed in the server is :1, 2, 3, 4, 5, 6
The key to the application. The js
let http = require('http')
let EventEmitter = require('events')
let context = require('./context')
let request = require('./request')
let response = require('./response')
let Stream = require('stream')
class Application extends EventEmitter{
constructor() {super(a)this.middlewares = []
this.context = context
this.request = request
this.response = response
}ge
use(fn){// Collect middleware
this.middlewares.push(fn)
}
compose(){// Synthetic middleware, specifically tandem middleware
return async ctx =>{
function createNext(middleware, oldNext){
return async() = > {await middleware(ctx, oldNext)
}
}
let len = this.middlewares.length
let next = async() = > {return Promise.resolve()
}
for (let i=len- 1; i>=0; i--) {
let currentMiddleware = this.middlewares[i]
next = createNext(currentMiddleware, next)
}
await next()
}
}
createContext(req, res){
const ctx = Object.create(this.context)
const request = ctx.request = Object.create(this.request)
const response = ctx.response = Object.create(this.response)
ctx.req = request.req = response.req = req
ctx.res = request.res = response.res = res
request.ctx = response.ctx = ctx
request.response = response
response.request = request
return ctx
}
handleRequest(ctx, fn){
let res = ctx.res
fn(ctx)// Call the middleware
.then((a)= >{
let bodyType = typeof ctx.body
if (bodyType == 'object') {
res.setHeader('Content-type'.'application/json; charset=utf8')
res.end(JSON.stringify(ctx.body))
} else if (ctx.body instanceof Stream) {
ctx.body.pipe(res)
} else if (bodyType == 'string' || Buffer.isBuffer(ctx.body)) {
res.setHeader('Content-type'.'text/html; charset=utf8')
res.end(ctx.body)
} else {
res.end('not found')
}
})
.catch(err= >{
this.emit('error', err)
res.statusCode = 500
res.end('server error')
})
}
callback(){
const fn = this.compose()// Compose middleware
return (req,res) = >{// Returns a callback to the Web service
console.log('callback.... ')
const ctx = this.createContext(req, res)// Get the context variable CTX
return this.handleRequest(ctx, fn)// Process the request} } listen(... args){let server = http.createServer(this.callback())// Create a Web service
returnserver.listen(... args) } }module.exports = Application
Copy the code
The listen and use functions are nothing to talk about, but the key is to understand the compse function, which is the core of KOA
Compose function for tandem middleware
Koa source in the use of koA-compose middleware to achieve, here is a reference to someone else’s handwriting compose function, very subtle.
compose(){
return async ctx =>{
function createNext(middleware, oldNext){
return async() = > {await middleware(ctx, oldNext)
}
}
let len = this.middlewares.length
let next = async() = > {return Promise.resolve()
}
for (let i=len- 1; i>=0; i--) {
let currentMiddleware = this.middlewares[i]
next = createNext(currentMiddleware, next)
}
await next()
}
}
Copy the code
For compose, the key is how to call the middleware sequentially.
Once the middleware is concatenated, it simply calls the first middleware in the handleRequest.
The key to this code is the createNext function, which wraps the next middleware call in an async function;
By iterating backwards through the middleware array, you can place the next middleware that should be called in the next from the previous call.
This may be a bit confusing, but WHEN I convert the code into a call diagram, I can understand it. Take middleware calls in app.js as an example:
Find the async/await feature super handy.
summary
Koa source code is really very short, there are a lot of good analysis, their own look again, write again, or super harvest.
reference
1. The koa source code
2. KOA2 framework principle analysis and implementation
3. Node advanced – handwritten KOA source for all details