What is Koa
Koa is an HTTP middleware framework based on node implementation, which is built from the original Express framework.
So why Koa is more popular (at least I prefer Koa), it’s more elegant and concise than Express, and it’s more expressive and free.
Second, Koa experience
Node implements the underlying HTTP server
const http = require('http');
const server = http.createServer((req, res) = > {
// When we need to do req processing, we need to do a lot of processing here, and it is not modular
res.writeHead(200);
res.end('hi jinguo');
});
server.listen(3000, () = > {console.log('Listening port 3000');
});
Copy the code
Koa implements the underlying HTTP server
const koa = require('koa');
cosnt app = new koa();
// Modularization/simplification
app.use(ctx= > {
ctx.body = 'hi jinguo';
})
app.listen(3000);
Copy the code
Koa’s goal is to implement the callback part in a more simplified, streamlined, and modular way
app.use(async (ctx, next) => {
ctx.state = 'hi jinguo';
await next();
});
app.use(ctx= > {
ctx.body = ctx.state;
});
Copy the code
Koa source code analysis
Source directory structure
Lib ├ ─ ─ application. Js / / koa entry documents ├ ─ ─ context. The js / / application context of koa CTX ├ ─ ─ request. Js / / encapsulates the processing HTTP requests └ ─ ─ the response. The js / / Encapsulates processing HTTP responsesCopy the code
application.js
// Inherits events, which gives the ability to listen to events and fire events
module.exports = class Application extends Emitter {
constructor(options) {
super(a);this.middleware = []; // This array holds all middleware functions introduced through the use function
// Create context, request, and response.
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
}
// There is a wrapper around http.createserverlisten(... args) { debug('listen');
// The important thing is that the callback passed in this function includes middleware merging, context handling, and special handling of res.
const server = http.createServer(this.callback());
returnserver.listen(... args); }Use is to register middleware, putting multiple middleware into a cache queue
use(fn) {
this.middleware.push(fn);
return this;
}
// Returns a function like (req, res) => {}
callback() {
Compose compose compose compose compose compose compose compose compose compose compose compose compose will be explained below for implementing KOA on its own
const fn = compose(this.middleware);
if (!this.listenerCount('error')) this.on('error'.this.onerror);
const handleRequest = (req, res) = > {
// Encapsulate the CTX required by the middleware according to REQ and RES.
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
// Encapsulates a powerful CTX
createContext(req, res) {
// Created 3 simple objects and specified their prototypes as corresponding objects in our app. The native REq and RES are then assigned to the corresponding attributes
const context = Object.create(this.context);
const request = context.request = Object.create(this.request);
const response = context.response = Object.create(this.response);
context.app = request.app = response.app = this;
context.req = request.req = response.req = req;
context.res = request.res = response.res = res;
request.ctx = response.ctx = context;
request.response = response;
response.request = request;
return context;
}
handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404;
// Call context.js's onerror function
const onerror = err= > ctx.onerror(err);
// Process the response content
const handleResponse = (a)= > respond(ctx);
// Ensure that a stream executes the response callback when it closes, completes, and reports an error
onFinished(res, onerror);
// Middleware implementation, unified error handling mechanism key
returnfnMiddleware(ctx).then(handleResponse).catch(onerror); }}Copy the code
Application.js does four things
- Start the framework
- Implement the Onion model middleware mechanism
- Encapsulate a highly cohesive context
- Implement uniform error handling mechanism for asynchronous functions
context.js
const proto = module.exports = {
onerror(err) {
// Raises an application instance error event
this.app.emit('error', err, this); }}// The following two delegates allow context objects to delegate properties and methods of request and response
delegate(proto, 'response')
.method('attachment')
.method('redirect')
.method('remove')... delegate(proto,'request')
.method('acceptsLanguages')
.method('acceptsEncodings')
.method('acceptsCharsets')...Copy the code
Context.js does two things
- Error event handling
- Proxy partial properties and methods of response and Request objects
request.js
module.exports = {
// Request objects encapsulate many convenient properties and methods based on REq
get header() {
return this.req.headers;
},
set header(val) {
this.req.headers = val;
},
get url() {
return this.req.url;
},
set url(val) {
this.req.url = val;
},
// omits a number of similar tool attributes and methods. };Copy the code
When you access ctx.request. XXX, you are actually accessing the setter and getter on the Request object
response.js
module.exports = {
get header() {
const { res } = this;
return typeof res.getHeaders === 'function'
? res.getHeaders()
: res._headers || {}; / / the Node < 7.7
},
get body() {
return this._body;
},
set body(val) {
this._body = val;
},
// omits a number of similar tool attributes and methods. }Copy the code
A Response object is similar to a Request object
Four, simple implementation of Koa
// JKoa.js
const http = require("http");
const request = require("./request");
const response = require("./response");
class JKoa {
constructor() {
this.middlewares = []; } listen(... args) {const server = http.createServer(async (req, res) => {
// Create a context object
const ctx = this.createContext(req, res);
// Combine the middlewares, the compose function is implemented below
const fn = this.compose(this.middlewares);
await fn(ctx)
// Return data to the userres.end(ctx.body); }); server.listen(... args); } use(middlewares) {this.middlewares.push(middlewares);
}
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; }}module.exports = JKoa;
Copy the code
context
In order to simplify the API, KOA introduces the concept of context, encapsulates and mounts the original request object REq and the corresponding object RES into the context, and sets the getter and setter on the context to simplify operations.
// context.js
module.exports = {
get url() {
retrun this.request.url;
},
get body() {
return this.response.body;
},
set body(val) {
this.response.body = val; }}// request.js
module.exports = {
get url() {
return this.req.url; }}// response.js
module.exports = {
get body() {
return this._body;
},
set body(val) {
this._body = val; }}Copy the code
The middleware
Let’s start with the following example to understand the concept of function composition
function add(x, y) {
return x + y;
}
function square(z) {
return z * z
}
// Common mode
const result = square(add(1.2));
// Function combination
function compose(middlewares) {
return middlewares.reduce((prev, next) = >(... args) => next(prev(... args))); }const middlewares = [add, square];
const resultFn = compose(middlewares);
const result = resultFn(1.2);
Copy the code
The above example composition function is synchronous and can be executed one by one. If it is asynchronous, we need middleware that supports async + await
function compose(middlewares) {
return function(ctx) {
// execute 0
return dispatch(0);
function dispatch(i) {
let fn = middlewares[i];
if(! fn) {return Promise.resolve();
}
return Promise.resolve(
fn(ctx, function next() {
// Promise completes before the next one
return dispatch(i + 1); })); }}}Copy the code
The Koa middleware mechanism is the concept of function composition, which combines a set of functions that need to be executed sequentially into a single function. The arguments of the outer function are actually the return values of the inner function. The onion ring model is a visual representation of this mechanism, which is the essence and difficulty of the Koa source code.
Five, the summary
Koa is a middleware onion model mechanism based on async/await that enables request and Response objects based on node’s native REQ and RES and encapsulates them into a context.
I hope you can gain something after reading my article. Thank you for reading my article!