溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何使用nodejs中的koa

發(fā)布時(shí)間:2021-10-29 15:05:39 來(lái)源:億速云 閱讀:171 作者:iii 欄目:web開發(fā)

本篇內(nèi)容主要講解“如何使用nodejs中的koa”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“如何使用nodejs中的koa”吧!

koa指的是一個(gè)類似于Express的基于Node實(shí)現(xiàn)的web框架,致力于成為web應(yīng)用和API開發(fā)領(lǐng)域中的一個(gè)更小、更富有表現(xiàn)力、更健壯的基石。Koa并沒(méi)有捆綁任何中間件,而是提供了一套優(yōu)雅的方法,幫助用戶快速而愉快地編寫服務(wù)端應(yīng)用程序。

如何使用nodejs中的koa

本教程操作環(huán)境:windows7系統(tǒng)、nodejs 12.19.0&&koa2.0版、Dell G3電腦。

Koa是一個(gè)類似于Express的Web開發(fā)框架,創(chuàng)始人也是同一個(gè)人。它的主要特點(diǎn)是,使用了ES6的Generator函數(shù),進(jìn)行了架構(gòu)的重新設(shè)計(jì)。也就是說(shuō),Koa的原理和內(nèi)部結(jié)構(gòu)很像Express,但是語(yǔ)法和內(nèi)部結(jié)構(gòu)進(jìn)行了升級(jí)。

Koa 是一個(gè)新的 web 框架,由 Express 幕后的原班人馬打造, 致力于成為 web 應(yīng)用和 API 開發(fā)領(lǐng)域中的一個(gè)更小、更富有表現(xiàn)力、更健壯的基石。 通過(guò)利用 async 函數(shù),Koa 幫你丟棄回調(diào)函數(shù),并有力地增強(qiáng)錯(cuò)誤處理。 Koa 并沒(méi)有捆綁任何中間件, 而是提供了一套優(yōu)雅的方法,幫助您快速而愉快地編寫服務(wù)端應(yīng)用程序。

官方faq有這樣一個(gè)問(wèn)題:”為什么koa不是Express 4.0?“,回答是這樣的:”Koa與Express有很大差異,整個(gè)設(shè)計(jì)都是不同的,所以如果將Express 3.0按照這種寫法升級(jí)到4.0,就意味著重寫整個(gè)程序。所以,我們覺(jué)得創(chuàng)造一個(gè)新的庫(kù),是更合適的做法?!?/code>

1 Koa應(yīng)用

一個(gè)Koa應(yīng)用就是一個(gè)對(duì)象,包含了一個(gè)middleware數(shù)組,這個(gè)數(shù)組由一組Generator函數(shù)組成。這些函數(shù)負(fù)責(zé)對(duì)HTTP請(qǐng)求進(jìn)行各種加工,比如生成緩存、指定代理、請(qǐng)求重定向等等。

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

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

app.listen(3000);
  • 上面代碼中,變量app就是一個(gè)Koa應(yīng)用。它監(jiān)聽3000端口,返回一個(gè)內(nèi)容為Hello World的網(wǎng)頁(yè)。

  • app.use方法用于向middleware數(shù)組添加Generator函數(shù)。

  • listen方法指定監(jiān)聽端口,并啟動(dòng)當(dāng)前應(yīng)用。
    它實(shí)際上等同于下面的代碼。

var http = require('http');
var koa = require('koa');
var app = koa();
http.createServer(app.callback()).listen(3000);

2 中間件

