Koa2 Introduction Tutorial

Keywords: node.js JSON Firefox xml socket

Full Demo Address

The demo was written by myself, so I can't guarantee it will run until there are environmental problems.If you are too lazy to write, move on and read the article. Most of the code even gives you the output information.
koa-demo

Introduction to Official Website

Created by Express's original team, koa is dedicated to becoming a smaller, more expressive, and more robust Web framework.Writing web applications using koa can avoid tedious nesting of callback functions and greatly improve the efficiency of error handling by combining different generator s.Koa does not bind any Middleware in the kernel method; it simply provides a lightweight and elegant Library of functions that makes writing Web applications easy.

preparation in advance

Let's start by installing some necessary libraries. You can use yarn, cnpm, or npm as you choose.

KOA Framework

yarn add koa

That's not enough, because Koa relies on node v7.6.0 or ES2015 and later versions and async method support. You can install as you want

Babel register

transform-async-to-generator or transform-async-to-module-method

Because I use Nodejs10.0, I don't need to install these things, let alone say.

Getting Started Simply

The convention is to use creating an application as an example of how to get started.

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

app
  .use(async ctx => {
    ctx.body = 'Code: Hello World';
  })
  .listen(3000);

console.log('Connection established, see effect http://127.0.0.1:3000/');

The code is clear at a glance, not nonsense.
(Complete code can be executed koa-demo lesson1 view effect)

Favicon.ico

The so-called favicon, short for Favorites Icon, as its name implies, allows browsers to distinguish different websites by icons in addition to their titles in their favorites.Of course, this isn't all about Favicon. Favicon displays vary depending on the browser: in most major browsers, such as FireFox and Internet Explorer (version 5.5 and above), favicon appears not only in favorites, but also in the address bar, where users can drag it to the desktop to build it.Shortcuts to websites; in addition, tagged browsers have many extensions, such as FireFox and even favicons that support animation formats.
The problem is that here the code browser automatically initiates this icon requesting the root directory of the site, interfering with the test, so the next print results ignore the Favicon.ico request.

cascade

A Koa application is an object that contains a set of middleware functions that are organized and executed in a stack-like manner.
When a middleware calls next(), the function pauses and passes control to the next middleware defined.When no more middleware executes downstream, the stack expands and each middleware resumes executing its parade behavior.(A similar analogy is that the middleware is equivalent to a DOM event, capturing event bubbles from the event).

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

// One-tier Middleware
app.use((ctx, next) => {
  console.log('Request resources:' + ctx.url);
  console.log('One-tier middleware control passed down');
  next();
  console.log('One-tier middleware control passed back');
  ctx.body = 'Code: Day Day Up';
});

// Two-tier Middleware
app.use((ctx, next) => {
  console.log('Two-tier middleware control passed down');
  next();
  console.log('Layer 2 middleware control passed back');
});

// response
app.use(ctx => {
  console.log('output body');
  ctx.body = 'Code: Good Good Study';
});

app.listen(3000);
console.log('Connection established, see effect http://127.0.0.1:3000/');

// One-tier middleware control passed down
// Two-tier middleware control passed down
// Output body
// Layer 2 middleware control passed back
// One-tier middleware control passed back

(Complete code can be executed koa-demo Lesson 2 of View Effect)

From the above results, you can see that each request for a resource passes through all the middleware, and control is reversed when executing to the last middleware. The output is that the body in the head covers the body in the tail.
To tell the truth, I haven't tried this way. I'm not used to it.

Context

Koa Context encapsulates Nodejs request and response objects into a single object, and each request creates a Context that is referenced as a receiver or as a CTX identifier in the middleware, with accessors and methods for many contexts delegated directly to their ctx.request or ctx.response.
We can print out the ctx object directly to see what it is.

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

// response
app.use(async ctx => {
  console.log('ctx: ', ctx);
});

app.listen(3000);
console.log('Connection established, see effect http://127.0.0.1:3000/');

