Home  >  Article  >  Web Front-end  >  What is koa in nodejs

What is koa in nodejs

青灯夜游
青灯夜游Original
2021-10-29 14:54:022891browse

koa refers to a web framework based on Node similar to Express, and is committed to becoming a smaller, more expressive, and more robust cornerstone in the field of web application and API development. Koa does not bundle any middleware, but provides a set of elegant methods to help users write server-side applications quickly and happily.

What is koa in nodejs

The operating environment of this tutorial: windows7 system, nodejs version 12.19.0&&koa2.0, Dell G3 computer.

Koa is a Web development framework similar to Express, and the founder is the same person. Its main feature is that it uses the ES6 Generator function and redesigns the architecture. In other words, the principle and internal structure of Koa are very similar to Express, but the syntax and internal structure have been upgraded.

Koa is a new web framework built by the people behind Express and is committed to becoming a smaller, more expressive, and more robust cornerstone of web application and API development. By leveraging async functions, Koa helps you discard callback functions and greatly enhance error handling. Koa does not bundle any middleware, but provides a set of elegant methods to help you write server-side applications quickly and happily.

OfficialfaqThere is such a question: "Why is koa not Express 4.0?", the answer is this: "Koa is very different from Express, the entire design is different , so if you upgrade Express 3.0 to 4.0 in this way, it means rewriting the entire program. Therefore, we feel that creating a new library is a more appropriate approach."

1 Koa application

A Koa application is a object, containing an middleware array, which is composed of a set of Generator functionscomposition. These functions are responsible for various processing of HTTP requests, such as generating cache, specifying proxy, request redirection, etc.

var koa = require('koa');
var app = koa();

app.use(function *(){
  this.body = 'Hello World';
});

app.listen(3000);
  • In the above code, the variable app is a Koa application. It listens on port 3000 and returns a web page with the content Hello World. The
  • app.use method is used to add the Generator function to the middleware array.
  • listen method specifies the listening port and starts the current application.
    It is actually equivalent to the code below.
var http = require('http');
var koa = require('koa');
var app = koa();
http.createServer(app.callback()).listen(3000);

2 Middleware

Koa’s middleware is very similar to Express’s middleware, which also processes HTTP requests. Processing function , but must be a Generator function.
Moreover, Koa's middleware is a Cascading structure, that is to say, it is called layer by layer, The first middleware calls the second middleware , the second calls the third , and so on. The upstream middleware must wait until the downstream middleware returns the result before continuing to execute, which is very similar to recursion.
Middleware is registered through the use method of the current application.

app.use(function* (next){
  var start = new Date; // (1)
  yield next;  // (2)
  var ms = new Date - start; // (3)
  console.log('%s %s - %s', this.method, this.url, ms); // (4)
});

In the above code, the parameter of the app.use method is the middleware, which is a Generator function, The biggest feature is the function Between the command and the parameter, must have an asterisk . The parameter next of the Generator function represents the next middleware.
GeneratorThe yield command is used inside the function to transfer the execution right of the program to the next middleware, that is, yield next, and you have to wait until the next middleware Only when the result is returned will execution continue.

  • In the above code, Inside the Generator function body, the first line of assignment statements is executed first and the timing starts,
  • The second lineyieldThe statement will hand over the execution right to the next middleware, and the current middleware will suspend execution.
  • When all subsequent middlewares are executed, the execution right will return to the place where it was originally paused, and execution will continue. The third line will be executed.
  • Calculate how long this process took, and the fourth line will print out this time.
    The following is an example of two middleware cascades.
app.use(function *() {
  this.body = "header\n";
  yield saveResults.call(this);
  this.body += "footer\n";
});

function *saveResults() {
  this.body += "Results Saved!\n";
}

In the above code, the first middleware calls the second middleware saveResults, and they both write content to this.body. Finally, the output of this.body is as follows.

header
Results Saved!
footer

As long as one middleware is missing the yield next statement, subsequent middleware will not be executed. This should be noted.