Koa的中間件很像Express的中間件,也是對(duì)HTTP請(qǐng)求進(jìn)行處理的函數(shù),但是必須是一個(gè)Generator函數(shù)。
而且,Koa的中間件是一個(gè)級(jí)聯(lián)式(Cascading)的結(jié)構(gòu),也就是說(shuō),屬于是層層調(diào)用,第一個(gè)中間件調(diào)用第二個(gè)中間件第二個(gè)調(diào)用第三個(gè),以此類推。上游的中間件必須等到下游的中間件返回結(jié)果,才會(huì)繼續(xù)執(zhí)行,這點(diǎn)很像遞歸。
中間件通過(guò)當(dāng)前應(yīng)用的use方法注冊(cè)。

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方法的參數(shù)就是中間件,它是一個(gè)Generator函數(shù)最大的特征就是function命令與參數(shù)之間,必須有一個(gè)星號(hào)。Generator函數(shù)的參數(shù)next,表示下一個(gè)中間件。
Generator函數(shù)內(nèi)部使用yield命令,將程序的執(zhí)行權(quán)轉(zhuǎn)交給下一個(gè)中間件,即yield next,要等到下一個(gè)中間件返回結(jié)果,才會(huì)繼續(xù)往下執(zhí)行。

  • 上面代碼中,Generator函數(shù)體內(nèi)部,第一行賦值語(yǔ)句首先執(zhí)行,開始計(jì)時(shí),

  • 第二行yield語(yǔ)句將執(zhí)行權(quán)交給下一個(gè)中間件,當(dāng)前中間件就暫停執(zhí)行

  • 等到后面的中間件全部執(zhí)行完成,執(zhí)行權(quán)就回到原來(lái)暫停的地方,繼續(xù)往下執(zhí)行,這時(shí)才會(huì)執(zhí)行第三行,

  • 計(jì)算這個(gè)過(guò)程一共花了多少時(shí)間,第四行將這個(gè)時(shí)間打印出來(lái)。
    下面是一個(gè)兩個(gè)中間件級(jí)聯(lián)的例子。

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

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

上面代碼中,第一個(gè)中間件調(diào)用第二個(gè)中間件saveResults,它們都向this.body寫入內(nèi)容。最后,this.body的輸出如下。

header
Results Saved!
footer

只要有一個(gè)中間件缺少yield next語(yǔ)句,后面的中間件都不會(huì)執(zhí)行,這一點(diǎn)要引起注意。

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

上面代碼中,因?yàn)榈诙€(gè)中間件少了yield next語(yǔ)句,第三個(gè)中間件并不會(huì)執(zhí)行。
如果想跳過(guò)一個(gè)中間件,可以直接在該中間件的第一行語(yǔ)句寫上return yield next。

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

由于Koa要求中間件唯一的參數(shù)就是next,導(dǎo)致如果要傳入其他參數(shù),必須另外寫一個(gè)返回Generator函數(shù)的函數(shù)。

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

上面代碼中,真正的中間件是logger函數(shù)的返回值,而logger函數(shù)是可以接受參數(shù)的。

3 多個(gè)中間件的合并

由于中間件的參數(shù)統(tǒng)一為next(意為下一個(gè)中間件),因此可以使用.call(this, next),將多個(gè)中間件進(jìn)行合并。

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內(nèi)部,就是依次調(diào)用random、backwards、pi,后一個(gè)中間件就是前一個(gè)中間件的參數(shù)。
Koa內(nèi)部使用koa-compose模塊,進(jìn)行同樣的操作,下面是它的源碼。

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是中間件數(shù)組。前一個(gè)中間件的參數(shù)是后一個(gè)中間件,依次類推。如果最后一個(gè)中間件沒(méi)有next參數(shù),則傳入一個(gè)空函數(shù)。

4 路由

可以通過(guò)this.path屬性,判斷用戶請(qǐng)求的路徑,從而起到路由作用。

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)

上面代碼中,每一個(gè)中間件負(fù)責(zé)一個(gè)路徑,如果路徑不符合,就傳遞給下一個(gè)中間件。
復(fù)雜的路由需要安裝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);

上面代碼對(duì)根路徑設(shè)置路由。
Koa-router實(shí)例提供一系列動(dòng)詞方法,即一種HTTP動(dòng)詞對(duì)應(yīng)一種方法。典型的動(dòng)詞方法有以下五種。

  • router.get()

  • router.post()

  • router.put()

  • router.del()

  • router.patch()
    這些動(dòng)詞方法可以接受兩個(gè)參數(shù),第一個(gè)是路徑模式,第二個(gè)是對(duì)應(yīng)的控制器方法(中間件),定義用戶請(qǐng)求該路徑時(shí)服務(wù)器行為。