/*
ctx:  { request:
   { method: 'GET',
     url: '/',
     header:
      { host: 'localhost:3000',
        connection: 'keep-alive',
        'cache-control': 'max-age=0',
        'upgrade-insecure-requests': '1',
        'user-agent':
         'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.89 Safari/537.36',
        accept:
         'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*!/!*;q=0.8',
        'accept-encoding': 'gzip, deflate, sdch',
        'accept-language': 'zh-CN,zh;q=0.8',
        cookie:
         'loginInfo={"username":"abc","password":"MjIyMjIy","rememberMe":1}' } },
  response: { status: 404, message: 'Not Found', header: {} },
  app: { subdomainOffset: 2, proxy: false, env: 'development' },
  originalUrl: '/',
  req: '<original node req>',
  res: '<original node res>',
  socket: '<original node socket>' }*/

(Complete code can be executed koa-demo lesson3 view effect)

Request

The Koa Request object is an abstraction on top of Nodejs'vanilla request object and provides many useful functions for HTTP server development.Koa's Request object includes useful content negotiation entities provided by accepts and negotiator.

  • request.accepts(types)
  • request.acceptsEncodings(types)
  • request.acceptsCharsets(charsets)
  • request.acceptsLanguages(langs)
  1. If no type is provided, all acceptable types are returned.
  2. If multiple types are provided, the best match is returned;
  3. If no match is found, a false is returned.

Because the usage is the same, choose one to explain.

request.accepts(types)

Check whether the given type is acceptable, the type value may be a string or an array of one or more mime types, and return the best matching type string if possible or false otherwise.

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

app
  .use(async ctx => {
    switch (ctx.accepts('json', 'html', 'text')) {
      case 'json':
        ctx.type = 'json';
        ctx.body = '<p>Match type json</p>';
        break;
      case 'html':
        ctx.type = 'html';
        ctx.body = '<p>Match type html</p>';
        break;
      case 'text':
        ctx.type = 'text';
        ctx.body = '<p>Match type text</p>';
        break;
      default:
        ctx.throw(406, 'json, html, or text only');
    }
  })
  .listen(3000);
console.log('Connection established, see effect http://127.0.0.1:3000/');

(Complete code can be executed koa-demo Les4 view effect)

More different processing details are required in actual development, so we can set the content as a template template template 1.html reference.

<!DOCTYPE html>
<html lang="en" dir="ltr">

  <head>
    <meta charset="utf-8">
    <title></title>
  </head>

  <body>
    <p>Match type html</p>
  </body>

</html>

(Complete code can be executed koa-demo Tempe1 view effect)

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

app
  .use(async ctx => {
    switch (ctx.accepts('json', 'html', 'text')) {
      case 'html':
        ctx.type = 'html';
        ctx.body = fs.createReadStream('./template1.html');
        break;
      default:
        ctx.throw(406, 'json, html, or text only');
    }
  })
  .listen(3000);
console.log('Connection established, see effect http://127.0.0.1:3000/');

(Complete code can be executed koa-demo lesson5 view effect)

Route

In fact, the code above is already used for the original route, so we can increase the judgment of requesting resources, and add a template 2.html template switch to see the effect

<!DOCTYPE html>
<html lang="en" dir="ltr">

  <head>
    <meta charset="utf-8">
    <title></title>
  </head>

  <body>
    <p>template2</p>
  </body>

</html>

(Complete code can be executed koa-demo Tempe2 view effect of)
Then let's get rid of the type judgment code to see the effect. Just write the html dead. Otherwise, the type is empty by default. Opening the page will trigger the download. Don't trust you to get rid of the line of code with the type set.

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

app
  .use(async ctx => {
    console.log('type: ' + ctx.type);
    switch (ctx.url) {
      case '/':
        ctx.type = 'html';
        ctx.body = fs.createReadStream('./template1.html');
        break;
      case '/template2':
        ctx.type = 'html';
        ctx.body = fs.createReadStream('./template2.html');
        break;
      default:
        ctx.throw(406, 'json, html, or text only');
    }
  })
  .listen(3000);
console.log('Connection established, see effect http://127.0.0.1:3000/');

(Complete code can be executed koa-demo Les6 view effect)
After executing the script, you will see the template2.html template content by default, and manually change to http://127.0.0.1:3000/template2 If you do not set the type to see the download pop-up window in Chrome, other browsers have not tried it.

