preface
This article will introduce some of the project construction in KOA’s project practice and some of the solutions. I will introduce the basic knowledge of KOA to better enter the practice of KOA project. Let’s take a look.
Front knowledge
To use KOA in a project, it is necessary to first understand the operation mechanism of KOA and master the implementation principle of middleware. Koa is based on the Onion model. So let’s look at KOA’s onion model.
Koa’s onion model
Onions we all know that one layer is layered on top of another, but instead of looking at the three-dimensional structure, we need to cut the onion, from the point of view of the cut plane:
You can see that in order to go through the center of the onion, you have to go layer by layer through the skin of the onion into the center, and then layer by layer through the skin of the onion from the center.
Here is a feature: as many layers of skin as you enter, you must exit. First through the skin, then through the skin, in line with what we call the stack list, first in, last out principle. And that’s how our Onion model works.
Then back to the Node.js framework, the skin of the onion can be thought of as middleware:
- Outside-in process is a key word next();
- And from the inside out is the completion of each middleware, into the next layer of middleware, until the last layer.
Koa’s middleware runs based on the Onion model.
Middleware execution
To understand the onion model and its implementation, let’s use KOA as a framework example to implement a back-end service. There is some preparatory work to be done here, and you can initialize the project by following these steps.
mkdir myapp
cd myapp
npm init
npm install koa --save
touch app.js
Copy the code
The following code, in which the app.use section is four middleware. One of them is asynchronous middleware.
const Koa = require("koa");
const app = new Koa();
/** * middleware 1 */
app.use(async (ctx, next) => {
console.log("first");
await next();
console.log("first end");
});
/** * Middleware 2 */
app.use(async (ctx, next) => {
console.log("second");
await next();
console.log("second end");
});
/** * asynchronous middleware */
app.use(async (ctx, next) => {
console.log("async");
await next();
await new Promise((resolve) = >
setTimeout(() = > {
console.log(`wait 1000 ms end`);
resolve();
}, 1000));console.log("async end");
});
/** * Middleware 2 */
app.use(async (ctx, next) => {
console.log("third");
await next();
console.log("third end");
});
app.use(async (ctx) => {
ctx.body = "Hello World";
});
app.listen(3000.() = > console.log(`Example app listening on port 3000! `));
Copy the code
Next we run the following command to start the project.
node app.js
Copy the code
After successful startup, open your browser and enter the following browser address:
http://127.0.0.1:3000/
Copy the code
Then in the command line window, you can see the following printed message:
Example app listening on port 3000!
first
second
async
third
third end
wait 1000 ms end
async end
second end
first end
Copy the code
You’ll notice that KOA follows the onion model exactly from top to bottom, printing first, second, async, and third from inside the onion to outside. Next, print third end, async end, Second end, first end from inside out.
Set up the project
Ok, with the basic principles finished, we will enter the link of building the project. We will introduce the necessary middleware application and process management for koA project construction from the aspects of unified middleware management ==> route management ==> parameter parsing ==> unified data format ==> global capture error handling ==> cross-domain processing ==> log recording ==> request parameter verification, etc.
First, create the project file directory:
├─ Server // Code Folder ├─ Common // Public Resources ├─ Config // Environment Configuration ├─ Middlewares // Middleware Set ├─ Router // Route ├─ schema // Parameter Validation ├─ utils // Toolset ├─ package.json // Package Configuration ├─ app.js // Run fileCopy the code
Note that in the following example code require(‘ XXX ‘), the XXX NPM package does not write NPM references (NPM install XXX –save). Here is a brief explanation.
In the server/app.js file, write the basic startup service code:
const Koa = require("koa");
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
const PORT = 3000;
app.listen(PORT, () = > {
console.log('Started successfully,The ${"http://127.0.0.1"}:${PORT}`);
});
Copy the code
Middleware merge
First, we used KoA-compose to centralize all middleware processing and make it easy to access.
In server/middlewares/index. Js file middleware under concentrated processing, the output list middleware
module.exports = [
...,
];
Copy the code
Then use it in the server/app.js file.
const compose = require("koa-compose");
const middlewares = require("./server/middlewares");
app.use(compose(middlewares));
Copy the code
Routing management
Use @koa/ Router to manage routes. Here we need to separate the routes and the corresponding processing functions of each route. Do not write them together to avoid difficult maintenance.
Create the following file:
server/router/routes.js
Routing list file;server/router/index.js
Route processing export file;server/contronllers/index.js
Unified business processing;server/controllers/home.js
Business processing documents;
Server/Contronllers folder to unify the code logic for business processing. The following server/controllers/home. Js is business processing files:
const home = async (ctx) => {
ctx.body = "hello world";
};
module.exports = { home };
Copy the code
Export business processing files in server/ Contronllers folder:
const homeRouter = require("./home");
module.exports = {
homeRouter,
};
Copy the code
In the server/router/routes.js file, the service route list is processed in a unified manner.
const { homeRouter } = require(".. /controllers");
const routes = [
{
method: "get".path: "/".controller: homeRouter.home,
},
];
module.exports = routes;
Copy the code
In the server/router/index.js file, loop the service route list and export the route processing file.
const router = require("@koa/router") ();const routeList = require("./routes");
routeList.forEach((item) = > {
const { method, path, controller } = item;
router[method](path, controller);
});
module.exports = router;
Copy the code
Argument parsing
Processing of request packets. Use koA-BodyParser to parse. Server/middlewares/index. Js file write:
const koaBodyParser = require("koa-bodyparser");
const myKoaBodyParser = koaBodyParser();
module.exports = [
...,
myKoaBodyParser,
]
Copy the code
Unified return data format
Do you have the problem that every time you return data, you write the following repeat logic:
ctx.body = {
code,
data,
msg: msg || "fail"};Copy the code
Here, we use an intermediary processing unified return data format, in the server/middlewares/response. The js file write:
const response = () = > {
return async (ctx, next) => {
ctx.res.fail = ({ code, data, msg }) = > {
ctx.body = {
code,
data,
msg: msg || "fail"}; }; ctx.res.success =(msg) = > {
ctx.body = {
code: 0.data: ctx.body,
msg: msg || "success"}; };await next();
};
};
module.exports = response;
Copy the code
Server/middlewares/index. Js file write:
const response = require("./response");
const myResHandler = response();
module.exports = [
...,
myResHandler,
]
Copy the code
I wrote the above code. We also need the error-handling middleware to trigger the uniform return data Format middleware. And then down.
Error handling
Based on the unified return data format processing, the onion model is used to build a middleware, global unified error processing. Server/middlewares/error. Js file write:
const error = () = > {
return async (ctx, next) => {
try {
await next();
if (ctx.status === 200) { ctx.res.success(); }}catch (err) {
if (err.code) {
// Error thrown by oneself
ctx.res.fail({ code: err.code, msg: err.message });
} else {
// An error occurred while the program was running
ctx.app.emit("error", err, ctx); }}}; };module.exports = error;
Copy the code
Server/middlewares/index. Js file write:
const error = require("./error");
const myErrorHandler = error();
module.exports = [
...,
myErrorHandler,
]
Copy the code
Combined with the error-handling middleware to trigger the uniform return data format middleware, we simply write the following code and the middleware can automatically supplement the return data format.
ctx.body = data
Copy the code
Cross domain processing
Use @KOA/CORS to handle cross-domain issues. Server/middlewares/index. Js file write:
const koaCors = require("@koa/cors");
const myKoaCors = koaCors({
origin: "*".credentials: true.allowMethods: ["GET"."HEAD"."PUT"."POST"."DELETE"."PATCH"]});module.exports = [
...,
myKoaCors,
]
Copy the code
logging
Use the KOA-Logger to handle simple logging problems. Server/middlewares/index. Js file write:
const logger = require("koa-logger");
const myLogger = logger();
module.exports = [
...,
myLogger,
]
Copy the code
Request parameter verification
Some solutions in actual projects
File Uploading Scheme
const koaBody = require('koa-body'); // File upload
app.use(koaBody({
multipart: true.formidable: {
maxFileSize: config.LIMIT.UPLOAD_IMG_SIZE // Set the maximum size of a file to be uploaded. The default size is 2 MB
}
Copy the code
Koa2 sets global variables
Using the app. The context [XXX].
const Koa = require('koa');
const app = new Koa();
// Functions as app.locals = {// XXX} in Express
app.context.state = Object.assign(app.context.state, {key1 : value1, key2: value2});
Copy the code
A method that requests an external interface
- One is node native Request. The request has been abandoned and is not updated.
- Koa2-request middleware, based on request to do a package, internal source code reference.
- Bent HTTP client with async/await node.js and browser functionality.
- Got is a friendly and powerful HTTP request library for Node.js.
- More references:
Image from alternative library list.
Proposal:
As a Node.js developer who has been using Request for a while, Bent is definitely an easy transition – 💖 is highly recommended
Concurrent request limit
Solution:
- The promise.race-based feature works with promise.all to limit the number of parallel requests and keep the number of requests.
- Based on the first-in-first-out (FIFO) feature of the queue, the asynchronous microtask queue is constructed with Promise to limit the number of parallel requests and keep the number of requests.
Specific reference:
- Js asynchronous concurrency control, limit the number of requests to resolve confusion.
Multi-process solution
Using PM2 is the daemon manager;
CPU overload protection design
To be added.