From the NodeJS course at Coderwhy.

Website koa.bootcss.com/#applicatio…

The introduction of Koa

  • The next generation Web framework for Node.js;
  • Koa aims to provide smaller, richer, and more powerful capabilities for Web applications and apis;
  • Compared with Express, it has stronger asynchronous processing capability.
  • With 1600+ lines of core code, Koa is a more lightweight framework that allows you to install and use middleware as needed;

At the beginning of Koa experience

    const Koa = require('koa');

    const app = new Koa();

    app.use((ctx, next) = > {
      ctx.response.body = "Hello Koa";
    });

    app.listen(8000.() = > {
      console.log("Koa initial experience server started successfully ~");
    });
Copy the code

Using the example above, let’s introduce two parameters in the middleware.

  • CTX: Context object;
    • Instead of separating REQ and RES as Express does, KOA treats them as CTX properties
    • ctx.reqFor the NoderequestObject.
    • ctx.resFor the NoderesponseObject.
    • ctx.responseFor KoaresponseObject.
    • ctx.requestFor KoarequestObject.
  • Next: Terminates this middleware use and invokes the next middleware.

Middleware is used in Koa

Registered middleware in Koa can only be registered through use(), and only one middleware can be passed without matching paths.

    app.use((ctx, next) = > {
      ctx.response.body = "Hello Koa";
    });
Copy the code

Use the routing

Since koA provides us with routing processing, if we want to do complex routing matching, we need to install a third-party library to help us with routing. Install the KOA-Router library.

Registered routing

    // users.js
    const Router = require('koa-router');

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

    userRouter.get('/'.(ctx, next) = > {
      ctx.response.body = "User Lists~";
    });

    userRouter.put('/'.(ctx, next) = > {
      ctx.response.body = "put request~";
    });


    module.exports = userRouter;
Copy the code

Use the routing

    // index.js
    const Koa = require('koa');

    const userRouter = require('./router/user');

    const app = new Koa();

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

    app.listen(8000.() = > {
      console.log("Koa routing server started successfully ~");
    });
Copy the code

As can be seen from the above example

  • As a routing file, in the APProuter.routes()Register as middleware
  • router.allowedMethods()It is used to determine which request methods we have set up. If only get requests are set, we will get an error requesting POST,patch,delete, etc.Method Not Allowed, status code: 405;

Parse the parameters passed by the request

Parse the params parameter

We can get it directly from ctx.request.params. When no route is used, it is not parsed.

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

    const userRouter = new Router({ prefix: '/users' });
    / / http://127.0.0.1:8000/users/9? name=zh&age=20
    userRouter.get('/:id'.(ctx, next) = > {
      console.log(ctx.request.url);
      console.log(ctx.request.params);
      console.log(ctx.request.query);
      /** * /users/9? name=zh&age=20 * { id: '9' } * [Object: null prototype] { name: 'zh', age: '20' } */
    })

    app.use(userRouter.routes());

    app.listen(8000.() = > {
      console.log("Parameter processing server started successfully ~");
    });
Copy the code

Parsing query parameters

We can get this directly from ctx.request.query.

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

    const userRouter = new Router({ prefix: '/users' });
    / / http://127.0.0.1:8000/users/9? name=zh&age=20
    userRouter.get('/:id'.(ctx, next) = > {
      console.log(ctx.request.url);
      console.log(ctx.request.params);
      console.log(ctx.request.query);
      /** * /users/9? name=zh&age=20 * { id: '9' } * [Object: null prototype] { name: 'zh', age: '20' } */
    })

    app.use(userRouter.routes());

    app.listen(8000.() = > {
      console.log("Parameter processing server started successfully ~");
    });
Copy the code

Parse the JSON-formatted data in the request

Koa does not provide built-in middleware like Express does, so we need to use the third-party library KoA-BodyParser. Place the parsed parameters on ctx.request.body.

    const bodyParser = require('koa-bodyparser');
    app.use((ctx, next) = > {
      console.log("body===json", ctx.request.body);
      ctx.response.body = "Hello World";
    });
Copy the code

Parse the x-www-FROm-Urlencoded data in the request

Koa does not provide built-in middleware like Express does, so we need to use the third-party library KoA-BodyParser. Place the parsed parameters on ctx.request.body.

    const bodyParser = require('koa-bodyparser');
    app.use((ctx, next) = > {
      console.log("body===json", ctx.request.body);
      ctx.response.body = "Hello World";
    });
Copy the code

Parse the form-data data in the request

Their use is equivalent to the use of multer in expres

We need to install the Koa-Multer library to help us parse. This library is similar to Express for parsing file data. Do not use it as global middleware. Uploading file and non-file form-data may cause conflicts. And it mounts the data to request.body in NodeJS. Get it from ctx.req.body

Github.com/koajs/multe…

Parse non-file form data

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

    const app = new Koa();
    const uploadRouter = new Router({prefix: '/upload'});

    const upload = multer();

    uploadRouter.post('/', upload.any(), (ctx, next) = > {
      console.log(ctx.req.body);
      ctx.response.body = "Parse successful ~";
    });

    app.use(uploadRouter.routes());

    app.listen(8000.() = > {
      console.log("Koa initial experience server started successfully ~");
    });
Copy the code

Parse the file’s form data

We can specify the file name and suffix for the upload. You can also automatically assign file names to the system.

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

    const app = new Koa();
    const uploadRouter = new Router({prefix: '/upload'});

    const upload = multer({
      dest: './uploads/'
    });

    uploadRouter.post('/avatar', upload.single('avatar'), (ctx, next) = > {
      console.log(ctx.req.file);
      ctx.response.body = "Avatar uploaded successfully ~";
    });

    app.use(uploadRouter.routes());

    app.listen(8000.() = > {
      console.log("Koa initial experience server started successfully ~");
    });