router.get('/', function *(next) {
  this.body = 'Hello World!';
});

上面代碼中,router.get方法的第一個(gè)參數(shù)是根路徑,第二個(gè)參數(shù)是對(duì)應(yīng)的函數(shù)方法。
注意,路徑匹配的時(shí)候,不會(huì)把查詢字符串考慮在內(nèi)。比如,/index?param=xyz匹配路徑/index。
有些路徑模式比較復(fù)雜,Koa-router允許為路徑模式起別名。
起名時(shí),別名要添加為動(dòng)詞方法的第一個(gè)參數(shù),這時(shí)動(dòng)詞方法變成接受三個(gè)參數(shù)。

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

上面代碼中,路徑模式\users\:id的名字就是user。路徑的名稱,可以用來(lái)引用對(duì)應(yīng)的具體路徑,比如url方法可以根據(jù)路徑名稱,結(jié)合給定的參數(shù),生成具體的路徑。

router.url('user', 3);
// => "/users/3"

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

上面代碼中,user就是路徑模式的名稱,對(duì)應(yīng)具體路徑/users/:id。url方法的第二個(gè)參數(shù)3,表示給定id的值是3,因此最后生成的路徑是/users/3。
Koa-router允許為路徑統(tǒng)一添加前綴。

var router = new Router({
  prefix: '/users'
});

router.get('/', ...); // 等同于"/users"
router.get('/:id', ...); // 等同于"/users/:id"

路徑的參數(shù)通過(guò)this.params屬性獲取,該屬性返回一個(gè)對(duì)象,所有路徑參數(shù)都是該對(duì)象的成員。

// 訪問(wèn) /programming/how-to-node
router.get('/:category/:title', function *(next) {
  console.log(this.params);
  // => { category: 'programming', title: 'how-to-node' }
});
param方法可以針對(duì)命名參數(shù),設(shè)置驗(yàn)證條件。

router
  .get('/users/:user', function *(next) {
    this.body = this.user;
  })
  .param('user', function *(id, next) {
    var users = [ '0號(hào)用戶', '1號(hào)用戶', '2號(hào)用戶'];
    this.user = users[id];
    if (!this.user) return this.status = 404;
    yield next;
  })

上面代碼中,如果/users/:user的參數(shù)user對(duì)應(yīng)的不是有效用戶(比如訪問(wèn)/users/3),param方法注冊(cè)的中間件會(huì)查到,就會(huì)返回404錯(cuò)誤。
redirect方法會(huì)將某個(gè)路徑的請(qǐng)求,重定向到另一個(gè)路徑,并返回301狀態(tài)碼。

router.redirect('/login', 'sign-in');

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

redirect方法的第一個(gè)參數(shù)是請(qǐng)求來(lái)源,第二個(gè)參數(shù)是目的地,兩者都可以用路徑模式的別名代替。

5 context對(duì)象

  • 中間件當(dāng)中的this表示上下文對(duì)象context,代表一次HTTP請(qǐng)求和回應(yīng),即一次訪問(wèn)/回應(yīng)的所有信息,都可以從上下文對(duì)象獲得。

  • context對(duì)象封裝了request和response對(duì)象,并且提供了一些輔助方法。每次HTTP請(qǐng)求,就會(huì)創(chuàng)建一個(gè)新的context對(duì)象。

app.use(function *(){
  this; // is the Context
  this.request; // is a koa Request
  this.response; // is a koa Response
});

context對(duì)象的很多方法,其實(shí)是定義在ctx.request對(duì)象或ctx.response對(duì)象上面
比如,ctx.typectx.length對(duì)應(yīng)于ctx.response.typectx.response.length,ctx.path和ctx.method對(duì)應(yīng)于ctx.request.path和ctx.request.method。
context對(duì)象的全局屬性。

  • request:指向Request對(duì)象

  • response:指向Response對(duì)象

  • req:指向Node的request對(duì)象

  • req:指向Node的response對(duì)象

  • app:指向App對(duì)象

  • state:用于在中間件傳遞信息。

