The introduction

I recently took a look at Node and studied the KoAJS technical framework of Node. Here, I summarized some understanding of the usage process and analyzed the main mechanism of Koa.

why

Why should we use the Koa framework? When we use the Node native HTTP module to write back-end services, especially when developing complex business logic, the native package is often difficult to maintain subsequent iterations of requirements. At this point, we can choose an appropriate Web framework to support development.

An overview of the

Koa aims to be a smaller, more expressive, and more robust cornerstone of web application and API development. Koa, developed by Express originators (TJ Holowaychuk), eliminates cumbersome nested callback functions and greatly improves the efficiency of common errors by using a combination of more advanced asynchronous flow control middleware. As a Web development microframework, Koa can be used for traditional Web application development, as a server-side interface, as a standalone API layer, gateway, and more.

The basic use

Let’s look at a basic Demo in use:

const Koa = require("koa");
const app = new Koa();
app.use(async (ctx, next) => {
  // Here we define a log where our business logic can be written before and after next()
  // Set a timestamp before returning the time
  const start = Date.now();
  // next() to enter the next use
  await next();
  // Return a timestamp after the return time
  const end = Date.now();
  console.log(` request${ctx.url}Time consumingThe ${parseInt(end - start)}ms`);
});
app.use((ctx, next) = > {
  // We wait 120 milliseconds to return to this point for relevant business logic processing
  const expire = Date.now() + 120;
  while (Date.now() < expire) {
    ctx.body = {
      name: "myName"}; }}); app.listen(3000.() = > {
  console.log("start...");
});
Copy the code

Print result:

The execution process is as follows:

For Koa framework, we here but more on the official documentation and source code analysis, interested students can understand Koa official website or take a look at the Koa source code analysis of the article jelly.jd.com/article/5f8… .

Koa’s goal is to implement callback business logic in a more simplified, streamlined, and modular way. To achieve this goal, Koa uses context and middleware mechanisms. To better understand these two mechanisms, let’s simply implement them manually.

context

In order to simplify the API, KOA introduces the concept of context, encapsulates and mounts the original request object REq and response object RES into the context, and sets getters and setters on the context to simplify operations.

First we create request, response, context files and add get and set properties to them.

// request.js
module.exports ={
    get url() {return this.req.url
    },
    get method() {return this.req.method.toLowerCase()
    }
    // ...
}
Copy the code
// response.js
module.exports = {
    get body() {return this._body
    },
    set body(val) {this._body = val
    }
    // ...
}
Copy the code
// context.js
module.exports = {
    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
    }
    // ...
}
Copy the code

We attach RES, REq, Response and Request to each other through CTX to associate context with each other.

createContext(req, res) {
  const ctx = Object.create(context);
  ctx.request = Object.create(request);
  ctx.response = Object.create(response);
  ctx.req = ctx.request.req = req;
  ctx.res = ctx.response.res = res;
  return ctx;
}
Copy the code

Then pass the REq and RES to the createContext function in createServer to create the context.

const server = http.createServer(async (req, res) => {
  / /...
  let ctx = this.createContext(req, res);
  / /...
Copy the code

The middleware

So what is middleware? Middleware is the core extension mechanism of THE Koa framework. It is mainly used to abstract the HTTP request process. In the process of HTTP request, the middleware is like a layer of filter net. Each middleware realizes the corresponding business logic by rewriting the request and response data and status in the process of HTTP processing. The Koa middleware mechanism is the concept of functional composition, which combines a set of functions that need to be executed sequentially into a single function. The parameters of the outer function are actually the return values of the inner function. We can think of middleware as a chain of responsibility pattern in design patterns.

We can see the order of middleware execution by looking at the above execution flow, which is layer by layer opening and layer by layer closing, like peeling an onion. Early Python gave this execution a nice name, the Onion model.

Middleware to achieve this onion ring mechanism has a variety of implementation ideas, we use the idea of recursive implementation. We first declare a compose function that returns a composition of functions and passes it to our context CTX object. We then declare the asynchronous function that needs to be executed each time and return the promise of the first layer. We decide to return an empty promise if the local layer function is empty. Otherwise return the execution function that executes the method itself and pass in the execution of the next function. Let’s look at the code

// myKoa
compose(middlewares) {
    return function (ctx) {
      return dispatch(0);
      // I => indicates which asynchronous function to return
      function dispatch(i) {
        // Take out the first onion ring
        let fn = middlewares[i];
        // The onion rings themselves are asynchronous functions, we can only return the Promise object, through the cupboard
        if(! fn) {return Promise.resolve();
        }
        return Promise.resolve(
          // The next object put into each execution of the method itself is the next-level execution commitment
          fn(ctx, function next() {
            // Return to the next layer of onion rings to execute the next layer
            return dispatch(i + 1); })); }}; }Copy the code

We initialize an array middlewares in the constructor method

// myKoa
  constructor() {
    this.middlewares = [];
  }
Copy the code

You then initialize the compose composition function in createServer, pass in the CTX context object and execute the composition function.

// myKoa
// ...
const server = http.createServer(async (req, res) => {
  / /...
  const fn = this.compose(this.middlewares);
  await fn(ctx);
  / /...
Copy the code

Finally, we take each passed function in the use method and store it in the Middlewares array

// myKoa
use(middleware) {
  this.middlewares.push(middleware);
}
Copy the code

Let’s test it out:

const MyKoa = require("./myKoa");
const app = new MyKoa();
const delay = () = > new Promise((resolve) = > setTimeout(() = > resolve(), 2000));
app.use(async (ctx, next) => {
  ctx.body = "1";
  await next();
  ctx.body += "5";
});
app.use(async (ctx, next) => {
  ctx.body += "2";
  await delay();
  await next();
  ctx.body += "4";
});
app.use(async (ctx, next) => {
  ctx.body += "3";
});
app.listen(3000.() = > {
  console.log("start...");
});
Copy the code

Mykoa.js complete source code;

const http = require("http");
const context = require("./context");
const request = require("./request");
const response = require("./response");
class MyKoa {
  constructor() {
    this.middlewares = [];
  }
  listen(. args) {
    // Create an HTTP server
    const server = http.createServer(async (req, res) => {
      // Create context
      let ctx = this.createContext(req, res);
      const fn = this.compose(this.middlewares);
      await fn(ctx);
      // Data response
      res.end(ctx.body);
    });
    // Start the listenerserver.listen(... args); }use(middleware) {
    this.middlewares.push(middleware);
  }
  createContext(req, res) {
    const ctx = Object.create(context);
    ctx.request = Object.create(request);
    ctx.response = Object.create(response);
    ctx.req = ctx.request.req = req;
    ctx.res = ctx.response.res = res;
    return ctx;
  }

  compose(middlewares) {
    return function (ctx) {
      return dispatch(0);
      // I => indicates which asynchronous function to return
      function dispatch(i) {
        // Take out the first onion ring
        let fn = middlewares[i];
        // The onion rings themselves are asynchronous functions, so we can only return the Promise object recursively
        if(! fn) {return Promise.resolve();
        }
        return Promise.resolve(
          // The next object put into each execution of the method itself is the next-level execution commitment
          fn(ctx, function next() {
            // Return to the next layer of onion rings to execute the next layer
            return dispatch(i + 1); })); }}; }}module.exports = MyKoa;

Copy the code

Above we have implemented a simple middleware onion ring model mechanism. Koa middleware can intercept both requests and responses, which is a rare feature in Web frameworks. Other Web frameworks, such as Express, only intercept requests, not responses, so Koa has an advantage in middleware mechanisms. Of course, Koa’s mechanism is a bit more complex than Express’s, as the increased number of middleware pieces add up to make the interception of requests and responses into paper-clip like calls.

conclusion

The KOA2 framework will be the popular framework for Node.js Web development. This article looks at the basic use of Koa and briefly implements the middleware mechanism and context. The above is all the content of this article, I hope to help you learn