Node. Js HTTP modules

How to use the Node.js HTTP module?

The HTTP module in Node.js is the foundation on which Node can function as a Webserver. Is a native module. Based on this module, Node.js has the ability to handle HTTP requests. Creating an HTTPServer using HTTP modules is very simple and takes only a few lines of code

const http = require('http')

const server = http.createServer((req, res) = > {
  // Handle request and response
  rse.end(`The request path is ${req.url}`);
})

server.listen(3000.() = > {
  console.log('The server is listening at 3000')})Copy the code

This creates the simplest HTTP server possible. As you can see, the callback function in createServer is the service logic for our entire server. In this example, we directly return the url information requested by the user.

  const server = http.createServer((req, res) = > {
    // Handle request and response
    rse.end(`The request path is ${req.url}`);
  })

Copy the code

This callback takes two arguments, one req and the other res. The HTTP module of Node encapsulates the user’s request and response, respectively. For detailed documentation, refer to http.ClientRequest and http.serverResponse. The content here is a little bit too much, and I will write a separate article to introduce it accordingly. All we need to know here is that all HTTP operations are based on two objects.

Koa.js is essentially a wrapper around HTTP modules and provides middleware loading capabilities. The koa.js logic is wrapped in a function called callback, the most important of which is compose. So to study koa.js is essentially to study what the compose function is doing.

module.exports = class Application extends Emitter {
  /**
   * Shorthand for:
   *
   *    http.createServer(app.callback()).listen(...)
   *
   * @param {Mixed} . *@return {Server}
   * @api public* /

  listen(. args) {
    debug("listen");
    const server = http.createServer(this.callback());
    returnserver.listen(... args); }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);
    };

    returnhandleRequest; }};Copy the code

Middleware in koa.js

Koa.js provides two functions, one is HTTP request and response, and this is a call to the HTTP module in Node.js. Another important feature is middleware.

How to use koA.js middleware?

According to the documentation example, we can use the use function to mount the middleware we need to Koa.

const Koa = require("koa");

const app = new Koa();

app.use(async function (ctx, next) {
  console.log(">> one");
  await next();
  console.log("<< one");
});

app.use(async function (ctx, next) {
  console.log(">> two");
  await next();
  ctx.body = "two";
  console.log("<< two");
});

app.use(async function (ctx, next) {
  console.log(">> three");
  await next();
  console.log("<< three");
});

app.listen(8080);


Access browser / * * *, we can see * > > one * > > two * > > < < three three * * * < < < < two one * /Copy the code

We can look at the example in codesanbox. We can see that the statements before next are executed in sequence, and the functions after Next are executed in reverse order, which is a bit like a stack. Why did this happen? This brings us to koA’s perennial onion ring model.

What is an onion ring model?

The Onion ring model, in effect, is an HTTP request that passes once on the way in and passes again on the way back. Their cut-off point is at the next function. Thus once middleware has two opportunities to process a complete HTTP request.

             +-----------------+
             |                 |
             |   +----------+  |
             |   |          |  |
             |   |  +----+  |  |
             |   |  |    |  |  |
    Request  |   |  |    |  |  |  Response
------------------->| |------------------>
             |   |  |    |  |  |
             |   |  |    |  |  |
             |   |  +----+  |  |
             |   |          |  |
             |   +----------+  |
             |                 |
             +-----------------+
Copy the code

Middleware engine implementation, Koa-compose

The properties of Middleware can be summarized in terms of stacks.

  • Mount operations First in, last out.
  • There is a unified contextctx.

Going back to the Koa initialization example above, WHEN Koa creates an HTTP Server, callback is passed in as a callback function.


  use (fn) {
    if (typeoffn ! = ='function') throw new TypeError('middleware must be a function! ')
    debug('use %s', fn._name || fn.name || The '-')
    this.middleware.push(fn) The use function adds our middleware to the middleware array
    return this
  }

  callback () {
    const fn = compose(this.middleware) // Pass the middleware array into the compose function to get a new function.

    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
  }


  handleRequest (ctx, fnMiddleware) {
    const res = ctx.res
    res.statusCode = 404
    const onerror = err= > ctx.onerror(err)
    const handleResponse = () = > respond(ctx)
    onFinished(res, onerror)
    return fnMiddleware(ctx).then(handleResponse).catch(onerror) // In the handleRequest function, the CTX object is passed directly to the fn for execution.
  }

Copy the code

The compose function is the core of koA. Excluding some exception handling statements, let’s see what Compose does

function compose (middleware) {
  return function (context, next) {
    // Records the last middleware invocation
    let index = -1
    return dispatch(0)
    function dispatch (i) {
      index = i // Assign the middleware index
      let fn = middleware[i]
      if (i === middleware.length) fn = next
      try {
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1))) // Bind the index of the next middleware
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}
Copy the code

Here, through the chain call of bind, the next mounted middleware is passed in as the next parameter to achieve the purpose of controlling the middleware queue. The context object is also passed in as an argument.

The core implementation of koa.js

With an understanding of compose’s principle, we can now implement a simple koa.js on our own.

Let’s create an Application class first. Then write the core functions well, mainly two categories, namely HTTP request processing handleRequest, and middleware related use, callback.

const Emitter = require("events");
const compose = require("koa-compose");
const http = require("http");

module.exports = class Application extends Emitter {
  /**
   * Initialize a new `Application`.
   *
   * @api public* /
  constructor(options) {
    super(a);this.middleware = [];
    this.context = Object.create({});
  }

  /** * Calls the underlying HTTP module createServer */
  listen(. args) {
    const server = http.createServer(this.callback());
    returnserver.listen(... args); }/** * Mount middleware */
  use(fn) {
    if (typeoffn ! = ="function")
      throw new TypeError("middleware must be a function!");
    this.middleware.push(fn);
    return this;
  }

  /** * Create a generic context */
  createContext(req, res) {
    let context = Object.create(this.context);
    context.req = req;
    context.res = res;
    return context;
  }

  /** * HTTP request processing callback function */
  callback() {
    const fn = compose(this.middleware); // here we still use koa-compose.

    const handleRequest = (req, res) = > {
      const ctx = this.createContext(req, res); // Create the request context
      return this.handleRequest(ctx, fn);
    };
    return handleRequest;
  }

  /**
   * Handle request in callback.
   *
   * @api private* /

  handleRequest(ctx, fnMiddleware) {
    const res = ctx.res;
    res.statusCode = 404;
    const handleResponse = () = > ctx.res.end();
    returnfnMiddleware(ctx).then(handleResponse); }};Copy the code

So if you’re interested in running this example on codesandbox, see what happens, and I’ve posted the address of my experiment here. Koa example

conclusion

Koa lays the foundation for the operation of the whole framework and the direction of subsequent development through the creation of Context objects and the design of middleware mechanism. All subsequent functions, such as koA-Router and KOA-Logger, are developed and loaded in the form of middleware. The rest of the content will focus on how to extend the functionality of KOA by developing middleware.