Koa source code study

brief introduction

Koa Is a very lightweight web development framework built by the Express team. Compared with Express, Koa uses async function to solve asynchronous problems, and is completely separated from middleware. It is very elegant, and Koa code is concise and friendly, which is very suitable for beginners.

Koa code structure

You can see that the structure of KOA is very simple. Under the lib folder is koa's core file:

application.js

Application is the koa's entry file, and export s an application class (inherited from events.Emitter). Application has the following main (public) APIs:

  • listen: implement the encapsulation of http.createServer(). The passed parameter callback completes middleware merging, error listening, Context Creation and request processing.
  • Use: we usually use app.use(function) to add middleware to applications. In the use method, koa adds the middleware (function) to this.middleware array.
  • Callback: koa compose combines the middleware, returns a request callback function, and gives listen as a callback.
  • toJSON: returns an object without private attributes (_).
module.exports = class Application extends Emitter {
  listen(...args) {
    debug('listen');
    const server = http.createServer(this.callback());
    return server.listen(...args);
  }
  
  use(fn) {
    if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
    this.middleware.push(fn);
    return this;
  }
  
  callback() {
    const fn = compose(this.middleware);
    
    if (!this.listenerCount('error')) this.on('error', this.onerror);
    
    const handleRequest = (req, res) => {
      const ctx = this.createContext(req, res);
      return this.handleRequest(ctx, fn);
    };
  
    return handleRequest;
  }
}

context.js

Context is the ctx we are most exposed to when using koa, which is an exposed object. The get set operation of cookies is implemented in context, which is also the principle that we can directly use ctx to operate cookies. In addition, the most important thing in ctx is delegate, that is, delegate. Let's take a brief look at the code:

delegate(proto, 'response')
  .method('attachment')
  .method('redirect')
  .method('remove')
  .method('vary')
  .method('set')
  .method('append')
  .method('flushHeaders')
  .access('status')
  .access('message')
  .access('body')
  .access('length')
  .access('type')
  .access('lastModified')
  .access('etag')
  .getter('headerSent')
  .getter('writable');

The above proto is CTX, which implements the proxy for the response object. For example, we can access ctx.response.status by using ctx.status.

Similarly, the attributes and methods above request are also proxied into ctx:

delegate(proto, 'request')
  .method('acceptsLanguages')
  .method('acceptsEncodings')
  .method('acceptsCharsets')
  .method('accepts')
  .method('get')
  .method('is')
  .access('querystring')
  .access('idempotent')
  .access('socket')
  .access('search')
  .access('method')
  .access('query')
  .access('path')
  .access('url')
  .access('accept')
  .getter('origin')
  .getter('href')
  .getter('subdomains')
  .getter('protocol')
  .getter('host')
  .getter('hostname')
  .getter('URL')
  .getter('header')
  .getter('headers')
  .getter('secure')
  .getter('stale')
  .getter('fresh')
  .getter('ips')
  .getter('ip');

ctx.hostname is ctx.request.hostname.

request.js && response.js

Encapsulate the Koa Request/Response object in request.js and response.js. You can request.xxx/response.xxx Operate on it. Many get and set methods are used.

Implement a simple moa

  • First, you need to complete the encapsulation of the http module. You can use to create a server.
  • Then complete the encapsulation of request and response objects and proxy them to the context object.
  • Then you need to deal with the middleware and implement the onion model.
  • Finally, you need to complete error handling and exception capture.

Posted by chamal on Fri, 26 Nov 2021 01:51:20 -0800