Functions overview
- Application: Create the context, merge the middleware, and start the service
- Request: An extension to the native HTTP module REq
- Response: Extension to the native HTTP module RES
- Context: Merge and broker request and response
Service startup :listen
use
const Koa=require('./application')
const app=new Koa()
app.listen(3000, () = > {console.log('run server__')})Copy the code
implementation
// The HTTP module is directly used internally
const http=require('http')
class Application{
listen(. args){
constserver= http.createServer() server.listen(... args) } }Copy the code
Request handling :handleRequest
use
const Koa=require('./application')
const app=new Koa()
app.use((ctx) = >{
// The following four expressions are the same
// Why
console.log(ctx.url)
console.log(ctx.request.url)
console.log(ctx.req.url)
console.log(ctx.request.req.url)
})
app.listen(3000, () = > {console.log('run server__')})Copy the code
implementation
class Application{
// Function registration (middleware is also a function)
use(fn){
this.fn=fn
}
// Request processing
handleRequest(req,res){
// Create context: merge req,res is CTX
// This method implementation is described below
const ctx=this.createContext(req,res)
// CTX is the first argument for external use
// app.use(ctx=>{})
this.fn(ctx)
}
/ /... Start the service
listen(. args){
// Bind ensures that this points to the custom Application
const server= http.createServer(this.handleRequest.bind(this)) server.listen(... args) } }Copy the code
CreateContext :createContext
- Think about it. Why did KOA merge REQ and RES into one CTX? Blind guessing is for ease of use. One is better than two.
- What’s the difference between REQ, RES and CTX? The req, res some,
CTX is
, req,res doesn’t,CTX and
. - You ask me what is the West factory? One word: east factory tube of I want tube, east factory tube of I want tube more!
implementation
/** * create application.js request.js response.js context.js */
//request.js
const request={}
module.exports=request
//response.js
const response={}
module.exports=response
//context.js
const context={}
module.exports=context
//application.js
class Application{
constructor(){
// These three are imported from outside
this.context=context;
this.request=request;
this.response=response;
}
// createContext will eventually return a merged context;
createContext(req,res){
Object. Create is used to extend without interfering with the original module, and is also a layer of inheritance
const context=Object.create(this.context)
const request=Object.create(this.request)
const response=Object.create(this.response)
// Context association and merge
// context.req=req;
// context.request.req=req
// context.res=res;
// context.response.res=res
// The above code can be shortened to the following form
context.req=context.request.req=req
context.res=context.response.res=res
// There is one thing you should know:
//ctx.req.url and ctx.request.req.url are the same because ctx.req and ctx.request
// Ctx.res and ctx.response.res also point to the same
// Another thing to think about in the meantime: what about ctx.url and ctx.request.url?
/ / return the context
return context
}
}
Copy the code
Request enhancement: Request
Request is koA’s enhancement of the native HTTP module REq, which is why the source code does not use getters and setters directly as context does.
Url = ctx.request.req.url = ctx.request.req.url = ctx.request.url = ctx.request.url = ctx.request.url = ctx.request.url = ctx.request.url = ctx.request.url
const request={
get url() {
// This refers to request
// Request in createContext happens to have a REQ attached to it
// Ctx.request. Req is essentially the same pointer as ctx.req
// Ctx.request.url is the same as ctx.req.url, ctx.request.req.url
return this.req.url; }},module.exports=request
Copy the code
Context: the agent
Ok, now the only thing missing is ctx.url, which is essentially ctx.request.url
Why do you say that? Let’s take a look inside the context implementation
const context = {}
function delegateGet(prop, key) {
The __defineGetter__ method executes a callback when a key of an object is accessed
context.__defineGetter__(key, function () {
return this[prop][key]
})
}
// ctx.url=>ctx.request.url
delegateGet('request'.'url')
module.exports = context
Copy the code
For __defineGetter__, see MDN
Developer.mozilla.org/zh-CN/docs/… __
If you go to the KOA source code, it uses a third party package :delegates, which is actually implemented __defineGetter__
Response enhancement :response
use
const Koa=require('./application')
const app=new Koa()
app.use((ctx) = >{
ctx.body='hello lengyuexin';
console.log(ctx.body)
})
app.listen(3000, () = > {console.log('run server__')})Copy the code
implementation
// CTX is a proxy, CTX is a proxy, CTX is a proxy
// So CTX must be given to Response
// If CTX does a request, it must be sent to Request
/ / it is: CTX. Body = > CTX.. The response of the body
const context = {}
function delegateGet(prop, key) {
context.__defineGetter__(key, function () {
return this[prop][key]
})
}
function delegateSet(prop, key) {
context.__defineSetter__(key, function (newValue) {
this[prop][key] = newValue
})
}
delegateGet('response'.'body')/ / access CTX. Body
delegateSet('response'.'body')/ / set the CTX. Body = 'XXX'
module.exports = context
Copy the code
So response, like request, is a getter setter
const response = {
_body: ' '.get body() {
return this._body
},
set body(newBody) {
this._body = newBody
}
}
module.exports = response
Copy the code
Merge middleware :compose
- In KOA, the use function can be called multiple times, and only the first one is executed by default.
- To execute the rest, call the next function, the second argument to the use function.
- Also, if asynchronous operations are involved, async,await can be used.
Onion diagram
use
// Print 123 in sequence
const app = new (require('koa'))
app.use((ctx, next) = > {
console.log(1)
next()
})
app.use((ctx, next) = > {
console.log(2)
next()
})
app.use((ctx, next) = > {
console.log(3)})// Error listening
app.on('error'.(err) = >{
console.error(err)
})
app.listen(3000)
Copy the code
implementation
// To facilitate asynchronous processing and error catching
The compose method returns a promise
// Middleware also needs to store it in an array
const EventEmitter = require('events')
const Stream = require('stream')
class Application extends EventEmitter {
// Middlewares are added to application constructor to initialize an empty array
constructor() {
super(a)this.context = context
this.request = request
this.response = response
this.middlewares = []// Multiple use calls, save
}
// The use method just pushes the function directly to middlewares
use(fn){
this.middlewares.push(fn)
}
handleRequest(req, res) {
const ctx = this.createContext(req, res)
// Compose the middleware and execute the returned promise
// Get the _body response
this.compose(ctx).then(() = > {
// Only buffers and strings can be processed by default
let _body = ctx.body;
if (_body === ' ') {
// Give the default value if the body is not set
// Set the status code to 404
res.statusCode = 404
_body = 'not found'
return res.end(_body)
} else if (_body instanceof Stream) {
// KOA also supports returning a file stream directly, via PIPE
// Handle the convection
return _body.pipe(res)
} else if (typeof_body ! = ='null' && typeof _body === 'object') {
// Processing of objects
return res.end(JSON.stringify(_body))
} else if (_body == null) {
//null and undefined are output as strings
// toString cannot be called directly
return res.end(_body + ' ')}else {
// Other types of direct toString
return res.end(_body.toString())
}
}).catch(err= > {
// Add error listener for app
// Application inherits the Events module
this.emit('error', err)
})
}
/ / compose
// Core logic three things: 1. Transboundary processing 2. Implementation of the first middleware 3. Execute the following middleware in turn
compose(ctx) {
// Dispatch uses the arrow function here
// The inner this refers to the custom Application
const dispatch = (index) = > {
// Handle handleRequest and then
if (index === this.middlewares.length) return Promise.resolve()
// Getting the current middleware is initially the first
const middleware = this.middlewares[index]
Middleware execution requires two parameters
const exec = middleware(ctx, () = > dispatch(++index))
// It is possible that this method does not have an async wrapper
// Return a promise
// The handleRequest then function will not report an error
return Promise.resolve(exec)
}
return dispatch(0)}}Copy the code
At the end of the text read
The interview was asked what you understand about KOA.
My first thought was to say something about the Onion model and handwriting compose.
When I calm down and realize, wow, I’ve forgotten how to write…
Love is fickle,
But move already hurt.
Thank you so much for reading my article,
I’m Cold Moon Heart. See you next time.