Nodejsのkoaとは何ですか

青灯夜游
青灯夜游オリジナル
2021-10-29 14:54:022971ブラウズ

koa は、Express と同様の Node ベースの Web フレームワークを指し、Web アプリケーションおよび API 開発の分野において、より小さく、より表現力豊かで、より堅牢な基盤となるよう取り組んでいます。 Koa はミドルウェアをバンドルしていませんが、ユーザーがサーバー側アプリケーションを迅速かつ楽しく作成できるようにするための一連のエレガントなメソッドを提供します。

Nodejsのkoaとは何ですか

このチュートリアルの動作環境: Windows7 システム、nodejs バージョン 12.19.0&&koa2.0、Dell G3 コンピューター。

KoaExpress に似た Web 開発フレームワークであり、創設者は同じ人です。最大の特徴は、ES6 Generator機能を利用し、アーキテクチャを再設計したことです。つまり、Koa の原理と内部構造は Express と非常に似ていますが、構文と内部構造はアップグレードされています。

Koa は、Express の背後にいる人々によって構築された新しい Web フレームワークであり、Web アプリケーションと API 開発のより小さく、より表現力豊かで、より堅牢な基盤となるよう取り組んでいます。 Koa は、非同期関数を利用することにより、コールバック関数を破棄し、エラー処理を大幅に強化するのに役立ちます。 Koa にはミドルウェアはバンドルされていませんが、サーバー側アプリケーションを迅速かつ快適に作成できるようにする一連のエレガントなメソッドが提供されています。

公式faq「なぜkoaはExpress 4.0ではないのですか?」という質問がありますが、その答えは次のとおりです: 「KoaはExpressとは大きく異なり、全体の設計は次のとおりです。」したがって、この方法で Express 3.0 を 4.0 にアップグレードすると、プログラム全体を書き直すことになります。そのため、新しいライブラリを作成する方が適切なアプローチであると考えています。"

1 Koa application

A Koa application は、Middleware 配列を含む オブジェクトであり、Generator 関数のセットで構成されます構成。これらの関数は、キャッシュの生成、プロキシの指定、リクエストのリダイレクトなど、HTTP リクエストのさまざまな処理を担当します。

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

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

app.listen(3000);
  • 上記のコードでは、変数 app は Koa アプリケーションです。ポート 3000 をリッスンし、Hello World というコンテンツを含む Web ページを返します。
  • app.use メソッドは、Generator 関数middleware 配列に追加するために使用されます。
  • listen メソッドは、リスニング ポートを指定し、現在のアプリケーションを起動します。
    これは実際には以下のコードと同等です。
var http = require('http');
var koa = require('koa');
var app = koa();
http.createServer(app.callback()).listen(3000);

2 ミドルウェア

Koa のミドルウェアは、Express のミドルウェア とよく似ており、Express のミドルウェア も HTTP リクエストを処理します。関数 を処理しますが、Generator 関数
である必要があります。 さらに、Koa のミドルウェアは Cascading 構造です。つまり、レイヤーごとに呼び出されます。最初のミドルウェアが 2 番目のミドルウェア を呼び出し、2 番目のミドルウェアが を呼び出します。 3 番目の など。 上流のミドルウェアは、実行を続行する前に、下流のミドルウェアが結果を返すまで待機する必要があります
これは再帰と非常によく似ています。 ミドルウェアは、現在のアプリケーションの use

メソッドを通じて登録されます。

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)
});
上記のコードでは、app.useメソッドのパラメータはミドルウェアであり、Generator関数最大の特徴はfunction コマンド とパラメータの間の にはアスタリスク が必要です。 Generator 関数のパラメーター next
は、次のミドルウェアを表します。 Generatoryield コマンドは関数内で使用され、プログラムの実行権を次のミドルウェア (yield next

) に移譲します。次のミドルウェアまで待つ必要があります。結果が返された場合にのみ実行が続行されます。
  • 上記のコードでは、ジェネレーター関数本体
  • 内で、代入ステートメントの最初の行が最初に実行され、タイミングが開始されます。
  • 2行目yield
  • このステートメントは次のミドルウェアに実行権を渡し、現在のミドルウェアは実行を一時停止します。
  • 後続のすべてのミドルウェアが実行されると、実行権は元の場所に戻ります。元々一時停止されていたため、実行は続行されます。3 行目は実行されます。

  • このプロセスにかかった時間を計算すると、今度は 4 行目が出力されます。
  • 以下は 2 つのミドルウェア カスケードの例です。

app.use(function *() {
  this.body = "header\n";
  yield saveResults.call(this);
  this.body += "footer\n";
});

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

上記のコードでは、最初のミドルウェアが 2 番目のミドルウェア saveResults を呼び出し、両方ともコンテンツを this.body に書き込みます。最終的に this.body の出力は次のようになります。

header
Results Saved!
footer
1 つのミドルウェアに yield next

ステートメントが指定されていない限り、後続のミドルウェアは実行されません。

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;);
});
上記のコードでは、2 番目のミドルウェアに yield next
ステートメントが欠落しているため、3 番目のミドルウェアは実行されません。

ミドルウェアをスキップしたい場合は、ミドルウェアの最初の行に return yield next を直接記述できます。

app.use(function* (next) {
  if (skip) return yield next;
})
Koa ではミドルウェアの唯一のパラメーターが next である必要があるため、他のパラメーターを渡したい場合は、Generator を返す別の関数を作成する必要があります。関数### 。

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;));

上記のコードでは、実際のミドルウェアはロガー関数の戻り値であり、ロガー関数はパラメーターを受け取ることができます。

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 教程》】

以上がNodejsのkoaとは何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。