The upper part is the English version, and the lower part is the Chinese version, with the same content. If there are any wrong, or you have anything hard to understand, pls feel free to let me know.many thx.
Use koa-compose to process the middleware array and get a recursively called function fn(fnMiddleware)
Initialize a new ctx instance by createContext() for the current request
return an http service callback function handleRequest
function compose (middleware) {
...
return function (context, next) {
// last called middleware #
let index = -1
return dispatch(0)
function dispatch (i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
} catch (err) {
return Promise.reject(err)
}
}
}
}
context is the ctx parameter in the middleware function, next is the next middleware function
dispatch.bind(null, i + 1) means the next middleware function, use bind to point this to null, which is next in the middleware function
After the middleware is processed, invoke handleResponse, if has an error, invoke ctx.onerror
onFinished is used to invoke the onerror method after the end of the monitoring res stream, similar to on('finish') or on('error').
3.1.5 respond()
function respond(ctx) {
...
// responses
if (Buffer.isBuffer(body)) return res.end(body);
if ('string' === typeof body) return res.end(body);
if (body instanceof Stream) return body.pipe(res);
// body: json
body = JSON.stringify(body);
if (!res.headersSent) {
ctx.length = Buffer.byteLength(body);
}
res.end(body);
}
According to the data type of response data, pass it to res.end() after processing
Print exception messages
Only synchronous exceptions can be caught here. Asynchronous exceptions in middleware need to be caught by process.on("unhandledRejection",()=>{}), or caught with try..catch asynchronous codes handled by async/await
Note: in the callback method, this line if (!this.listenerCount('error')) this.on('error', this.onerror); means that invoke the onerror method only if we do not use the on('error') form to listen exceptions
3.1.8 Other
inspect invoke toJSON method, toJSON prints object information
When calling properties or methods of ctx, delegate to response or request
For example, ctx.attachment() is equivalent to response.attachment(), ctx.acceptsLanguages() is equivalent to request.acceptsLanguages()
proto is the context object
Hijack proto's get by defineGetter and access target instead
Also can be done it by Object.defineProroty or Proxy, defineGetter, defineSetter are obsolete api
Wraped properties of the origin response object and extended some properties or methods
Such as
...
get status() {
return this.res.statusCode;
},
...
Recommended to use ctx.status in the code
Invoking chain: ctx.status -> response.status -> res.statusCode
3.4 Summarize
application.js exports the Koa instance class. It inherits EventEmitter, which is convenient to monitor exceptions by the form of on('error')
application.js: listen() launchs an http sever; use() adds the middleware function into the middleware queue; callback() is used as the callback function of the http service, specifically iterating the middlewares queue to generate a recursive function fnMiddleware, and after executing fnMiddleware use handleRequest to handle some general response and exceptions
context.js: proxy for properties and methods of request.js, response.js
request.js: encapsulates the properties of the http request
response.js: encapsulates the properties of the http response
4 Common middleware analysis
It is difficult to build a web system in MVC mode with koa alone, so need some middleware with
As below is an analysis of commonly used middleware
layer.stack is an array of route middleware, so need to execute memo.concat(layer.stack)
4.1.4 router.allowedMethods()
allowedMethods() does some common finishing works after an error in the response, i.e. no status code or 404
4.1.4 Summarize
Wrap the route into a Layer object and register it in the stack queue of Layer object by router.method. router.routes() will return a dispatch function that invoked the chain of koa middlers. When the user requests a url from client, the dispatch function will be executed, it will iterate the paths of all Layer objects in the stack to compare ctx.path. Then get the matching Layer.stack (middlewares of each route) and execute them in order.
Since all routes will be iterated, if there are many routes, such as thousands or more, you may need to consider optimization it
parse.json()
json: Determine if the Content-Type is json such as application/json, convert the data of the request stream into a string, after parsing it with JSON.parse, then assign it to the ctx.body
form: After determining that it is "application/x-www-form-urlencoded" type, convert a string like 'a=1&b=1' into a json object by qs package, and then assign it to ctx.body
text/plain or text/xml: assigned to ctx.body string value
default ctx.body = {}
If you need to support multipart/form-data (upload files), you need to use koa-multer additionally, or use koa-body instead of koa-bodayparser
koa-static parsed the path parameter to the absolute path by path.resolve and passes it to koa-send. koa-send creates a readable stream by file.createReadStream and assigns it to ctx.body. Finally, After processing by the line of code in koa's respond method 'if (body instanceof Stream) return body.pipe(res) ' , the response is sent to the client
Sometimes assert(condition, error) instead of if is a good choice, such as the line: assert(root, 'root directory is required to serve files')
function compose (middleware) {
...
return function (context, next) {
// last called middleware #
let index = -1
return dispatch(0)
function dispatch (i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if (!fn) return Promise.resolve()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)))
} catch (err) {
return Promise.reject(err)
}
}
}
}
context 就是中间件函数里的ctx, next就是下一个中间件函数
dispatch.bind(null, i + 1) 下一个中间件函数,用bind把this指向null,也就是中间件函数里的next
The upper part is the English version, and the lower part is the Chinese version, with the same content. If there are any wrong, or you have anything hard to understand, pls feel free to let me know.many thx.
1 How to use
2 Directory Structure
3 Key source code analysis
3.1 application.js
Skeleton
3.1.1 constructor()
3.1.2 listen()
3.1.3 callback()
3.1.3.1 Analyse koa-compose
source code
3.1.4 handleRequest()
3.1.5 respond()
3.1.6 use()
3.1.7 onerror()
3.1.8 Other
3.2 context.js
source code Skeleton
3.2.1 onerror()
3.2.2 delegate response and request
3.2.2.1 Analyse delegates
source code
3.2.2.1.1 delegate.method()
3.2.2.1.2 delegate.access()
3.2 request.js
source code
3.3 response.js
source code
3.4 Summarize
4 Common middleware analysis
4.1 koa-router
source code How to use
4.1.1 The http methods it supports
4.1.2 Invoke http method
register()![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/20220422144607.png)
Layer source code![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/20220427135931.png)
4.1.3 router.routes()
match()![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/20220422145659.png)
routes()![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/20220422150231.png)
4.1.4 router.allowedMethods()
4.1.4 Summarize
4.2 koa-bodyparser
source code![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/1BC63C45-7474-4B68-A3F5-F323C4A54D2C.png)
parse.json()
json: Determine if the Content-Type is json such as application/json, convert the data of the request stream into a string, after parsing it with JSON.parse, then assign it to the ctx.body
4.2 koa-static
source code How to use
koa-send source code
4.3 koa-cors
source code
Not OPTIONS request![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/20220422201623.png)
OPTIONS request![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/20220422201911.png)
The following is the Chinese version, the same content as above
1 如何使用
2 目录结构
3 关键源码分析
3.1 application.js
骨架
3.1.1 constructor()
3.1.2 listen()
3.1.3 callback()
3.1.3.1 分析koa-compose
源码
3.1.4 handleRequest()
3.1.5 respond()
3.1.6 use()
3.1.7 onerror()
3.1.8 其他
3.2 context.js
源码 骨架
3.2.1 onerror()
3.2.2 delegate response and request
3.2.2.1 分析delegates
源码
3.2.2.1.1 delegate.method()
3.2.2.1.2 delegate.access()
3.2 request.js
源码
3.3 response.js
源码
3.4 总结
4 常见中间件分析
4.1 koa-router
源码 用法
4.1.1 它支持的method
4.1.2 Http method 调用
register()![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/20220422144607.png)
Layer 源码![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/20220427135931.png)
4.1.3 router.routes()
match()![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/20220422145659.png)
routes()![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/20220422150231.png)
4.1.4 router.allowedMethods()
4.1.4 总结
4.2 koa-bodyparser
源码![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/1BC63C45-7474-4B68-A3F5-F323C4A54D2C.png)
parse.json()![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/20220422114649.png)
4.2 koa-static
源码
用法
koa-send 源码
4.3 koa-cors
源码
非 OPTIONS 请求![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/20220422201623.png)
OPTIONS 请求![](https://raw.githubusercontent.com/mominger/MyPublicFiles/master/img/20220422201911.png)