As mentioned in the previous article, this.callback() returns a callback function, in the form of a closure that returns a local function variable handleRequest that the Server calls to handle HTTP requests.
callback() {
const fn = compose(this.middleware);
const handleRequest = (req, res) = > {
const ctx = this.createContext(req, res);
return this.handleRequest(ctx, fn);
};
return handleRequest;
}
Copy the code
When the request arrives, the Server passes the native Request and response provided by Node to the callback handleRequest, which does two things:
- Create a context
ctx
Encapsulates the request and response - context
ctx
And the functionfn
Under thethis.handleRequest()
To deal with
Let’s look at how the context CTX is created and used.
Create context CTX
Pass the native Request and response provided by Node directly to this.createcontext ().
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
The code seems very repetitive, so let’s sort it out:
attribute | meaning |
---|---|
context / .ctx | context |
req / .req | The Node requests |
res / .res | The Node response |
request / .request | Koa request |
response / .response | Koa response |
This is a cross-reference between context, Node request & response, and Koa request & response for ease of use.
How does CTX encapsulate the request and response? What is the relationship between Node request & response and Koa request & response? This brings us to the delegation pattern used by Koa.
Delegate pattern
The Delegation Pattern is one of the design patterns, meaning that exposed objects delegate requests to other objects inside for processing.
As we can see from context.js, Koa uses the NPM package delegates things that should be handled by the context CTX to Request and Response, which are from Request.js and Response.js.
/* context.js */
const delegate = require('delegates');
const proto = module.exports = {
/* Here are some methods and attributes that the context itself does */
}
/* Delegate to response */
delegate(proto, 'response')
.method('attachment')
.method('redirect')
.access('status')
.access('body')
.access('length')
/ *... * /
/* Delegate to request */
delegate(proto, 'request')
.method('acceptsLanguages')
.method('acceptsEncodings')
.access('method')
.access('query')
.access('path')
.access('url')
.getter('host')
.getter('hostname')
.getter('URL')
/ *... * /
Copy the code
As a result, our operations on context CTX, such as ctx.type and ctx.length, will be performed by response objects, and ctx.path and ctx.method will be performed by Request objects. Don’t forget that Response and Request are Koa’s own requests and responses. How do they relate to Node requests & responses?
Request and Response
Again, it’s not the context CTX that really puts the request and response operations in place, but the Request object from Request.js and the response object from Response.js. Let’s look at the implementation of these two objects.
/* request.js */
module.exports = {
/ *... * /
/** * Get request URL. * * @return {String} * @api public */
get url() {
return this.req.url;
},
/ *... * /
}
Copy the code
/* response.js */
module.exports = {
/ *... * /
/** * Check if a header has been written to the socket. * * @return {Boolean} * @api public */
get headerSent() {
return this.res.headersSent;
},
/ *... * /
}
Copy the code
Koa requests/responses to Node requests/responses. The context CTX delegates to the Koa request/response, and the Koa request/response operation Node requests/responses, thus implementing the complete request/response processing flow.
This relationship is understood, the context of Koa CTX is also understood.
Getting POST parameters is a common problem in development
As mentioned earlier, ctx.query is delegated to Request, which encapsulates the Query in the Node request, so we can use ctx.query directly to retrieve the GET parameter.
POST requests do not have this encapsulation and need to parse Node’s native request to get its parameters.
app.use( async ( ctx ) => {
if ( ctx.url === '/' && ctx.method === 'POST' ) {
// When a POST request is made, the data in the POST form is parsed and displayed
let postData = await parsePostData( ctx )
ctx.body = postData
}
})
// Parse the POST parameter of Node's native request in context
function parsePostData( ctx ) {
return new Promise((resolve, reject) = > {
try {
let postdata = "";
ctx.req.addListener('data', (data) => {
postdata += data
})
ctx.req.addListener("end".function(){
let parseData = parseQueryStr( postdata )
resolve( parseData )
})
} catch ( err ) {
reject(err)
}
})
}
// Parse the POST request parameter string into JSON
function parseQueryStr( queryStr ) {
let queryData = {}
let queryStrList = queryStr.split('&')
console.log( queryStrList )
for ( let [ index, queryStr ] of queryStrList.entries() ) {
let itemList = queryStr.split('=')
queryData[ itemList[0]] =decodeURIComponent(itemList[1])}return queryData
}
/ / code from: https://chenshenhai.github.io/koa2-note/note/request/post.html
Copy the code
You can also directly use the NPM package koA-BodyParser as middleware for POST data processing.
const bodyparser = require('koa-bodyparser')
app.use(bodyparser())
app.use( async (ctx) => {
if (ctx.url === '/' && ctx.method === 'POST') {
let data = ctx.request.body
ctx.body = data
}
})
Copy the code