koa は、Express と同様の Node ベースの Web フレームワークを指し、Web アプリケーションおよび API 開発の分野において、より小さく、より表現力豊かで、より堅牢な基盤となるよう取り組んでいます。 Koa はミドルウェアをバンドルしていませんが、ユーザーがサーバー側アプリケーションを迅速かつ楽しく作成できるようにするための一連のエレガントなメソッドを提供します。
このチュートリアルの動作環境: Windows7 システム、nodejs バージョン 12.19.0&&koa2.0、Dell G3 コンピューター。
Koa
は Express に似た Web
開発フレームワークであり、創設者は同じ人です。最大の特徴は、ES6 Generator機能
を利用し、アーキテクチャを再設計したことです。つまり、Koa の原理と内部構造は Express と非常に似ていますが、構文と内部構造はアップグレードされています。
Koa は、Express の背後にいる人々によって構築された新しい Web フレームワークであり、Web アプリケーションと API 開発のより小さく、より表現力豊かで、より堅牢な基盤となるよう取り組んでいます。 Koa は、非同期関数を利用することにより、コールバック関数を破棄し、エラー処理を大幅に強化するのに役立ちます。 Koa にはミドルウェアはバンドルされていませんが、サーバー側アプリケーションを迅速かつ快適に作成できるようにする一連のエレガントなメソッドが提供されています。
公式faq
「なぜkoaはExpress 4.0ではないのですか?」という質問がありますが、その答えは次のとおりです: 「KoaはExpressとは大きく異なり、全体の設計は次のとおりです。」したがって、この方法で Express 3.0 を 4.0 にアップグレードすると、プログラム全体を書き直すことになります。そのため、新しいライブラリを作成する方が適切なアプローチであると考えています。"
A Koa application
は、Middleware
配列を含む オブジェクト
であり、Generator 関数のセットで構成されます
構成。これらの関数は、キャッシュの生成、プロキシの指定、リクエストのリダイレクトなど、HTTP リクエストのさまざまな処理を担当します。
var koa = require('koa'); var app = koa(); app.use(function *(){ this.body = 'Hello World'; }); app.listen(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);
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
Generator
yield コマンドは関数内で使用され、プログラムの実行権を次のミドルウェア (
yield next) に移譲します。次のミドルウェアまで待つ必要があります。結果が返された場合にのみ実行が続行されます。 上記のコードでは、
ジェネレーター関数本体2行目
yieldapp.use(function *() { this.body = "header\n"; yield saveResults.call(this); this.body += "footer\n"; }); function *saveResults() { this.body += "Results Saved!\n"; }
header Results Saved! footer
1 つのミドルウェアに
yield next ステートメントが指定されていない限り、後続のミドルウェアは実行されません。 app.use(function *(next){ console.log('>> one'); yield next; console.log('<< one'); }); app.use(function *(next){ console.log('>> two'); this.body = 'two'; console.log('<< two'); }); app.use(function *(next){ console.log('>> three'); yield next; console.log('<< three'); });
上記のコードでは、2 番目のミドルウェアに
yield nextapp.use(function* (next) { if (skip) return yield next; })
Koa ではミドルウェアの唯一のパラメーターが next である必要があるため、他のパラメーターを渡したい場合は、
Generator を返す別の関数を作成する必要があります。関数### 。 function logger(format) { return function *(next){ var str = format .replace(':method', this.method) .replace(':url', this.url); console.log(str); yield next; } } app.use(logger(':method :url'));
上記のコードでは、実際のミドルウェアはロガー関数の戻り値であり、ロガー関数はパラメーターを受け取ることができます。
由于中间件的参数统一为next
(意为下一个中间件),因此可以使用.call(this, next)
,将多个中间件进行合并。
function *random(next) { if ('/random' == this.path) { this.body = Math.floor(Math.random()*10); } else { yield next; } }; function *backwards(next) { if ('/backwards' == this.path) { this.body = 'sdrawkcab'; } else { yield next; } } function *pi(next) { if ('/pi' == 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参数,则传入一个空函数。
可以通过this.path属性,判断用户请求的路径,从而起到路由作用。
app.use(function* (next) { if (this.path === '/') { this.body = 'we are at home!'; } }) // 等同于 app.use(function* (next) { if (this.path !== '/') return yield next; this.body = 'we are at home!'; })
下面是多路径的例子。
let koa = require('koa') let app = koa() // normal route app.use(function* (next) { if (this.path !== '/') { return yield next } this.body = 'hello world' }); // /404 route app.use(function* (next) { if (this.path !== '/404') { return yield next; } this.body = 'page not found' }); // /500 route app.use(function* (next) { if (this.path !== '/500') { return yield next; } this.body = 'internal server error' }); app.listen(8080)
上面代码中,每一个中间件负责一个路径,如果路径不符合,就传递给下一个中间件。
复杂的路由需要安装koa-router插件。
var app = require('koa')(); var Router = require('koa-router'); var myRouter = new Router(); myRouter.get('/', function *(next) { this.response.body = 'Hello World!'; }); app.use(myRouter.routes()); app.listen(3000);
上面代码对根路径设置路由。
Koa-router实例提供一系列动词方法,即一种HTTP动词对应一种方法。典型的动词方法有以下五种。
路径模式
,第二个是对应的控制器方法
(中间件),定义用户请求该路径时服务器行为。router.get('/', function *(next) { this.body = 'Hello World!'; });
上面代码中,router.get
方法的第一个参数是根路径,第二个参数是对应的函数方法。注意
,路径匹配的时候,不会把查询字符串考虑在内。比如,/index?param=xyz匹配路径/index。
有些路径模式比较复杂,Koa-router允许为路径模式起别名。
起名时,别名要添加为动词方法的第一个参数,这时动词方法变成接受三个参数。
router.get('user', '/users/:id', function *(next) { // ... });
上面代码中,路径模式\users\:id
的名字就是user
。路径的名称,可以用来引用对应的具体路径,比如url方法可以根据路径名称,结合给定的参数,生成具体的路径。
router.url('user', 3); // => "/users/3" router.url('user', { id: 3 }); // => "/users/3"
上面代码中,user就是路径模式的名称,对应具体路径/users/:id。url方法的第二个参数3,表示给定id的值是3,因此最后生成的路径是/users/3。
Koa-router允许为路径统一添加前缀。
var router = new Router({ prefix: '/users' }); router.get('/', ...); // 等同于"/users" router.get('/:id', ...); // 等同于"/users/:id"
路径的参数通过this.params
属性获取,该属性返回一个对象,所有路径参数都是该对象的成员。
// 访问 /programming/how-to-node router.get('/:category/:title', function *(next) { console.log(this.params); // => { category: 'programming', title: 'how-to-node' } }); param方法可以针对命名参数,设置验证条件。 router .get('/users/:user', function *(next) { this.body = this.user; }) .param('user', function *(id, next) { var users = [ '0号用户', '1号用户', '2号用户']; this.user = users[id]; if (!this.user) return this.status = 404; yield next; })
上面代码中,如果/users/:user
的参数user对应的不是有效用户(比如访问/users/3),param方法注册的中间件会查到,就会返回404错误。
redirect方法会将某个路径的请求,重定向到另一个路径,并返回301状态码。
router.redirect('/login', 'sign-in'); // 等同于 router.all('/login', function *() { this.redirect('/sign-in'); this.status = 301; });
redirect方法的第一个参数是请求来源,第二个参数是目的地,两者都可以用路径模式的别名代替。
this
表示上下文对象context
,代表一次HTTP请求和回应
,即一次访问/回应的所有信息,都可以从上下文对象获得。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.type
和ctx.length
对应于ctx.response.type
和ctx.response.length
,ctx.path和ctx.method对应于ctx.request.path和ctx.request.method。
context对象的全局属性。
this.state.user = yield User.find(id);
上面代码中,user
属性存放在this.state
对象上面,可以被另一个中间件读取。
context对象的全局方法。
this.throw(403); this.throw('name required', 400); this.throw('something exploded'); this.throw(400, 'name required'); // 等同于 var err = new Error('name required'); err.status = 400; throw err;
Koa提供内置的错误处理机制,任何中间件抛出的错误都会被捕捉到,引发向客户端返回一个500
错误,而不会导致进程停止,因此也就不需要forever这样的模块重启进程。
app.use(function *() { throw new Error(); });
上面代码中,中间件内部抛出一个错误,并不会导致Koa应用挂掉。Koa内置的错误处理机制,会捕捉到这个错误。
当然,也可以额外部署自己的错误处理机制。
app.use(function *() { try { yield saveResults(); } catch (err) { this.throw(400, '数据无效'); } });
上面代码自行部署了try...catch
代码块,一旦产生错误,就用this.throw
方法抛出。该方法可以将指定的状态码和错误信息,返回给客户端。
对于未捕获错误,可以设置error事件的监听函数。
app.on('error', function(err){ log.error('server error', err); });
error
事件的监听函数还可以接受上下文对象,作为第二个参数。
app.on('error', function(err, ctx){ log.error('server error', err, ctx); });
如果一个错误没有被捕获,koa会向客户端返回一个500错误“Internal Server Error”。this.throw
方法用于向客户端抛出一个错误。
this.throw(403); this.throw('name required', 400); this.throw(400, 'name required'); this.throw('something exploded'); this.throw('name required', 400) // 等同于 var err = new Error('name required'); err.status = 400; throw err; this.throw方法的两个参数,一个是错误码,另一个是报错信息。如果省略状态码,默认是500错误。 this.assert方法用于在中间件之中断言,用法类似于Node的assert模块。 this.assert(this.user, 401, 'User not found. Please login!');
上面代码中,如果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('error', err, this); } }); app.use(function *(next) { throw new Error('some error'); })
cookie的读取和设置。
this.cookies.get('view'); this.cookies.set('view', n);
get和set
方法都可以接受第三个参数,表示配置参数。其中的signed
参数,用于指定cookie
是否加密。
如果指定加密的话,必须用app.keys指定加密短语。
app.keys = ['secret1', 'secret2']; this.cookies.set('name', '张三', { signed: true });
this.cookie的配置对象的属性如下。
signed
:cookie是否加密。expires
:cookie何时过期path
:cookie的路径,默认是“/”。domain
:cookie的域名。secure
:cookie是否只有https请求下才发送。httpOnly
:是否只有服务器可以取到cookie,默认为true。var session = require('koa-session'); var koa = require('koa'); var app = koa(); app.keys = ['some secret hurr']; app.use(session(app)); app.use(function *(){ var n = this.session.views || 0; this.session.views = ++n; this.body = n + ' views'; }) app.listen(3000); console.log('listening on port 3000');
【推荐学习:《nodejs 教程》】
以上がNodejsのkoaとは何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。