Koa framework

Now many projects are based on the KOA framework to achieve, mainly because KOA is small and light, the use of plug-in expansion, according to the needs of the function to choose different plug-ins, more convenient and fast development. So it is very, very, very necessary to understand how KOA works.

Koa series of articles

  • The KOA framework will use and write — (KOA infrastructure)
  • The KOA framework will use and write — (koa-router)
  • The KOA framework will use and write — (koa-view, koa-static)
  • – KoA frameworks will be used and written — (koA-bodyParser, KoA-better-Body)

Analysis of koA usage

const Koa = require('koa'); let app = new Koa(); Use ((CTX,next)=>{console.log(ctx.req.path); console.log(ctx.req.path) =>{console.log(ctx.req.path); console.log(ctx.request.req.path); console.log(ctx.request.path); console.log(ctx.path); next(); }) app.listen(3000); //Koa has a listener on its prototypeCopy the code

Onion model and middleware combination

The onion model

const Koa = require('koa'); const app = new Koa(); app.use(async (ctx, next)=>{ console.log(1) await next(); console.log(2) }); app.use(async (ctx, next) => { console.log(3) await next(); console.log(4) }) app.use(async (ctx, next) => { console.log(5) awit next(); Console. log(6)}) // Prints the result: 1 3 5 6 4 2Copy the code

Middleware portfolio

The implementation of the KOA Onion model is to store functions in a Middlewares queue via use and then distribute middleware via function dispatch.

  • Dispatch Combined middleware:
let app = { middlewares:[]; Use (fn){// Register middleware this.middlewares.push(fn); } } app.use(next=>{ console.log(1) next(); console.log(2) }); app.use(next => { console.log(3) next(); console.log(4) }) app.use(next => { console.log(5) next(); Console. log(6)}) dispatch(0) function dispatch(index){// Dispatch middleware if(index===app.middlewares.length) retrun; let middleware = app.middlewares[index]; middleware(()=>{ dispatch(index+1); })}Copy the code
  • Array. The prototype. ReduceRight combination middleware:
let app = { middlewares:[]; Use (fn){// Register middleware this.middlewares.push(fn); } } app.use(next=>{ //fn1(next) next => fn2 console.log(1) next(); console.log(2) }); app.use(next => { //fn2(next) next => fn3 console.log(3) next(); console.log(4) }) app.use(next => { //fn3(next) next => null; console.log(5) next(); console.log(6) }) let fn= compose(app.middlewares) function conpose(middle){ return middles.reduceRight((a,b)=>{ Return function(){b(a); }}, () = > {}); } fn(); //fn3(next) next:() => {}; //fn2(next) next:() => fn3(()=>{}) //fn1(next) next:() => fn2(()=>fn3(()=>{}))Copy the code
  • Array.prototype.reduce Combination middleware:
let app = { middlewares:[]; Use (fn){// Register middleware this.middlewares.push(fn); } } app.use(next=>{ //fn1(next) next => fn2 console.log(1) next(); console.log(2) }); app.use(next => { //fn2(next) next => fn3 console.log(3) next(); console.log(4) }) app.use(next => { //fn3(next) next => null; console.log(5) next(); Console. log(6)}) let fn= compose(app.middlewares) function conpose(middle){return middles.reduce((a,b)=>{// converge into a function return (arg)=>{ a(()=>{b(arg)}) } }); } fn(()=>{});Copy the code

Part of KOA

Koa consists of four main parts:

  • Application: The main logic of KOA, which contains middleware processing
  • Context: KOA encapsulates CTX
  • Request: Encapsulation of the KOA request object
  • Response: KOA response object encapsulation

The realization of koa

Prototype inheritance

Let proto = {// similar to object.defineProperty (request,'url'){get(){... }} get height(){ // this => example return this.options.height }, name: 'proto' } let example = Object.create(proto) console.log(proto) //{name:'proto'} console.log(example) //{} example.name = 'example' example.options = {height:170,weight:50} console.log(proto) //{name:'proto'} console.log(example) //{name:'example',options:{height:170,weight:50}} console.log(example.height) //170 /**************************************************************/ let proto = { name: 'proto' } let sub = proto console.log(proto) //{name:'proto'} console.log(sub) //{name:'proto'} sub.name = 'sub' console.log(proto) //{name:'sub'} console.log(sub) //{name:'sub'}Copy the code

The request to encapsulate

Extend attributes such as URL and path on request

//request.js

let request = {
    get url(){
        // this => ctx.request
        // ctx.request.req.url
        this.req.url;   
    }
    get path(){
        let url = require('url');
        return url.parse(this.req.url).pathname;
    }
}
module.exports = request;
Copy the code

The response to encapsulate

Extend properties such as body on request

//response.js let response = { get body(){ return this._body; } set body(val){// set the built-in _body to store this._body = val}} module.exports = response;Copy the code

CTX encapsulation

The CTX property proxies some properties on CTx. request and ctx.response, enabling CTx. xx to access ctx.request. Xx or ctx.response.xx

//context.js let proto = {}; function defineGetter(property,key){ proto.__defineGetter(key,function(){ return this[property][key]; }) } function defineSetter(property,key){ proto.__defineSetter(key,function(val){ this[property][key] = val; }) } defineGetter('request','url'); // CTX proxy ctx.request.url get defineGetter('request','path'); // CTX proxy ctx.request.path get defineGetter('response','body'); // CTX proxy ctx.request.path get defineGetter('response','body'); // CTX proxy ctx.response.body get defineSetter('response','body'); // CTX projects ctx.response.body set // returns an object as CTX's prototype. module.exports = proto;Copy the code

Class Koa initialization

  • Koa is a class that has Middleware, CTX, Request, and Response
  • Koa.prototype has use registry middleware
  • Koa. Prototype has Listen to listen for network requests and is an internal encapsulation of HTTP modules
  • The handleRquest in Koa handles the context CTX and middleware
//application.js const http = require('http'); let context = require('./context'); let request = require('./request'); let response = require('./response'); class Koa { constructor(){ this.middlewares = []; this.context = Object.create(context); this.request = Object.create(request); this.response = Object.create(response); } use(fn){ this.middlewares.push(fn) ; } // The mount package handles CTX createContext(req,res){// ctx.request, ctx.response objects that are part of koA // the KOA plugin extends other properties on ctx.request let CTX = Object.create(this.context); ctx.request = Object.create(this.request); ctx.response = Object.create(this.response); // HTTP module native attribute req ctx.request.req = ctx.req = req; ctx.response.res = ctx.res = res; return ctx; } // Composing middleware compose(CTX,middles){function dispatch(index){if(index === midd.length) return; let middle = middles[index]; middle(ctx,()=>dispatch(index+1)); } dispatch(0); } // Network request listener callback handleRequest(req,res){// a new context is created each time a request is received let CTX = createContext(req,res); this.compose(ctx,this.middlewares); } // Network request listen(... args){ let server = http.createServer(this.handleRquest); server.listen(... args) } } module.exports = KoaCopy the code

Handle asynchrony and errors

All of the above functions are based on synchronous functions, but most of the node functions are asynchronous, so the middleware handler functions need to be compatible with asynchronous functions. Since async+ AWit is equal to Generator + CO (KOA1.0), and the implementation of generator automation in CO is based on the Promise implementation, the promisification function must be used here. If you don’t know Promise, Generator, async, check out another article. The Promise principle is that simple

//application.js const http = require('http'); let context = require('./context'); let request = require('./request'); let response = require('./response'); let Stream = require('stream'); let EventEmitter = require('events'); Class Koa extends EventEmitter {constructor(){this.middlewares = []; this.context = Object.create(context); this.request = Object.create(request); this.response = Object.create(response); } use(fn){ this.middlewares.push(fn) ; } createContext(req,res){ let ctx = this.context; ctx.request = this.request; ctx.response = this.response; ctx.req=ctx.request.req =req; ctx.res=ctx.response.res=res; return ctx; } compose(CTX,middles){function dispatch(index){// No middleware is registered, Return a promise if(index === middle. Length) return promise.resolve (); let middle = middles[index]; Resolve (middle(CTX,()=>dispatch(index+1))); } return dispatch(0); } handleRequest(req,res){ res.statusCode = 404; let ctx = createContext(req,res); // All middleware implementations can execute built-in logic, handle errors, etc. Let p = this.pose (CTX,this.middlewares); P.chen (()=>{let body = ctx.body; if (Buffer.isBuffer(body) || typeof body === 'string'){ res.setHeader('Content-Type','text/plain; charset=utf8') res.end(body); } else if (body instanceof Stream){ body.pipe(res); }else if(typeof body == 'object'){ res.setHeader('Content-Type','application/json; charset=utf8') res.end(JSON.stringify(body)); }else{ res.end('Not Found'); }}). Catch (e=>{this.emit('error',e); res.statusCode = 500; End (require('_http_server').status_codes [res.statuscode]); }) } listen(... args){ let server = http.createServer(this.handleRquest); server.listen(... args) } } module.exports = KoaCopy the code

conclusion

Koa infrastructure is introduced, in addition to the infrastructure, KOA has two important components of routing and middleware, the next article together with the koA routing!

  • The KOA framework will use and write — (koa-router)