Check the Koa, [email protected] source code, there are only four files in total application.js, context.js, request.js, response.js; They correspond to the Koa application entry, context, request object, and response object
A, application. Js
1, the overview
As you can see, this module uses the Node internal module Events as its parent class to export a constructor that creates the Koa application. This constructor has several property values, which we will focus on
2, attributes,
- Middleware, an array of middleware;
- Context, a context object;
- Request c.
- Response, response object;
3, methods,
3.1 listen
We usually just pass in a number to start the service at a certain port number. You can see that inside Listen calls the method used by Node to create the HttpServer. The HTTP. CreateServer method accepts a request listener function. The two parameters of this function are req request object and RES response object respectively. Look at the callback method,
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;
}
Copy the code
Start by returning a function for chained processing middleware via compose, and here’s how the Componse method is wrapped. Check whether the middleware array meets the requirements first, and then see in the function returned, Resolve will return the execution result of middleware through promise.resolve. The first parameter of middleware is CTX, and the second parameter is next. It can be seen here that Dispatch is the corresponding next. This forms a promise about the order of execution of the onion model in Koa.
/ / componse method
function compose (middleware) {
if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array! ')
for (const fn of middleware) {
if (typeoffn ! = ='function') throw new TypeError('Middleware must be composed of functions! ')}/** * @param {Object} context * @return {Promise} * @api public */
return function (context, next) {
// last called middleware #
let index = - 1
return dispatch(0)
function dispatch (i) {
if (i <= index) return Promise.reject(new Error('next() called multiple times'))
index = i
let fn = middleware[i]
if (i === middleware.length) fn = next
if(! fn)return Promise.resolve()
try {
return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
} catch (err) {
return Promise.reject(err)
}
}
}
}
Copy the code
It defines a function called handleRequest in the callback, but this is just the name of a function variable, and the handleRequest in the callback is the one we want to analyze. In order to facilitate the explanation of the view, can be temporarily rewritten into the following writing.
callback() {
const fn = compose(this.middleware);
if (!this.listenerCount('error')) this.on('error'.this.onerror);
const xxx = (req, res) = > {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return xxx;
}
Copy the code
In XXX function, the createContext method is first used to mount the object such as request message and response message on the context object, where Request is the Koa request object, response is the Koa response object, APP is the current Koa service instance object, req is the Node request object. Res is the Node response object. The handleRequest method is then executed, taking the context and the function that handles the middleware. Finally returns the result of the execution of a request. Here you can see that the return status code defaults to 404
createContext(req, res) {
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;
context.originalUrl = request.originalUrl = req.url;
context.state = {};
return context;
}
Copy the code
handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404;
const onerror = err= > ctx.onerror(err);
const handleResponse = (a)= > respond(ctx);
onFinished(res, onerror);
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}
Copy the code
3.2 handleRequest, createContext
The two methods used in callback above
3.3 the use
The use method is simple and simply pushes a single piece of middleware into an array
3.3 toJSON
The toJSON method introduces an only package that, when opened, filters an object and returns a new object containing only the specified property values
3.4 onerror
Printing error logs
3.5 respond
Processing the response result object, involving some concepts of buffers and streams, and finally returning the response result
Second, the context. Js
The return is the object prototype of the context
1, inspect
Return the current object
2, toJSON
An object that returns request and response messages and some other properties
3, assert, onerror
Exception handling
Get cookies,set cookies
Cookies value and assignment function
5, the delegate
As you can see from the above, the current context prototype has very few properties and methods, and the rest of the properties and methods are defined via a delegate. View the delegate dependency package
function Delegator(proto, target) {
if(! (this instanceof Delegator)) return new Delegator(proto, target);
this.proto = proto;
this.target = target;
this.methods = [];
this.getters = [];
this.setters = [];
this.fluents = [];
}
Copy the code
Return an object that takes Delegator as its constructor. The returned object defines the properties listed above. The proto parameter is the context prototype object passed in,target because the Delegator function calls response and Request twice Then the Delegator prototype has methods method, Access, getter, setter, fluent that return the original object when called, so you can chain call it when used in context.js. Take the method method as an example:
Delegator.prototype.method = function(name){
var proto = this.proto;
var target = this.target;
this.methods.push(name);
proto[name] = function(){
return this[target][name].apply(this[target], arguments);
};
return this;
};
Copy the code
Call this method, first save the method name into the method collection array, then define the property method on the original object, in this case proto is the context prototype object, and then pass the target parameter to indicate whether the method uses the method on the Response or Request object. The conclusion is that the context prototype object defines the important attributes such as Response and Request, and at the same time binds the methods in these two objects directly to the prototype object quickly. Therefore, response and Request can be omitted when some attributes or methods are used.
Third, the request. Js
Define a Request object to mount and return properties and methods of Node request message REq
Header and headers are both request headers
2. Urls, etc
3. Mount attributes from some Node request objects on Koa request object request
Fourth, the response. Js
Similar to Request, it returns a Response object. Recall the createContext method in application.js;
Where, Request is the Koa request object, response is the Koa response object, APP is the current Koa service instance object, REq is the Node request object, res is the Node response object