Copy the code

Respond to the request

For other methods, please visit koa.bootcss.com/#response

  • response.body: Data used to respond to a request.
    • String: indicates the string data
    • Buffer: Indicates the Buffer data
    • Stream: streams data
    • The Object | | Array: Object or Array
    • Null: Output nothing
    • If Response. status is not set, Koa automatically sets the status to 200 or 204.
  • response.typeSet:Content-Typefield
  • response.set: Sets the response header field. You can pass in an object or you can pass inkey, value.
  • response.status: Sets the status code.

Deploying a Static Server

There is no middleware built into Koa like Express, so you need to install a third-party KOA-static library.

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

    app.use(koaStatic(path.resolve(__dirname, "./static")));

    app.listen(8000.() = > {
      console.log("Koa initial experience server started successfully ~");
    });
Copy the code

Error handling

Officials are talking about beefing up error handling.

Emit an error via ctx.app.emit(“error”, error type, CTX).

App.on (“error”, (err, CTX) => {}) listen for errors and handle them.

Custom error types

    // The user name or password is not entered
    const USERNAME_OR_PASSWORD_NOT_INPUT = "username_or_password_not_input"
    // The user name is the same
    const USER_ALREADY_EXISTS = 'user_already_exists';
    // The username is incorrect
    const USERNAME_NOT_EXISTS = 'username_not_exists';
    // The password is incorrect
    const PASSWORD_ERROR = 'password_error'
    // No authorized token is carried
    const NOT_TAKE_AUTHORIZATION_TOKEN = "not_take_authorization_token"
    / / not authorized
    const UNAUTHORIZATION = 'unauthorization'
    // The user does not have permissions
    const NOT_PERMISSION = 'not_permission'


    module.exports = {
      USERNAME_OR_PASSWORD_NOT_INPUT,
      USER_ALREADY_EXISTS,
      USERNAME_NOT_EXISTS,
      PASSWORD_ERROR,
      NOT_TAKE_AUTHORIZATION_TOKEN,
      UNAUTHORIZATION,
      NOT_PERMISSION
    }
Copy the code

Middleware functions that handle errors

    // errorHandle.js
    const {
      USERNAME_OR_PASSWORD_NOT_INPUT,
      USER_ALREADY_EXISTS,
      USERNAME_NOT_EXISTS,
      PASSWORD_ERROR,
      NOT_TAKE_AUTHORIZATION_TOKEN,
      UNAUTHORIZATION,
      NOT_PERMISSION
    } = require("./errorType");

    const errorHandle = (errorMessage, ctx) = > {
      let status, message;
      switch (errorMessage) {
        case USERNAME_OR_PASSWORD_NOT_INPUT:
          status = 400; // Bad Request
          message = "User name or password cannot be empty ~";
          break;
        case USER_ALREADY_EXISTS:
          status = 409; // conflict
          message = "User name already exists ~";
          break;
        case USERNAME_NOT_EXISTS:
          status = 400; // Parameter error
          message = "User name does not exist ~";
          break;
        case PASSWORD_ERROR:
          status = 400; // Parameter error
          message = "Password is wrong ~";
          break;
        case NOT_TAKE_AUTHORIZATION_TOKEN:
          status = 401; // Parameter error
          message = "未携带token~";
          break;
        case UNAUTHORIZATION:
          status = 401; // Parameter error
          message = "Invalid token~";
          break;
        case NOT_PERMISSION:
          status = 401; // Parameter error
          message = "You do not have the operation permission ~";
          break;
        default:
          status = 404;
          message = "NOT FOUND";
      }

      ctx.status = status;
      ctx.body = message;
    }


    module.exports = errorHandle
Copy the code

Register wrong middleware

    const errorHandle = require("./errorHandle");
    app.on("error", errorHandle)
Copy the code

Koa source code analysis

When we call new Koa(), we call the constructor of the Application class internally. There is an array of middleware for storing middleware.

We start the service with app.listen(), which basically calls the Listen method in NodeJS

When middleware is registered, the use method is called to add middleware to the Middleware array.

The response is returned by calling the callback function

The compose function handles the middleware call

After processing the middleware, we pass the middleware into the handleRequest function to respond.

Differences between Express and Koa

From the perspective of architectural design:

  • Express is complete and powerful, and it helps us build in a lot of useful features;
  • Koa is clean and free, it contains only the most core functionality, and does not limit our use of other middleware. Even the most basic GET and POST are not provided for us in the app. We need to determine the request or other functionality by ourselves or by route;

Differences in middleware execution

The core of both Express and KOA frameworks is middleware: the execution mechanism of their middleware is different, especially for asynchronous operations in one middleware;

So, next, let’s look at the order of execution of express and KOA middleware;

scenarios

  • In middleware1, add a string aaa to req.message;

  • In middleware2, add a string BBB to req.message;

  • In MIDDLEware3, add the string CCC to req.message;

  • When all the content has been added, in MIDDLEware1, the final result is returned via res;

Express’s middleware does not concatenate strings in asynchronous operations and then return the response result, req.message, in the first middleware. It will be aaABBBCCC. If there is an asynchronous operation concatenation string (assuming CCC concatenates in an asynchronous operation), aaABbb will be returned. This is because when Express calls the middleware, it does not wait for the asynchronous operation to complete when it reaches the last middleware. The unfinished code of the previous middleware is returned.

When synchronous concatenation is performed in Koa’s middleware, it produces the same results as Express. We can make the code after next() operate asynchronously by setting the middleware to async and return aaABBBCCC correctly.