In practice, we don't have to be so cumbersome in developing routes. As mentioned above, koa doesn't bind any Middleware in the kernel method. It only provides a lightweight and elegant function library, so we need to install a routing middleware.
koa-route3.2.0 The last push was two years ago, and if you don't give up maintenance, it's already stable.

yarn add koa-route

If you need a route library that uses full features, you can see koa-router
Here is a brief demonstration of the use of koa-route.

const Koa = require('koa'),
  _ = require('koa-route'),
  fs = require('fs'),
  app = new Koa();

const route = {
  index: ctx => {
    //doSomethings
    ctx.type = 'html';
    ctx.body = fs.createReadStream('./template1.html');
  },
  template2: ctx => {
    //doSomethings
    ctx.type = 'html';
    ctx.body = fs.createReadStream('./template2.html');
  },
};

app
  .use(_.get('/', route.index))
  .use(_.get('/template2', route.template2))
  .listen(3000);
console.log('Connection established, see effect http://127.0.0.1:3000/');

(Complete code can be executed koa-demo Les7 view effect)

Response Status

If an error occurs while the code is running, we need to return the error information to the user.

ctx.throw([status], [msg], [properties])

Equivalent to

const err = new Error(msg);
err.status = status;
err.expose = true;
throw err;

Note: These are user-level errors and are marked with err.expose, which means that the message applies to the client response, which is usually not the content of the error message because you do not want to leak fault details.

100 "continue"
101 "switching protocols"
102 "processing"
200 "ok"
201 "created"
202 "accepted"
203 "non-authoritative information"
204 "no content"
205 "reset content"
206 "partial content"
207 "multi-status"
208 "already reported"
226 "im used"
300 "multiple choices"
301 "moved permanently"
302 "found"
303 "see other"
304 "not modified"
305 "use proxy"
307 "temporary redirect"
308 "permanent redirect"
400 "bad request"
401 "unauthorized"
402 "payment required"
403 "forbidden"
404 "not found"
405 "method not allowed"
406 "not acceptable"
407 "proxy authentication required"
408 "request timeout"
409 "conflict"
410 "gone"
411 "length required"
412 "precondition failed"
413 "payload too large"
414 "uri too long"
415 "unsupported media type"
416 "range not satisfiable"
417 "expectation failed"
418 "I'm a teapot"
422 "unprocessable entity"
423 "locked"
424 "failed dependency"
426 "upgrade required"
428 "precondition required"
429 "too many requests"
431 "request header fields too large"
500 "internal server error"
501 "not implemented"
502 "bad gateway"
503 "service unavailable"
504 "gateway timeout"
505 "http version not supported"
506 "variant also negotiates"
507 "insufficient storage"
508 "loop detected"
510 "not extended"
511 "network authentication required"

Status Code Error

There are two ways of writing, ctx.throw (status code) or ctx.status =status code, which automatically returns default text information, except that they set how information is returned.
Note: By default, response.status is set to 404 instead of 200 as node's res.statusCode does.

const Koa = require('koa'),
  _ = require('koa-route'),
  app = new Koa();

const router = {
  '403': ctx => {
    //doSomethings
    ctx.throw(403, '403 La');
  },
  '404': ctx => {
    //doSomethings
    ctx.status = 404;
    ctx.body = `<p>404 La</p>`;
  },
};

app
  .use(_.get('/403', router[403]))
  .use(_.get('/404', router[404]))
  .listen(3000);
console.log('Connection established, see effect http://127.0.0.1:3000/');

(Complete code can be executed koa-demo Les8 view effect)
You can open http://localhost:3000/403 and http://localhost:3000/404 to see the output.

Error monitoring

const Koa = require('koa'),
  _ = require('koa-route'),
  app = new Koa();

const router = {
  index: ctx => {
    //doSomethings
    ctx.throw(500, 'I mean it!');
  },
};

app
  .use(_.get('/', router.index))
  .on('error', (err, ctx) => {
    console.error('error', err);
  })
  .listen(3000);