this.state.user = yield User.find(id);

上面代碼中,user屬性存放在this.state對(duì)象上面,可以被另一個(gè)中間件讀取。
context對(duì)象的全局方法。

  • throw():拋出錯(cuò)誤,直接決定了HTTP回應(yīng)的狀態(tài)碼。

  • assert():如果一個(gè)表達(dá)式為false,則拋出一個(gè)錯(cuò)誤。

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;

6 錯(cuò)誤處理機(jī)制

Koa提供內(nèi)置的錯(cuò)誤處理機(jī)制,任何中間件拋出的錯(cuò)誤都會(huì)被捕捉到,引發(fā)向客戶端返回一個(gè)500錯(cuò)誤,而不會(huì)導(dǎo)致進(jìn)程停止,因此也就不需要forever這樣的模塊重啟進(jìn)程。

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

上面代碼中,中間件內(nèi)部拋出一個(gè)錯(cuò)誤,并不會(huì)導(dǎo)致Koa應(yīng)用掛掉。Koa內(nèi)置的錯(cuò)誤處理機(jī)制,會(huì)捕捉到這個(gè)錯(cuò)誤。
當(dāng)然,也可以額外部署自己的錯(cuò)誤處理機(jī)制。

app.use(function *() {
  try {
    yield saveResults();
  } catch (err) {
    this.throw(400, '數(shù)據(jù)無(wú)效');
  }
});

上面代碼自行部署了try...catch代碼塊,一旦產(chǎn)生錯(cuò)誤,就用this.throw方法拋出。該方法可以將指定的狀態(tài)碼和錯(cuò)誤信息,返回給客戶端。
對(duì)于未捕獲錯(cuò)誤,可以設(shè)置error事件的監(jiān)聽函數(shù)。

app.on('error', function(err){
  log.error('server error', err);
});

error事件的監(jiān)聽函數(shù)還可以接受上下文對(duì)象,作為第二個(gè)參數(shù)。

app.on('error', function(err, ctx){
  log.error('server error', err, ctx);
});

如果一個(gè)錯(cuò)誤沒(méi)有被捕獲,koa會(huì)向客戶端返回一個(gè)500錯(cuò)誤“Internal Server Error”。
this.throw方法用于向客戶端拋出一個(gè)錯(cuò)誤。

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方法的兩個(gè)參數(shù),一個(gè)是錯(cuò)誤碼,另一個(gè)是報(bào)錯(cuò)信息。如果省略狀態(tài)碼,默認(rèn)是500錯(cuò)誤。

this.assert方法用于在中間件之中斷言,用法類似于Node的assert模塊。

this.assert(this.user, 401, 'User not found. Please login!');

上面代碼中,如果this.user屬性不存在,會(huì)拋出一個(gè)401錯(cuò)誤。
由于中間件是層級(jí)式調(diào)用,所以可以把try { yield next }當(dāng)成第一個(gè)中間件。

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

7 cookie

cookie的讀取和設(shè)置。

this.cookies.get('view');
this.cookies.set('view', n);

get和set方法都可以接受第三個(gè)參數(shù),表示配置參數(shù)。其中的signed參數(shù),用于指定cookie是否加密。
如果指定加密的話,必須用app.keys指定加密短語(yǔ)。

app.keys = ['secret1', 'secret2'];
this.cookies.set('name', '張三', { signed: true });

this.cookie的配置對(duì)象的屬性如下。

  • signed:cookie是否加密。

  • expires:cookie何時(shí)過(guò)期

  • path:cookie的路徑,默認(rèn)是“/”。

  • domain:cookie的域名。

  • secure:cookie是否只有https請(qǐng)求下才發(fā)送。

  • httpOnly:是否只有服務(wù)器可以取到cookie,默認(rèn)為true。

8 session

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

到此,相信大家對(duì)“如何使用nodejs中的koa”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI