Collation of Common Knowledge Points in Koa Framework

Keywords: Javascript npm JSON xml Database

brief introduction

Koa It's a simple and easy-to-use Web framework. It is characterized by elegance, conciseness, strong expression and high degree of freedom. The code itself is only over 1000 lines, and all functions are implemented through plug-ins.

Pre-school preparation

Check the Nodejs version

Open the cmd command line window node-v
Note: Koa must use version 7.6 or more. If your version is below this requirement, upgrade Node first.
Supporting cases

I. Basic Usage

1.1 Three lines of code to set up HTTP services

npm install koa

  const Koa = require('koa');
  const app = new Koa();
  app.listen(3000);

1.2 Context object

Koa provides a Context object that represents the context of a conversation (including HTTP requests and HTTP replies). By processing this object, you can control the content returned to the user.

The Context object contains:

const Koa = require('koa')
const app = new Koa()

app.use((ctx, next) => {
    //ctx is the context of the entire application, including commonly used request s, response s
    //ctx.response stands for HTTP Response. Similarly, ctx.request represents HTTP Request.
    //ctx.response.body can be abbreviated as ctx.body.
    ctx.response.body = 'hello world'
})

app.listen(3000)

1.3 Types of HTTP Response

Koa's default return type is text/plain. If you want to return other types of content, you can first use ctx.request.accepts to determine what data the client wants to accept (according to the Accept field of HTTP Request), and then use ctx.response.type to specify the return type.

const Koa = require('koa')
const app = new Koa()
//Declare a main middleware. If you are eager to understand the middleware, you can jump to (3)
const main = (ctx,next) =>{
if (ctx.request.accepts('json')) {
    ctx.response.type = 'json';
    ctx.response.body = { data: 'Hello World' };
  } else if (ctx.request.accepts('html')) {
    ctx.response.type = 'html';
    ctx.response.body = '<p>Hello World</p>';
  } else if (ctx.request.accepts('xml')) {
    ctx.response.type = 'xml';
    ctx.response.body = '<data>Hello World</data>';
  } else{
    ctx.response.type = 'text';
    ctx.response.body = 'Hello World';
  };
}; //The json format is displayed on the direct running page, because we do not set the request header, so each format is ok.   

app.use(main)//app.use() is used to load middleware.
app.listen(3000)

1.4 Page Template

In actual development, web pages returned to users are often written as template files. We can let Koa read the template file first, and then return the template to the user.

About fs.createReadStream

const fs = require('fs');
const Koa = require('koa');
const app = new Koa();

const main = ctx => {
    ctx.response.type = 'html';
    ctx.response.body = fs.createReadStream('./data/index.html');
};

app.use(main);
app.listen(3000);

Two, routing

2.1 Native Routing

const Koa = require('koa')
const app = new Koa()
app.use((ctx, next) => {
    if (ctx.request.url == '/') {//Get the user request path through ctx.request.url
        ctx.body = '<h1>home page</h1>'
    } else if (ctx.request.url == '/my') {
        ctx.body = '<h1>Contact us</h1>'
    } else {
        ctx.body = '<h1>404 not found</h1>'
    }
})
app.listen(3000)

2.2 koa-router module routing

koa-route in npm

npm install koa-router

const Koa = require('koa')
const Router = require('koa-router')

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

app.use(router.routes()).use(router.allowedMethods());
//routes() returns to the router middleware, which schedules the routes that match the request.
//allowedMethods() handles the business of enriching the header header of the response object when ctx.status is empty or 404 after the execution of all routing middleware.

router.get('/',(ctx,next)=>{//A. get is a get request sent
  ctx.response.body = '<h1>home page</h1>'
})
router.get('/my',(ctx,next)=>{
  ctx.response.body = '<h1>Contact us</h1>'
})

app.listen(3000)

2.3 Static Resources

If the website provides static resources (pictures, fonts, stylesheets, scripts...), it will be very troublesome to write routing for them one by one, and there is no need for the koa-static module to encapsulate this part of the request. See the following examples

koa-static in npm

npm install koa-staic

const Koa = require('koa');
const app = new Koa();
const path = require('path');
const serve = require('koa-static');

const main = serve(path.join(__dirname));

app.use(main);
app.listen(3000);

Visit http://localhost:3000/data/index.html, you can see the contents of this file in the browser.

2.4 redirect jump

In some cases, the server needs to redirect access requests. For example, after a user logs in, redirect him to the page before he logs in. The ctx.response.redirect() method can emit a jump that directs the user to another route.

const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router()

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

router.get('/cdx',(ctx,next)=>{
  ctx.response.redirect('/');//Send out a jump to direct the user to another route.
})
router.get('/',(ctx,next)=>{
   ctx.body = 'Hello World';
})

app.listen(3000);

Visit http://localhost:3000/cdx, browsers will guide users to root routing.

3. Middleware

3.1 Logger function

One of Koa's greatest features and most important design is middleware. To understand middleware, let's first look at the implementation of Logger.

./logger/koa-logger.js

module.exports = (ctx, next) => {
    console.log(`${Date.now()} ${ctx.request.method} ${ctx.request.url}`);//custom
}   

./logger.js

const Koa = require('koa')
const koaLogger = require('./logger/koa-logger')
const app = new Koa();

app.use(koaLogger)
app.listen(3000)

Print results

3.2 Middleware Concept

In the middle of HTTP Request and HTTP Response, the function used to implement some intermediate function is called "middleware".

Basically, all of Koa's functions are implemented through middleware, and the main in the previous example is also middleware. Each middleware accepts two parameters by default. The first parameter is the Context object and the second parameter is the next function. As long as the next function is called, execution can be transferred to the next middleware.

Multiple middleware will form a stack structure, which will be executed in the order of "first in, last out".

  1. The outermost middleware executes first.
  2. Call the next function to give execution to the next middleware.
  3. ...
  4. The innermost middleware is finally executed.
  5. After execution, the execution right is returned to the upper middleware.
  6. ...
  7. After the outermost middleware regains execution rights, it executes the code behind the next function.

Example:

const Koa = require('koa');
const app = new Koa();

app.use((ctx, next)=>{
  console.log('>> one');
  next();
  console.log('<< one');
})

app.use((ctx, next)=>{
  console.log('>> two');
  next(); 
  console.log('<< two');
})
app.use((ctx, next)=>{
  console.log('>> three');
  next();
  console.log('<< three');
})
app.listen(3000);

Output results:

If the next function is not called inside the middleware, the execution right will not be passed on.

3.4 Asynchronous Middleware

So far, all examples of middleware are synchronous and do not include asynchronous operations. If there are asynchronous operations (such as reading a database), the middleware must be written as an async function.

npm install fs.promised

fs.promised in npm

const fs = require('fs.promised');
const Koa = require('koa');
const app = new Koa();

const main = async function (ctx, next) {
    ctx.response.type = 'html';
    ctx.response.body = await fs.readFile('./data/index.html', 'utf8');
};

app.use(main);
app.listen(3000);

In the above code, fs.readFile is an asynchronous operation that must be written as await fs.readFile(), and then the middleware must be written as an async function.

app.use(async(ctx, next)=>{
  ctx.body = '1'
  //Delay 2 seconds to execute the next middleware, which is useless because it is an asynchronous function
  setTimeout(()=>{
      next()
  },2000)
  ctx.body += '2'
})

app.use(async(ctx, next)=>{
  ctx.body += '3'
  next()
  ctx.body += '4'
})


server.js Correct approach
function delay(){
    return new Promise((reslove,reject)=>{
      setTimeout(()=>{
        reslove()
      },1000)
    })
}

app.use(async(ctx, next)=>{
  ctx.body = '1'
  await next()
  ctx.body += '2'
})

app.use(async(ctx, next)=>{
  ctx.body += '3'
  await delay()
await next()
  ctx.body += '4'
})

Synthesis of 3.5 Middleware

koa-compose module can synthesize multiple middleware into one.

koa-compose in npm

npm install koa-compose

const Koa = require('koa');
const compose = require('koa-compose');
const app = new Koa();

const logger = (ctx, next) => {
  console.log(`${Date.now()} ${ctx.request.method} ${ctx.request.url}`);
  next();
}

const main = ctx => {
  ctx.response.body = 'Hello World';
};

const middlewares = compose([logger, main]);//Fusion Middleware

app.use(middlewares);//Loading Middleware
app.listen(3000);

Output: Print the log first, then display Hello World on the page

IV. Handling Errors

4.1 500 errors

If there is an error in the code, we need to return the error information to the user. The HTTP protocol stipulates that 500 status codes will be returned at this time. Koa provides the ctx.throw() method to throw errors, ctx.throw(500) is to throw 500 errors.

const Koa = require('koa');
const app = new Koa();

const main = ctx => {
  ctx.throw(500);//At this time, when you visit the home page, you will report a 500 error (internal server error) and the server will report an error.
};

app.use(main);
app.listen(3000);

4.2 404 error

Setting ctx.response.status to 404 is equivalent to ctx.throw(404), returning 404 errors.

const Koa = require('koa');
const app = new Koa();

const main = ctx => {
  ctx.response.status = 404;//The status code returned by response is 404
  ctx.response.body = 'Page Not Found';//Let the page display the content, the server does not report errors
};

app.use(main);
app.listen(3000);

4.3 Middleware for Error Handling

To handle errors easily, try...catch is the best way to capture them. However, it's too cumbersome to write try...catch for every middleware. We can make the outermost middleware responsible for the error handling of all middleware.

const Koa = require('koa');
const app = new Koa();

const handler = async (ctx, next) => {
  try {
    await next();//Execute the next Middleware
  } catch (err) {
     //If main middleware is a problem, it will go here.
    ctx.response.status = err.statusCode || err.status || 500;
    ctx.response.body = {
      message: err.message//Return the error message to the page
    };
  }
};

const main = ctx => {
  ctx.throw(500);//If there is no problem, it will be executed normally. If there is a problem, it will go catach.
};

app.use(handler);
app.use(main);
app.listen(3000);

4.4 Erors Event Monitoring

Once an error occurs during the run, Koa triggers an error event. Listening for this event can also handle errors.

const Koa = require('koa');
const app = new Koa();

const main = ctx => {
  ctx.throw(500);
};
app.on('error', (err, ctx) => {
   //If you make a mistake, you'll go here.
  console.error('server error', err);//Er is the source of err or
});

app.use(main);
app.listen(3000);

4.5 Release error events

It should be noted that if the error is caught by try...catch, the error event will not be triggered. At this point, ctx.app.emit() must be called to manually release the error event in order for the listener function to take effect.

const Koa = require('koa');
const app = new Koa();

const handler = async (ctx, next) => {
    try {
        await next();
    } catch (err) {
        ctx.response.status = err.statusCode || err.status || 500;
        ctx.response.type = 'html';
        ctx.response.body = '<p>If you have any questions, please contact the administrator.</p>';
        ctx.app.emit('error', err, ctx);//Release error events
    }
};

const main = ctx => {
    ctx.throw(500);
};

app.on('error', function (err) {
    //The listener function here will take effect only after the error event is released
    console.log('error', err.message);
    console.log(err);
});

app.use(handler);
app.use(main);
app.listen(3000);

The main function of the code above throws an error and is captured by the handler function. The catch block uses ctx.app.emit() to manually release error events so that the listener function can listen.

5. Functions of Web App

5.1 cookie

ctx.cookies are used to read and write cookies.

const Koa = require('koa');
const app = new Koa();

const main = function(ctx) {
                    //Reading cookie//does not return 0
  const n = Number(ctx.cookies.get('view') || 0) + 1;
  ctx.cookies.set('view', n);//Set cookie
  ctx.response.body = n + ' views';//Display cookie
}

app.use(main);
app.listen(3000);

5.2 form

Web applications can't do without processing forms. Essentially, a form is a key-value pair that the POST method sends to the server. The koa-body module can be used to extract key-value pairs from the data body of the POST request.

koa-body in npm

npm install koa-body

const Koa = require('koa');
const koaBody = require('koa-body');
const app = new Koa();

const main = async function (ctx) {
    const body = ctx.request.body;
    if (!body.name){
        ctx.throw(400, '.name required')
    };
    ctx.body = { name: body.name };
};

app.use(koaBody());
app.use(main);
app.listen(3000);

The above code uses the POST method to send a key-value pair to the server, which will be parsed correctly. If the data sent is incorrect, an error message will be received.

5.3 File Upload

The koa-body module can also be used to process file uploads.

. / demo / file upload. js

const Koa = require('koa');
const koaBody = require('koa-body');
const Router = require('koa-router');
const fs = require('fs');
const path = require('path');
const router = new Router()
const app = new Koa();

app.use(koaBody({
    multipart: true,//Parse multi-part principals, default false
    formidable: {
        maxFileSize: 200 * 1024 * 1024    // Set maximum upload file size limit, default 2M
    }
}));

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

router.post('/uploadfile', (ctx, next) => {
    // Upload a single file
    const file = ctx.request.files.file; // Getting Uploaded Files
    // Creating Readable Stream
    const reader = fs.createReadStream(file.path);
    let filePath = path.join(__dirname, 'data/') + `/${file.name}`;
    // Creating Writable Stream
    const upStream = fs.createWriteStream(filePath);
    // Readable streams are written to writable streams through pipes
    reader.pipe(upStream);
    return ctx.body = "Upload success!";
});

app.listen(3000)

./demo/web/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <form action="http://127.0.0.1:3000/uploadfile" method="post" enctype="multipart/form-data">
    <input type="file" name="file" id="file" value="" multiple="multiple" />
    <input type="submit" value="Submission"/>
</form>
</body>
</html>

fs file system module in nodejs can help you read and write files, but it will not help you create folders.

The source of this paper is http://www.ruanyifeng.com/blog/2017/08/koa.html.

Posted by amazing on Wed, 15 May 2019 23:57:02 -0700