app.use(function *(next){
  console.log('>> one');
  yield next;
  console.log(&#39;<< one&#39;);
});

app.use(function *(next){
  console.log(&#39;>> two&#39;);
  this.body = &#39;two&#39;;
  console.log(&#39;<< two&#39;);
});

app.use(function *(next){
  console.log(&#39;>> three&#39;);
  yield next;
  console.log(&#39;<< three&#39;);
});

In the above code, because the second middleware is missing the yield next statement, the third middleware will not be executed.
If you want to skip a middleware, you can directly write return yield next in the first line of the middleware.

app.use(function* (next) {
  if (skip) return yield next;
})

Since Koa requires that the only parameter of the middleware is next, if you want to pass in other parameters, you must write another function that returns the Generator function .

function logger(format) {
  return function *(next){
    var str = format
      .replace(&#39;:method&#39;, this.method)
      .replace(&#39;:url&#39;, this.url);

    console.log(str);

    yield next;
  }
}
app.use(logger(&#39;:method :url&#39;));

In the above code, the real middleware is the return value of the logger function, and the logger function can accept parameters.

3 多个中间件的合并

由于中间件的参数统一为next(意为下一个中间件),因此可以使用.call(this, next),将多个中间件进行合并。

function *random(next) {
  if (&#39;/random&#39; == this.path) {
    this.body = Math.floor(Math.random()*10);
  } else {
    yield next;
  }
};

function *backwards(next) {
  if (&#39;/backwards&#39; == this.path) {
    this.body = &#39;sdrawkcab&#39;;
  } else {
    yield next;
  }
}

function *pi(next) {
  if (&#39;/pi&#39; == this.path) {
    this.body = String(Math.PI);
  } else {
    yield next;
  }
}

function *all(next) {
  yield random.call(this, backwards.call(this, pi.call(this, next)));
}
app.use(all);

上面代码中,中间件all内部,就是依次调用random、backwards、pi,后一个中间件就是前一个中间件的参数。
Koa内部使用koa-compose模块,进行同样的操作,下面是它的源码。

function compose(middleware){
  return function *(next){
    if (!next) next = noop();

    var i = middleware.length;

    while (i--) {
      next = middleware[i].call(this, next);
    }

    yield *next;
  }
}

function *noop(){}

上面代码中,middleware是中间件数组。前一个中间件的参数是后一个中间件,依次类推。如果最后一个中间件没有next参数,则传入一个空函数。

4 路由

可以通过this.path属性,判断用户请求的路径,从而起到路由作用。

app.use(function* (next) {
  if (this.path === &#39;/&#39;) {
    this.body = &#39;we are at home!&#39;;
  }
})

// 等同于

app.use(function* (next) {
  if (this.path !== &#39;/&#39;) return yield next;
  this.body = &#39;we are at home!&#39;;
})

下面是多路径的例子。

let koa = require(&#39;koa&#39;)

let app = koa()

// normal route
app.use(function* (next) {
  if (this.path !== &#39;/&#39;) {
    return yield next
  }

  this.body = &#39;hello world&#39;
});

// /404 route
app.use(function* (next) {
  if (this.path !== &#39;/404&#39;) {
    return yield next;
  }

  this.body = &#39;page not found&#39;
});

// /500 route
app.use(function* (next) {
  if (this.path !== &#39;/500&#39;) {
    return yield next;
  }

  this.body = &#39;internal server error&#39;
});

app.listen(8080)

上面代码中,每一个中间件负责一个路径,如果路径不符合,就传递给下一个中间件。
复杂的路由需要安装koa-router插件。

var app = require(&#39;koa&#39;)();
var Router = require(&#39;koa-router&#39;);

var myRouter = new Router();

myRouter.get(&#39;/&#39;, function *(next) {
  this.response.body = &#39;Hello World!&#39;;
});

app.use(myRouter.routes());

app.listen(3000);

上面代码对根路径设置路由。
Koa-router实例提供一系列动词方法,即一种HTTP动词对应一种方法。典型的动词方法有以下五种。

  • router.get()
  • router.post()
  • router.put()
  • router.del()
  • router.patch()
    这些动词方法可以接受两个参数,第一个是路径模式,第二个是对应的控制器方法(中间件),定义用户请求该路径时服务器行为。
router.get(&#39;/&#39;, function *(next) {
  this.body = &#39;Hello World!&#39;;
});

上面代码中,router.get方法的第一个参数是根路径,第二个参数是对应的函数方法。
注意,路径匹配的时候,不会把查询字符串考虑在内。比如,/index?param=xyz匹配路径/index。
有些路径模式比较复杂,Koa-router允许为路径模式起别名。
起名时,别名要添加为动词方法的第一个参数,这时动词方法变成接受三个参数。

router.get(&#39;user&#39;, &#39;/users/:id&#39;, function *(next) {
 // ...
});

上面代码中,路径模式\users\:id的名字就是user。路径的名称,可以用来引用对应的具体路径,比如url方法可以根据路径名称,结合给定的参数,生成具体的路径。

router.url(&#39;user&#39;, 3);
// => "/users/3"

router.url(&#39;user&#39;, { id: 3 });
// => "/users/3"

上面代码中,user就是路径模式的名称,对应具体路径/users/:id。url方法的第二个参数3,表示给定id的值是3,因此最后生成的路径是/users/3。
Koa-router允许为路径统一添加前缀。

var router = new Router({
  prefix: &#39;/users&#39;
});

router.get(&#39;/&#39;, ...); // 等同于"/users"
router.get(&#39;/:id&#39;, ...); // 等同于"/users/:id"

路径的参数通过this.params属性获取,该属性返回一个对象,所有路径参数都是该对象的成员。

// 访问 /programming/how-to-node
router.get(&#39;/:category/:title&#39;, function *(next) {
  console.log(this.params);
  // => { category: &#39;programming&#39;, title: &#39;how-to-node&#39; }
});
param方法可以针对命名参数,设置验证条件。

router
  .get(&#39;/users/:user&#39;, function *(next) {
    this.body = this.user;
  })
  .param(&#39;user&#39;, function *(id, next) {
    var users = [ &#39;0号用户&#39;, &#39;1号用户&#39;, &#39;2号用户&#39;];
    this.user = users[id];
    if (!this.user) return this.status = 404;
    yield next;
  })

上面代码中,如果/users/:user的参数user对应的不是有效用户(比如访问/users/3),param方法注册的中间件会查到,就会返回404错误。
redirect方法会将某个路径的请求,重定向到另一个路径,并返回301状态码。

router.redirect(&#39;/login&#39;, &#39;sign-in&#39;);

// 等同于
router.all(&#39;/login&#39;, function *() {
  this.redirect(&#39;/sign-in&#39;);
  this.status = 301;
});

redirect方法的第一个参数是请求来源,第二个参数是目的地,两者都可以用路径模式的别名代替。

5 context对象

  • 中间件当中的this表示上下文对象context,代表一次HTTP请求和回应,即一次访问/回应的所有信息,都可以从上下文对象获得。
  • context对象封装了request和response对象,并且提供了一些辅助方法。每次HTTP请求,就会创建一个新的context对象。
app.use(function *(){
  this; // is the Context
  this.request; // is a koa Request
  this.response; // is a koa Response
});

context对象的很多方法,其实是定义在ctx.request对象或ctx.response对象上面
比如,ctx.typectx.length对应于ctx.response.typectx.response.length,ctx.path和ctx.method对应于ctx.request.path和ctx.request.method。
context对象的全局属性。

  • request:指向Request对象
  • response:指向Response对象
  • req:指向Node的request对象
  • req:指向Node的response对象
  • app:指向App对象
  • state:用于在中间件传递信息。
this.state.user = yield User.find(id);

上面代码中,user属性存放在this.state对象上面,可以被另一个中间件读取。
context对象的全局方法。

  • throw():抛出错误,直接决定了HTTP回应的状态码。
  • assert():如果一个表达式为false,则抛出一个错误。
this.throw(403);
this.throw(&#39;name required&#39;, 400);
this.throw(&#39;something exploded&#39;);

this.throw(400, &#39;name required&#39;);
// 等同于
var err = new Error(&#39;name required&#39;);
err.status = 400;
throw err;

6 错误处理机制

Koa提供内置的错误处理机制,任何中间件抛出的错误都会被捕捉到,引发向客户端返回一个500错误,而不会导致进程停止,因此也就不需要forever这样的模块重启进程。

app.use(function *() {
  throw new Error();
});

上面代码中,中间件内部抛出一个错误,并不会导致Koa应用挂掉。Koa内置的错误处理机制,会捕捉到这个错误。
当然,也可以额外部署自己的错误处理机制。

app.use(function *() {
  try {
    yield saveResults();
  } catch (err) {
    this.throw(400, &#39;数据无效&#39;);
  }
});

上面代码自行部署了try...catch代码块,一旦产生错误,就用this.throw方法抛出。该方法可以将指定的状态码和错误信息,返回给客户端。
对于未捕获错误,可以设置error事件的监听函数。

app.on(&#39;error&#39;, function(err){
  log.error(&#39;server error&#39;, err);
});

error事件的监听函数还可以接受上下文对象,作为第二个参数。

app.on(&#39;error&#39;, function(err, ctx){
  log.error(&#39;server error&#39;, err, ctx);
});

如果一个错误没有被捕获,koa会向客户端返回一个500错误“Internal Server Error”。
this.throw方法用于向客户端抛出一个错误。

this.throw(403);
this.throw(&#39;name required&#39;, 400);
this.throw(400, &#39;name required&#39;);
this.throw(&#39;something exploded&#39;);

this.throw(&#39;name required&#39;, 400)
// 等同于
var err = new Error(&#39;name required&#39;);
err.status = 400;
throw err;
this.throw方法的两个参数,一个是错误码,另一个是报错信息。如果省略状态码,默认是500错误。

this.assert方法用于在中间件之中断言,用法类似于Node的assert模块。

this.assert(this.user, 401, &#39;User not found. Please login!&#39;);

上面代码中,如果this.user属性不存在,会抛出一个401错误。
由于中间件是层级式调用,所以可以把try { yield next }当成第一个中间件。

app.use(function *(next) {
  try {
    yield next;
  } catch (err) {
    this.status = err.status || 500;
    this.body = err.message;
    this.app.emit(&#39;error&#39;, err, this);
  }
});

app.use(function *(next) {
  throw new Error(&#39;some error&#39;);
})

7 cookie

cookie的读取和设置。

this.cookies.get(&#39;view&#39;);
this.cookies.set(&#39;view&#39;, n);

get和set方法都可以接受第三个参数,表示配置参数。其中的signed参数,用于指定cookie是否加密。
如果指定加密的话,必须用app.keys指定加密短语。

app.keys = [&#39;secret1&#39;, &#39;secret2&#39;];
this.cookies.set(&#39;name&#39;, &#39;张三&#39;, { signed: true });

this.cookie的配置对象的属性如下。

  • signed:cookie是否加密。
  • expires:cookie何时过期
  • path:cookie的路径,默认是“/”。
  • domain:cookie的域名。
  • secure:cookie是否只有https请求下才发送。
  • httpOnly:是否只有服务器可以取到cookie,默认为true。

8 session

var session = require(&#39;koa-session&#39;);
var koa = require(&#39;koa&#39;);
var app = koa();
app.keys = [&#39;some secret hurr&#39;];
app.use(session(app));

app.use(function *(){
  var n = this.session.views || 0;
  this.session.views = ++n;
  this.body = n + &#39; views&#39;;
})

app.listen(3000);
console.log(&#39;listening on port 3000&#39;);

【推荐学习:《nodejs 教程》】

The above is the detailed content of What is koa in nodejs. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn