The project was interested in using KOA, CTX and asynchronous middleware execution, so it just implemented it
- You need to know what this refers to in JS
- A clear understanding of promsie is required
Do a systematic knowledge of the properties on koA CTX
ctx.req.request.url // ctx.req = req
ctx.request.req.url // ctx.request.req = req;
ctx.request.url //ctx.request is wrapped by KOA
ctx.url // CTX proxies ctx.request
Copy the code
Encapsulation of appliction files
const http = require("http");
const context = require("./context");
const request = require("./request");
const response = require("./response");
class Mkoa {
constructor() {
this.middlewares = [];
this.context = context;
this.request = request;
this.response = response;
}
use(cb) {
this.middlewares.push(cb); // Save all middleware
}
createContext(req, res) {
let ctx = Object.create(this.context); // CTX can retrieve properties of the global context and modify the CTX without affecting the context
// The context mounts the request ctx.request to the external request file for extension
ctx.request = Object.create(this.request);
ctx.req = ctx.request.req = req;
// Mount the response on CTX
ctx.response = Object.create(this.response);
ctx.res = ctx.response.res = res;
return ctx;
}
// Return a promise that is executed in the middleware
componse(ctx, middlewares) {
function dispatch(index) {
if (index === middlewares.length) {
return Promise.resolve();
}
const middleware = middlewares[index];
return Promise.resolve(
middleware(ctx, () => {
return dispatch(index + 1); })); }return dispatch(0);
}
hanleRequest(req, res) {
let ctx = this.createContext(req, res); // Create CTX based on req res
let composeMiddleware = this.componse(ctx, this.middlewares); // Componse implements internal middleware, returning a promise that all middleware is executed sequentially
// After all the middleware has executed, check whether there is a value on the body and return it accordingly
// The body type can be set to different request header string streams
composeMiddleware.then((a)= > {
let body = ctx.body;
if (typeof body === "undefined") {
res.end(`not found`);
} else if (typeof body === "string") {
res.end(body);
}
});
}
listen(...arg) {
let app = http.createServer(this.hanleRequest.bind(this));
app.listen(...arg);
}
}
module.exports = Mkoa;
Copy the code
Encapsulate the request file
let url = require('url');
const request = {
Ctx.request.url => ctx.request.url.call(ctx.request)
// This refers to ctx.request
// ctx.req = ctx.request.req = req
get url(){
return this.req.url
},
get path(){
return url.parse(this.req.url).pathname
}
}
module.exports = request
/* Fn. Call (context,a,b) obj.child.method(p1, p2) is equivalent to obj.child.method.call(obj.child, p1, p1). p2); var obj = { foo: Function (){console.log(this)}} var bar = obj.foo obj.foo() // Convert obj.foo.call(obj), Function fn (){function fn (){function fn (){function fn (){function fn (){function fn (){function fn (){ Console. log(this)} var arr = [fn, fn2] arr[0]() * /
Copy the code
Encapsulate the response file
const response = {
set body(value){
this.res.statusCode = 200;
this._body = value // Save the value
},
get body(){
this.res.statusCode = 404;
return this._body
}
}
module.exports = response
Copy the code
The context encapsulation
// Access the properties on CTX directly
const context = {
}
// Delegate from koA source code
function defineGetter(property,name){
// context[name] returns context[property][name]
context.__defineGetter__(name,function(){
return this[property][name]
})
}
// ctx.body => ctx.body.call(ctx)
function defineSetter(property,name){
// context[name] returns context[property][name]
context.__defineSetter__(name,function(value){
return this[property][name] = value
})
}
// KoA has a delegate method inside and we'll just do a simple implementation
defineGetter('request'.'url'); // Complete access chain ctx.url => ctx.request.url => ctx.request.req.url
defineGetter('request'.'path');
defineGetter('response'.'body')
defineSetter('response'.'body')
module.exports = context
Copy the code
Write a running example
let koa = require("./ckoa/application");
let app = new koa();
function logger() {
return new Promise((resolve, reject) = > {
setTimeout((a)= > {
console.log("Problem with asynchronous order");
resolve();
}, 2000);
});
}
/** * If there is asynchron in a middleware, we need to use await next() to ensure that the next middleware is finished
app.use(async (ctx, next) => {
console.log(1);
await next();
console.log(2);
});
app.use(async (ctx, next) => {
console.log(3);
await logger()
next();
console.log(4);
});
app.use((ctx, next) = > {
console.log(5);
next();
console.log(6);
});
app.listen(3001);
Copy the code
File analysis
Ctx.request = object.create (our custom extended request) Ctx.request can access the properties defined on the request through the prototype chain. If you look at the REQUEST file in the KOA source code, **** this.req.url this.req.header is a native req ctx.request.url. First of all, ctx.request does not have a URL property, You need to go through the stereotype chain to find that this on the stereotype refers to the instance attribute. Or ctx.request.url() => ctx.request.url.call(ctx.request) => this refers to ctx.request ctx.request.req.url which is actually req.url because ctx.req = ctx.request.req = req; Ctx. url first of all, CTX does not have a url, so it will find the context file, because the CTX prototype points to the context file, but the context file is further proxied. Ctx. url is actually ctx.request.url, which is accessed via a delegate, and then it goes back to the original ctx.request.req.url ctx.req = ctx.request.req = req so there's a *** ctx.request.req.** is an attribute on the accessed native REq. Ctx.req. path ctx.request.req.path is unreachable but ctx.request.path ctx.req Ctx.request. path returns url.parse(this.req.url). Pathname. This is ctx.request. In the same way, Path ctx.response = Object.create(Response) ctx.res = ctx.response.res = ctx.response Ctx.response.body => access the body in response after the context is proxied. This is ctx.response.body, and we set this.res.statusCode = 200; In fact set the CTX. Response. Res., statusCode = 200 * /
Copy the code
Componse Problems encountered when serial middleware
Failed to understand why return dispatch(index+1) must involve the nested problem of promises. Fulfill a promise and fill it later
Promise.resolve(middleware(ctx,()=>{
return dispatch(index+1)}))Copy the code