This article is from “Xintan blog · In-depth KOA source code: Architecture Design”
Front-end interviews, design pattern manuals, Webpack4 tutorials, NodeJs practice and more, please come to the navigation page for food
All articles in the series are hosted on Github. Star ✿ years (° °) Blue ✿
I recently read the source code of KOA to clarify the architecture design and the third party library used. This three-part series introduces the architecture of KOA and the principles of the three core libraries, culminating in a manual implementation of a simple KOA.
The implementation of KOA is all in the repository’s lib directory, as shown below, with only four files:
These four files can be divided into three categories based on purpose and encapsulation logic: REQ and RES, context, and Application.
The req and res
The corresponding files are request.js and response.js. The client requests information and the server returns information.
The two files are logically identical in implementation. All attributes on the object use getters or setters to achieve read and write control.
context
The corresponding file is context.js. Stores context information about the running environment, such as cookies.
In addition, since both Request and response are context information, the delegate.js library is used to delegate all properties on Request.js and Response.js. For example:
/** * Response delegation. */
delegate(proto, "response")
.method("attachment")
.method("redirect");
/** * Request delegation. */
delegate(proto, "request")
.method("acceptsLanguages")
.method("acceptsEncodings");
Copy the code
Another benefit of using proxies is easier access to properties on REq and RES. For example, when developing a KOA application, you can read the headers of a client request using ctx.headers instead of ctx.res.headers (which is correct).
Note: req and RES are not bound to context in context.js, but to context variable CTX in application. The reason is that the REQ/RES is not the same for each request.
Application
The corresponding file is: application.js. The logic of this file is the most important, and its main purpose is:
- Expose the service startup interface to the user
- For each request, a new context is generated
- Process the middleware and chain it together
External exposed interface
With KOA, we usually start the server with listen or callback:
const app = new Koa();
app.listen(3000); / / listen to start
http.createServer(app.callback()).listen(3000); / / callback
Copy the code
The two startup methods are completely equivalent. Because inside the Listen method, callback is called and passed to http.createserver. The callback method does the following:
- call
koa-compose
Wire middleware together (more on that later). - Generated to
http.createServer()
Function, and returns.
http.createServer
Both the request information and the return information passed to the function parameters are taken by the function. And to thecreateContext
Method to generate the context for this request.- Pass the generated context to the middleware call chain generated in step 1. Is that why we can access CTX while the middleware processes the logic
Generating a new context
The context method here corresponds to the createContext method. Here I think it’s more like grammar candy to make it easier for KOA users to use. Take this code for example:
// this.request is the object exposed by request.js, and its reference is stored in context.request
// The user can access the corresponding property directly through the CTX. Property name
const request = (context.request = Object.create(this.request));
// This req is the request information passed to the callback function by http.createserver
context.req = request.req = response.req = req;
Copy the code
Headers is a syntactic sugar for ctx.request. Headers, though. But it feels weird. In this example, ctx.headers accesses the headers on ctx.reqeust, not the headers on this request message. This request is posted on ctx.req.
Let’s go back to the source code for reqeust.js and see the getter implementation of headers:
get headers() {
return this.req.headers;
}
Copy the code
Ok, so this is the context of this. So somewhere in application.js you must have changed the reference to this. Sure enough, you can see it in the application.js constructor:
this.request = Object.create(request);
Copy the code
Request on the application instance is passed to context.request, and this naturally refers to the context.
As you can see, KOA does a lot of work on the context to make it easy for developers to use.
Middleware mechanisms
Middleware design is the most important part of KOA, and koA-compose library is used to connect the middleware, forming the “Onion model”. This library is explained in the second introduction to the KOA core library.
The middleware functions in application are use and handleRequest:
use
Function: pass inasync/await
Function and put it on the Application instancemiddleware
In the array. Is called if the incoming is generatorkoa-conver
The library converts it toasync/await
Function.handleRequest(ctx, fnMiddleware)
Function: passed infnMiddleware
It is the middleware that has been concatenated, and all the function does is add a function and error handler back to the client. The function returned to the client is essentiallyrespond
Function, inside by callingres.end()
Return the information to the client and the process is complete.