console.log('Connection established, see effect http://127.0.0.1:3000/');
/*
error { InternalServerError: I mean it!
    at Object.throw (C:\project\test\koa-demo\node_modules\koa\lib\context.js:93:11)
    at Object.index (C:\project\test\koa-demo\lesson9.js:8:18)
    at C:\project\test\koa-demo\node_modules\koa-route\index.js:39:44
    at dispatch (C:\project\test\koa-demo\node_modules\koa-compose\index.js:42:32)
    at C:\project\test\koa-demo\node_modules\koa-compose\index.js:34:12
    at Application.handleRequest (C:\project\test\koa-demo\node_modules\koa\lib\application.js:150:12)
    at Server.handleRequest (C:\project\test\koa-demo\node_modules\koa\lib\application.js:132:19)
    at Server.emit (events.js:182:13)
    at parserOnIncoming (_http_server.js:654:12)
    at HTTPParser.parserOnHeadersComplete (_http_common.js:109:17) message: 'I mean it!'}*/

(Complete code can be executed koa-demo Lesson 9 for viewing effects)

Error Capture

You can also use try...catch() directly to handle it, but the error listening event will no longer receive the error message.

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

const err = async (ctx, next) => {
    try {
      await next();
    } catch (err) {
      ctx.status = 404;
      ctx.body = `<p>Check if there are any print errors in the terminal!</p>`;
    }
  },
  index = ctx => {
    ctx.throw(500);
  };

app
  .use(err)
  .use(index)
  .on('error', function(err) {
    console.error('error', err);
  })
  .listen(3000);
console.log('Connection established, see effect http://127.0.0.1:3000/');

(Complete code can be executed koa-demo Les10 view effect)
Note that error handling here can be monitored by an "error" event if the ctx.throw() method is used, not because it throws a new error.

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

const err = async (ctx, next) => {
    try {
      await next();
    } catch (err) {
      ctx.throw(404, 'Check if there are any print errors in the terminal!');
    }
  },
  index = ctx => {
    ctx.throw(500);
  };

app
  .use(err)
  .use(index)
  .on('error', function(err) {
    console.error('error', err);
  })
  .listen(3000);
console.log('Connection established, see effect http://127.0.0.1:3000/');

(Complete code can be executed koa-demo Lesson 1 for viewing effects)1
KOA also provides emit methods to trigger error listening at the same time.

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

const err = async (ctx, next) => {
    try {
      await next();
    } catch (err) {
      ctx.status = 404;
      ctx.body = `<p>Check if there are any print errors in the terminal!</p>`;
      ctx.app.emit('error', err, ctx);
    }
  },
  index = ctx => {
    ctx.throw(500);
  };

app
  .use(err)
  .use(index)
  .on('error', function(err) {
    console.error('error', err);
  })
  .listen(3000);
console.log('Connection established, see effect http://127.0.0.1:3000/');

/*
error { InternalServerError: Internal Server Error
    at Object.throw (C:\project\test\koa-demo\node_modules\koa\lib\context.js:93:11)
    at index (C:\project\test\koa-demo\lesson12.js:14:18)
    at dispatch (C:\project\test\koa-demo\node_modules\koa-compose\index.js:42:32)
    at err (C:\project\test\koa-demo\lesson12.js:6:19)
    at dispatch (C:\project\test\koa-demo\node_modules\koa-compose\index.js:42:32)
    at C:\project\test\koa-demo\node_modules\koa-compose\index.js:34:12
    at Application.handleRequest (C:\project\test\koa-demo\node_modules\koa\lib\application.js:150:12)
    at Server.handleRequest (C:\project\test\koa-demo\node_modules\koa\lib\application.js:132:19)
    at Server.emit (events.js:182:13)
    at parserOnIncoming (_http_server.js:654:12) message: 'Internal Server Error' }
*/

(Complete code can be executed koa-demo Les11 for viewing effects)

Static Resources

Smart people can see some problems in the code above, and remember that we said we ignored Favicon.ico's request.We don't just have requests for pages, we have requests for other resources.
We still use the url to determine whether to return to the page. What if there are other static resources such as pictures?Here's a description of dependent Libraries koa-static5.0.0

yarn add koa-static
--------------------------
require('koa-static')(root, opts)

By setting the root directory and optionally configuring the static resource lookup path, we first create an img directory to hold a picture, then reference it in template 3.html, and then set the path require ('koa-static') (u dirname +'/img/'), which will automatically find the resource in the specified directory.

<!DOCTYPE html>
<html lang="en" dir="ltr">

  <head>
    <meta charset="utf-8">
    <title></title>
  </head>

  <body>
    <p>Yes, I'm the first page</p>
    <img src="./1.gif"/>
  </body>

</html>

(Complete code can be executed koa-demo Tempe3 view effect of)

const Koa = require('koa'),
  _ = require('koa-route'),
  serve = require('koa-static')(__dirname + '/img/'),
  fs = require('fs'),
  app = new Koa();

const router = {
  index: ctx => {
    //doSomethings
    ctx.type = 'html';
    ctx.body = fs.createReadStream('./template3.html');
  },
};

app
  .use(serve)
  .use(_.get('/', router.index))
  .listen(3000);
console.log('Connection established, see effect http://127.0.0.1:3000/');

(Complete code can be executed koa-demo Les13 view effect)
Modify the path if you still don't understand it, require ('koa-static') (u dirname), and then change the picture address to'. /img/1.gif'.You will see or find the corresponding resource.

middleware management

As the project develops, you may install more and more middleware, all of which can be used koa-compose Do middleware management.Many of these libraries have similar middleware for simplifying the use of middleware.
The example we used above to illustrate the koa cascade can be used directly for modification.

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

// One Level Middle
const mid1 = (ctx, next) => {
  console.log('Request resources:' + ctx.url);
  console.log('One-tier middleware control passed down');
  next();
  console.log('One-tier middleware control passed back');
};

// Middle of 2nd floor
const mid2 = (ctx, next) => {
  console.log('Two-tier middleware control passed down');
  next();
  console.log('Layer 2 middleware control passed back');
};

// response
const mid3 = ctx => {
  console.log('output body');
  ctx.body = 'Code: Hello World';
};

app.use(compose([mid1, mid2, mid3])).listen(3000);
console.log('Connection established, see effect http://127.0.0.1:3000/');
// Request resources: /
// One-tier middleware control passed down
// Two-tier middleware control passed down
// Output body
// Layer 2 middleware control passed back
// One-tier middleware control passed back

(Complete code can be executed koa-demo Les14 view effect)
You can see that the general principle is to change the way you use multiple middleware references to assemble multiple middleware into one use.

Request Processing

We can use it when processing requests koa-body Resolve the request body.

A full-featured koa body parser middleware. Support multipart, urlencoded and json request bodies. Provides same functionality as Express's bodyParser - multer. And all that is wrapped only around co-body and formidable.

A rich body parsing middleware that supports multiple parts, urlencoded, json requester, provides the same function methods as bodyParse in Express

Direct Installation Dependency

yarn add koa-body

Create a new submission page

<!DOCTYPE html>
<html lang="en" dir="ltr">

  <head>
    <meta charset="utf-8">
    <title></title>
  </head>

  <body>
    <form class="" action="/upload" method="post">
      <input type="text" name="name" value="">
      <button type="submit" name="button">Submit</button>
    </form>
  </body>

</html>

(Complete code can be executed koa-demo Tempe4 view effect of)

Can output format to see the effect

const Koa = require('koa'),
  koaBody = require('koa-body'),
  _ = require('koa-route'),
  fs = require('fs'),
  app = new Koa();

const router = {
    index: ctx => {
      //doSomethings
      ctx.type = 'html';
      ctx.body = fs.createReadStream('./template4.html');
    },
  },
  upload = ctx => {
    console.log(ctx.request.body);
    ctx.body = `Request Body: ${JSON.stringify(ctx.request.body)}`;
  };

app
  .use(koaBody())
  .use(_.get('/', router.index))
  .use(_.post('/upload', upload))
  .listen(3000);
console.log('Connection established, see effect http://127.0.0.1:3000/');

(Complete code can be executed koa-demo Les15 view effect)
After submitting the content, you can see the body content on both the page and the terminal.

Reference material

Koa (koajs)
Koa examples
Koa Framework Tutorial

Posted by maineyak on Tue, 13 Aug 2019 09:09:28 -0700