1. How did the code work in the early days before KOA?

In the early days before KOA and EXPess, all request responses could only be done in http.createserver

const http = require('http')
const server = http.createServer((req, res) = >{
 // The req.url(request path) is used to return different data
 if (req.url == "/") {
    res.writeHead(200, { "Content-Type": "text/html" }) // Return the request status code
    res.end("Return the root page to the browser")}else if (req.url == "/about") {
    res.writeHead(200, { "Content-Type": "text/html" });
    res.end("Return/About directory page to browser")
  }
})
server.listen(3000.() = >{
 console.log('Listening port 3000') // The server started the successful callback function
})
Copy the code

2. How the code works with KOA middleware

App.use (fn) is passed as a parameter to the FN function, which is middleware

const Koa = require('koa')
const app = new Koa() // Create a KOA service that encapsulates the HTTP implementation

app.use(async (ctx, next) => {
    console.log(1)
    await next()
    console.log(4)
})

app.use(async (ctx, next) => {
    console.log(2)
    await next()
    console.log(3)
})

app.listen(3000) // Listen to the service

// Result: 1, 2, 3, 4
Copy the code

When a browser accesses a request, it does 1, 2, 3, and 4 different processes to return the final data to the browser. Koa is more hierarchical than HTTP in that it handles browser requests more logically

The execution of koA middleware is similar to the process of slicing an onion, with half of the onion being servedFrom outside to insideCut it layer by layer(execute)The halfFrom the inside outCut it layer by layer(execute), so we sayKoa middlewareModel for”The onion model“.

3. How do YOU implement a KOA yourself?

We know that KOA is implemented by encapsulating HTTP using the Principles of the Onion model. Let’s implement a KOA as well.

const http = require('http')

class Koa {
  constructor() {
    this.middlewares = []  // Store the middleware function fn passed in by koa.use(fn)
  }
  // Create an HTTP service when the koa.listen(3000) method is executed
  listen(. args) {
    const server = http.createServer(async (req, res) => {
      const ctx = this.createContext(req, res)  //1. Get the request parameters wrapped in CTX
      const fn = this.compose(this.middlewares) //2. Pass CTX into middleware through higher-order function principle
      await fn(ctx)                             //3. Execute all middleware methods
      res.end(ctx.body)                         //4. Finally return data to the browser}) server.listen(... args) }use(middleware) {
    this.middlewares.push(middleware)
  }
  createContext(req, res) {
    // Selectively expose the get set method to prevent users from arbitrarily modifying properties
    const ctx = Object.create({
      get url() {
        return this.request.url
      },
      get body() {
          return this.response.body
      },
      set body(val) {this.response.body = val
      },
      get method() {
          return this.request.method
      }
    })

    ctx.request = Object.create({
      get url() {return this.req.url
      },
      get method() {return this.req.method.toLowerCase()
      }
    })

    ctx.response = Object.create({
      get body() {return this._body
      },
      set body(val) {this._body = val
      }
    })

    ctx.req = ctx.request.req = req
    ctx.res = ctx.response.res = res
    return ctx
  }
  // The core principles of the Onion model
  compose(middlewares) {
    return function (ctx) {
      return dispatch(0)
      function dispatch(i) {
        let fn = middlewares[i]
        if(! fn) {return Promise.resolve()
        }
        The reason the dispatch method returns a Promise is to ensure that in the case of asynchrony the onion model can still be executed sequentially with await syntax
        return Promise.resolve(
          fn(ctx, function next() {
            // Function recursive loop: the last function is not completed until the next function is completed. This is the core principle of the Onion model
            return dispatch(i + 1)})}}}}// Now we use our own implementation of Koa
const app = new Koa() // Create a KOA service that encapsulates the HTTP implementation

app.use(async (ctx, next) => {
    console.log(1)
    await next()
    console.log(4)}); app.use(async (ctx, next) => {
    console.log(2)
    await delay() // Test whether the code can be executed sequentially after asynchrony
    await next()
    console.log(3)});function delay() {
  return new Promise(resolve= > {
    setTimeout(() = > {
      console.log('I'm asynchronous code in mid-stream')
      resolve()
    }, 2000)
  })
}

app.listen(3000.() = >{
  console.log('Listening port 3000') // The server started the successful callback function
})
Copy the code

The running results are as follows: