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 contextctxEncapsulates the request and response
  • contextctxAnd the functionfnUnder 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