Express

For example, the following code creates a Web server at port 3000

const express = require("express");

const app = express();

app.listen(3000.() = > {
  console.log("start");
});
Copy the code

In my understanding express is just a function called to a set of middleware

For example, common methods for handling GET and POST requests are middleware calls

When we require express, we’re essentially importing a function

You can view the source code below as an extract of the entry file express.js

exports = module.exports = createApplication;

/**
 * Create an express application.
 *
 * @return {Function}
 * @api public* /

function createApplication() {
  var app = function (req, res, next) {
    app.handle(req, res, next);
  };

  mixin(app, EventEmitter.prototype, false);
  mixin(app, proto, false);

  // expose the prototype that will get set on requests
  app.request = Object.create(req, {
    app: { configurable: true.enumerable: true.writable: true.value: app },
  });

  // expose the prototype that will get set on responses
  app.response = Object.create(res, {
    app: { configurable: true.enumerable: true.writable: true.value: app },
  });

  app.init();
  return app;
}
Copy the code

Express.js exports a function called createApplication, which is the function that we require to import

In this function, we initialize an app function with req/res/next and we mix a lot of properties into the app with the minin function

Specific apis can consult expressjs.com/en/4x/api.h…

The middleware

A callback function passed to Express the middleware is located between the request and the response so it can

• Execute any code

• Change the request and response objects

• End the request response period res.end

• Call the next middleware

We can use the app.use method to register the middleware globally so that all routes will match the middleware

You can also use middleware in specific routing as shown below

const middlewareA = ((req,res,next) = >{
  do something...
})

app.use(middlewareA)

app.get('/', middlewareA ,(req,res,next) = >{
  do something...
})
Copy the code

Managed static file

const express = require("express");
const path = require("path");

const app = express();

// Use /static to access the public folder
app.use("/static", express.static(path.join(__dirname, "public")));

app.listen(8080.() = > {
  console.log("Static resource server deployed successfully");
});
Copy the code

get

The parameters of the get request are all in the URL and we can get them in the form of query and params

const express = require("express");

const app = express();

app.get("/query".(req, res, next) = > {
  res.json(req.query);
});

app.get("/param/:id".(req, res, next) = > {
  res.json(req.params);
});

app.listen(3000.() = > {
  console.log("start");
});
Copy the code

post

The parameter of the POST request is in the body but if we print the body directly we won’t see any results

This requires the introduction of the body-Parser library

This library integrates with the different versions of Express as follows

  • 3. The built-in x

  • 4. X

  • 4.16 Built-in functions

So we need to manually install this third-party library for versions prior to 4.x 4.16

const express = require("express");

const app = express();

// The old version is not recommended
// var bodyParser = require("body-parser");
// app.use(bodyParser.urlencoded({ extended: false }));
// app.use(bodyParser.json());

// We recommend writing after 4.16
// Extended False is parsed using Node's built-in queryString module
// True means using the third-party module QS
app.use(express.urlencoded({ extended: false }));
app.use(express.json());

app.post("/login".(req, res, next) = > {
  console.log(req.body);
});

app.listen(8000.() = > {
  console.log("start");
});
Copy the code

File upload

If you do this in the native way it’s going to be a lot of trouble in the background because the binary stream has not only the information about the file but also some information about the header so we can print out some of that data and look at it

const express = require("express");

const app = express();

app.post("/upload".(req, res, next) = > {
  console.log(req.headers);
  let data = "";
  req.on("data".(buffer) = > {
    data += buffer;
  });
  req.on("end".() = > {
    console.log(data.toString());
  });
});

app.listen(8080.() = > {
  console.log("start~~~");
});
Copy the code

Localhost :8080/upload = localhost:8080/upload = localhost:8080/upload = localhost:8080/upload = localhost:8080/upload = localhost:8080/upload

Headers information is as follows

{
  "user-agent": "PostmanRuntime / 7.13.0"."accept": "* / *"."cache-control": "no-cache"."postman-token": "e48f538d-2988-4e39-8d50-80fdede0ed02"."host": "localhost:8080"."accept-encoding": "gzip, deflate"."content-type": "multipart/form-data; boundary=--------------------------372446874075094600561084"."content-length": "376074"."connection": "keep-alive"
}
Copy the code

If you look closely, you will see that the binary stream contains the content-type in the header and the MIME type of the file. If you write the binary stream to the file using the FS module without any processing, the final file will not be parsed correctly

If you introduce the Multer module, the file upload function becomes easy

For example, we need the user to upload the avatar code as follows

const express = require("express");
const multer = require("multer");
const path = require("path");

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    // Node callbacks with err as first argument are error-first callbacks
    // The second parameter is the destination of the file upload
    cb(null."uploads");
  },
  filename: function (req, file, cb) {
    // Again, the first parameter is the error message and the second parameter is the file name
    cb(
      null,
      file.fieldname + "-" + Date.now() + path.extname(file.originalname) ); }});const upload = multer({ storage: storage });

const app = express();

app.post("/upload", upload.single("avatar"), (req, res, next) = > {
  console.log(req.file);
});

app.listen(8080.() = > {
  console.log("start~~~");
});
Copy the code

The above method demonstrates a single file upload to req.file where the file information is stored as follows

{
  "fieldname": "avatar"."originalname": "CHOU16.jpg"."encoding": "7bit"."mimetype": "image/jpeg"."destination": "uploads"."filename": "avatar-1616384964609.jpg"."path": "uploads\\avatar-1616384964609.jpg"."size": 375864
}
Copy the code

If you need to support multiple file uploads, for example, you also need to upload user background images, the code can be rewritten as follows

app.post(
  "/fields",
  upload.fields([
    { name: "avatar".macCount: 1 },
    { name: "bg".maxCount: 2},]),(req, res, next) = > {
    console.log(req.files); });/ / {
// avatar: [
/ / {
// fieldname: 'avatar',
// originalname: 'CHOU1.jpg',
// encoding: '7bit',
// mimetype: 'image/jpeg',
// destination: 'uploads',
// filename: 'CHOU1.jpg',
// path: 'uploads/CHOU1.jpg',
// size: 646567
/ /}
/ /,
// bg: [
/ / {
// fieldname: 'bg',
// originalname: 'CHOU2.jpg',
// encoding: '7bit',
// mimetype: 'image/jpeg',
// destination: 'uploads',
// filename: 'CHOU2.jpg',
// path: 'uploads/CHOU2.jpg',
// size: 398519
/ /}
/ /]
// }
Copy the code

If you want to upload multiple background images to the same field, you can use the array method. The second parameter is the maximum number of images that can be uploaded

If the maximum number is exceeded, multer will return MulterError: Unexpected field

app.post("/array", upload.array("bg".3), (req, res, next) = > {
  console.log(req.files);
});

/ / /
/ / {
// fieldname: 'bg',
// originalname: 'CHOU1.jpg',
// encoding: '7bit',
// mimetype: 'image/jpeg',
// destination: 'uploads',
// filename: 'CHOU1.jpg',
// path: 'uploads/CHOU1.jpg',
// size: 646567
/ /},
/ / {
// fieldname: 'bg',
// originalname: 'CHOU2.jpg',
// encoding: '7bit',
// mimetype: 'image/jpeg',
// destination: 'uploads',
// filename: 'CHOU2.jpg',
// path: 'uploads/CHOU2.jpg',
// size: 398519
/ /}
// ]
Copy the code

router

If all request processing is handled in index.js, the index.js code will become bloated. In this case, we can use the route to break up our code

For example, there is a user module in the project to achieve the function of adding, deleting, changing and checking

We can create a new user.js file

const express = require("express");

const router = express.Router();

router.get("/add".(req, res, next) = > {
  res.end("added");
});

router.post("/delete".(req, res, next) = > {
  res.end("deleted");
});

router.post("/update".(req, res, next) = > {
  res.end("updated");
});

router.post("/select".(req, res, next) = > {
  res.end("selected");
});

module.exports = router;
Copy the code

Then import the route in index.js

const express = require("express");
const userRouter = require("./user");

const app = express();

app.use("/user", userRouter);

app.listen(3000.() = > {
  console.log("start");
});
Copy the code

DEMO

The middleware in Express is all synchronous code, that is, one piece of middleware executes before the other piece of middleware executes

For example, we have the following functions to append content to message respectively in ABC middleware and output results in MIDDLEWARE A

const express = require("express");

const app = express();

const middlewareA = (req, res, next) = > {
  req.message = "";
  req.message += "A";
  next();
  res.end(req.message);
};

const middlewareB = (req, res, next) = > {
  req.message += "B";
  next();
};

const middlewareC = async (req, res, next) => {
  req.message += "C";
  next();
};

app.use(middlewareA);
app.use(middlewareB);
app.use(middlewareC);

app.listen(8000.() = > {
  console.log(8000);
});
Copy the code

The next function finds the next middleware in the stack and executes, so the res.end in the middlewareA executes ABC only after all middleware has been executed

So what happens if we simulate asynchronous operations with timers in the third middleware

MiddlewareC is transformed as follows

const middlewareC = (req, res, next) = > {
  setTimeout(() = > {
    req.message += "C";
  }, 0);
  next();
};
Copy the code

By accessing port 8000 we can see that this time the final output becomes AB and so you can see that

The code in the middleware is invoked synchronously

This is a weakness of Express for asynchronous scenarios, unlike KOA

Koa

Koa supports the use of async and await functions, which means that koA can throw away the writing of callback functions in Express to address asynchronous scenarios in a more elegant way

The basic use

Unlike Express, KOA exports not a function but an object called Application

So we only need an instance of new to use it. Otherwise, it is similar to Express

const Koa = require("koa");

const app = new Koa();

app.listen(8080.() = > {
  console.log("Koa");
});
Copy the code

routing

In this case, we’re going to use a third party module called KOA-Router because KOA itself is very pure and basically everything has to be done through a third party plug-in

Create a new route module Koa of user.js and combine the request and response in Express into the context object context abbreviated as CTX

const Router = require("koa-router");

const userRouter = new Router({ prefix: "/user" });

userRouter.get("/home".(ctx, next) = > {
  ctx.body = "welcome~~";
});

userRouter.post("/login".(ctx, next) = > {
  ctx.body = "login";
});

module.exports = userRouter;
Copy the code

Then introduce user.js into the index

const Koa = require("koa");

const userRouter = require("./router");

const app = new Koa();

app.use(userRouter.routes());

app.listen(8080.() = > {
  console.log("Koa");
});
Copy the code

Handle the request

Koa needs to introduce koA-BodyParser to parse JSON and Urlencoded

const Koa = require("koa");
const Router = require("koa-router");
const bodyParser = require("koa-bodyparser");

const app = new Koa();
const router = new Router();

app.use(bodyParser());
app.use(router.routes());

/ / query
router.get("/query".(ctx, next) = > {
  console.log(ctx.request.query);
});

/ / parsing params
router.get("/params/:id".(ctx, next) = > {
  console.log(ctx.request.params);
});

/ / parsing urlencoded
router.post("/urlencoded".(ctx, next) = > {
  console.log(ctx.request.body);
});

/ / parsing json
router.post("/json".(ctx, next) = > {
  console.log(ctx.request.body);
});

app.listen(8080.() = > {
  console.log("start");
});
Copy the code

Note that the koa-BodyParser middleware needs to be used first

Asynchronous processing

Going back to the Demo in Express makes it very easy to handle asynchronous operations in KOA

const Koa = require("koa");
const axios = require("axios");
const app = new Koa();

const middlewareA = async (ctx, next) => {
  ctx.msg = "";
  ctx.msg += "A";
  await next();
  ctx.body = ctx.msg;
};

const middlewareB = async (ctx, next) => {
  ctx.msg += "B";
  await next();
};

const middlewareC = async (ctx) => {
  const res = await axios.get("https://koa.bootcss.com/");
  ctx.msg += res.data;
};

app.use(middlewareA);
app.use(middlewareB);
app.use(middlewareC);

app.listen(8080.() = > {
  console.log("Koa");
});
Copy the code

The onion model

The onion model is actually not a tall and fashionable concept, as we can see from the following figure, right

All the middleware is accessed twice by the request just like peeling the onion and that’s the onion model

Express contrast koa

  • Express is complete and powerful, which helps us build in a lot of useful features;

  • Koa is clean and free, it only contains the core functionality and does not restrict our use of other middleware. Even the most basic get and post are not provided to us in the app; We need to determine the request mode or other functionality by ourselves or by the route

  • Koa middleware supports async await