Koa2

Koa 必須使用 7.6 以上的版本。如果你的版本低于這個要求,就要先升級 Node。

基本用法

Koa 提供一個 Context 對象,表示一次對話的上下文(包括 HTTP 請求和 HTTP 回復(fù))。通過加工這個對象,就可以控制返回給用戶的內(nèi)容。
Context.response.body屬性就是發(fā)送給用戶的內(nèi)容。如下:

const Koa = require('koa');
const app = new Koa();
const main = ctx => {
  ctx.response.body = 'Hello World';
};
app.use(main);
app.listen(3000);    //架設(shè)http服務(wù)器

app.listen()就是http.createServer(app.callback()).listen(...)的縮寫。

main函數(shù)用來設(shè)置ctx.response.body。然后,使用app.use方法加載main函數(shù)。
ctx.response代表 HTTP Response。同樣地,ctx.request代表 HTTP Request。

Koa 默認(rèn)的返回類型是text/plain,如果想返回其他類型內(nèi)容,可以用ctx.request.accepts判斷一下,客戶端希望接受什么數(shù)據(jù)(根據(jù) HTTP Request 的Accept字段),然后使用ctx.response.type指定返回類型。

const main = ctx => {
  if (ctx.request.accepts('xml')) {
    ctx.response.type = 'xml';
    ctx.response.body = '<data>Hello World</data>';
  } else if (ctx.request.accepts('json')) {
    ctx.response.type = 'json';
    ctx.response.body = { data: 'Hello World' };
  } else if (ctx.request.accepts('html')) {
    ctx.response.type = 'html';
    ctx.response.body = '<p>Hello World</p>';
  } else {
    ctx.response.type = 'text';
    ctx.response.body = 'Hello World';
  }
};

路由

原生路由

通過ctx.request.path可以獲取用戶請求的路徑,由此實現(xiàn)簡單的路由。

const main = ctx => {
  if (ctx.request.path !== '/') {
    ctx.response.type = 'html';
    ctx.response.body = '<a href="/">Index Page</a>';
  } else {
    ctx.response.body = 'Hello World';
  }
};

koa-route 模塊

原生路由用起來不太方便,我們可以使用封裝好的[koa-route]模塊。

const route = require('koa-route');
const about = ctx => {
  ctx.response.type = 'html';
  ctx.response.body = '<a href="/">Index Page</a>';
};
const main = ctx => {
  ctx.response.body = 'Hello World';
};
app.use(route.get('/', main));
app.use(route.get('/about', about));

上面代碼中,根路徑/的處理函數(shù)是main,/about路徑的處理函數(shù)是about。

靜態(tài)資源

靜態(tài)資源一個個寫路由很麻煩,也沒必要。[koa-static]模塊封裝了這部分的請求。

const path = require('path');
const serve = require('koa-static');
const main = serve(path.join(__dirname));
app.use(main);

重定向

服務(wù)器需要重定向(redirect)訪問請求,ctx.response.redirect()方法可以發(fā)出一個302跳轉(zhuǎn),將用戶導(dǎo)向另一個路由。

const redirect = ctx => {
  ctx.response.redirect('/');
  ctx.response.body = '<a href="/">Index Page</a>';
};
app.use(route.get('/redirect', redirect));

中間件

const logger = (ctx, next) => {
  console.log(`${Date.now()} ${ctx.request.method} ${ctx.request.url}`);
  next();
}
app.use(logger);

上面代碼中的logger函數(shù)就叫做"中間件"(middleware),因為它處在 HTTP Request 和 HTTP Response 中間,用來實現(xiàn)某種中間功能。app.use()用來加載中間件。

Koa 所有的功能都是通過中間件實現(xiàn)的,默認(rèn)接受兩個參數(shù),第一個參數(shù)是 Context 對象,第二個參數(shù)是next函數(shù)。只要調(diào)用next函數(shù),就可以把執(zhí)行權(quán)轉(zhuǎn)交給下一個中間件。
多個中間件會形成一個棧結(jié)構(gòu)(middle stack),以"先進(jìn)后出"(first-in-last-out)的順序執(zhí)行。如果中間件內(nèi)部沒有調(diào)用next函數(shù),那么執(zhí)行權(quán)就不會傳遞下去。

  • 最外層的中間件首先執(zhí)行。
  • 調(diào)用next函數(shù),把執(zhí)行權(quán)交給下一個中間件。
    ...
  • 最內(nèi)層的中間件最后執(zhí)行。
  • 執(zhí)行結(jié)束后,把執(zhí)行權(quán)交回上一層的中間件。
    ...
  • 最外層的中間件收回執(zhí)行權(quán)之后,執(zhí)行next函數(shù)后面的代碼。

如果有異步操作(比如讀取數(shù)據(jù)庫),中間件就必須寫成 [async 函數(shù)]。

const fs = require('fs.promised');
const Koa = require('koa');
const app = new Koa();
const main = async function (ctx, next) {
  ctx.response.type = 'html';
  ctx.response.body = await fs.readFile('./demos/template.html', 'utf8');
};
app.use(main);
app.listen(3000);

中間件的合成[koa-compose]模塊可以將多個中間件合成為一個。

const middlewares = compose([logger, main]);
app.use(middlewares);

Koa 提供了ctx.throw()方法,用來拋出錯誤。ctx.throw(500)就是拋出500錯誤。

const main = ctx => {
  ctx.throw(500);
};

如果將ctx.response.status設(shè)置成404,就相當(dāng)于ctx.throw(404),返回404錯誤。

const main = ctx => {
  ctx.response.status = 404;
  ctx.response.body = 'Page Not Found';
};

為了方便處理錯誤,最好使用try...catch將其捕獲。但是,為每個中間件都寫try...catch太麻煩,我們可以讓最外層的中間件,負(fù)責(zé)所有中間件的錯誤處理。

const handler = async (ctx, next) => {
  try {
    await next();  // 此處包住了后面內(nèi)部所有中間件
  } catch (err) {
    ctx.response.status = err.statusCode || err.status || 500;
    ctx.response.body = {
      message: err.message
    };
  }
};
const main = ctx => {
  ctx.throw(500);
};
app.use(handler);
app.use(main);

Web App 的功能

Cookies

ctx.cookies用來讀寫 Cookie。

const main = function(ctx) {
  const n = Number(ctx.cookies.get('view') || 0) + 1;
  ctx.cookies.set('view', n);
  ctx.response.body = n + ' views';
}
// 訪問 http://127.0.0.1:3000 ,你會看到1 views。刷新一次頁面,就變成了2 views。

表單就是 POST 方法發(fā)送到服務(wù)器的鍵值對。[koa-body]模塊可以用來從 POST 請求的數(shù)據(jù)體里面提取鍵值對。

const koaBody = require('koa-body');
const main = async function(ctx) {
  const body = ctx.request.body;
  if (!body.name) ctx.throw(400, '.name required');
  ctx.body = { name: body.name };
};
app.use(koaBody());

[koa-body]模塊還可以用來處理文件上傳。請看:
https://github.com/ruanyf/jstutorial/blob/gh-pages/nodejs/koa.md 阮一峰教程

級聯(lián)式(Cascading)的結(jié)構(gòu),也就是說,屬于是層層調(diào)用,第一個中間件調(diào)用第二個中間件,第二個調(diào)用第三個,以此類推。上游的中間件必須等到下游的中間件返回結(jié)果,才會繼續(xù)執(zhí)行,這點很像遞歸。

Koa2與Koa1的區(qū)別

簡單來說就是async取代了星號* await取代了yield;
async/await 的特點:
可以讓異步邏輯用同步寫法實現(xiàn)
最底層的await返回需要是Promise對象
可以通過多層 async function 的同步寫法代替?zhèn)鹘y(tǒng)的callback嵌套

Koa2特性:

  • 只提供封裝好http上下文、請求、響應(yīng),以及基于async/await的中間件容器。
  • 利用ES7的async/await的來處理傳統(tǒng)回調(diào)嵌套問題和代替koa@1的generator,但是需要在node.js 7.x的harmony模式下才能支持async/await
  • 中間件只支持 async/await 封裝的,如果要使用koa@1基于generator中間件,需要通過中間件koa-convert封裝一下才能使用。
  • generator 中間件開發(fā)在koa v1和v2中使用
  • async await 中間件開發(fā)和只能在koa v2中使用
const Koa = require('koa')
const app = new Koa()
// 簡單例子:
app.use( async ( ctx ) => {
  let url = ctx.request.url
  ctx.body = url
})
app.listen(3000)

koa-router中間件

npm/cnpm install --save koa-router

const Koa = require('koa')
const fs = require('fs')
const app = new Koa()
const Router = require('koa-router')
let home = new Router()
// 子路由1
home.get('/', async ( ctx )=>{
  let html = `
    <ul>
      <li><a href="/page/helloworld">/page/helloworld</a></li>
      <li><a href="/page/404">/page/404</a></li>
    </ul>
  ctx.body = html
})
// 子路由2
let page = new Router()
page.get('/404', async ( ctx )=>{
  ctx.body = '404 page!'
}).get('/helloworld', async ( ctx )=>{
  ctx.body = 'helloworld page!'
})
// 裝載所有子路由
let router = new Router()
router.use('/', home.routes(), home.allowedMethods())
router.use('/page', page.routes(), page.allowedMethods())
// 加載路由中間件
app.use(router.routes()).use(router.allowedMethods())

app.listen(3000, () => {
  console.log('[demo] route-use-middleware is starting at port 3000')
})

官網(wǎng)技術(shù)說明

Koa 依賴 node v7.6.0 或 ES2015及更高版本和 async 方法支持.
必修的 hello world 應(yīng)用:

const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
  ctx.body = 'Hello World';
});
app.listen(3000);

級聯(lián)通過一系列功能直接傳遞控制,直到一個返回,Koa 調(diào)用“下游”,然后控制流回“上游”。當(dāng)一個中間件調(diào)用 next() 則該函數(shù)暫停并將控制傳遞給定義的下一個中間件。當(dāng)在下游沒有更多的中間件執(zhí)行后,堆棧將展開并且每個中間件恢復(fù)執(zhí)行其上游行為。

app.use(function) 將給定的中間件方法添加到此應(yīng)用程序。
app.keys= 設(shè)置簽名的 Cookie 密鑰。

上下文(Context)

Koa Context 將 node 的 request 和 response 對象封裝到單個對象中,為編寫 Web 應(yīng)用程序和 API 提供了許多有用的方法。 這些操作在 HTTP 服務(wù)器開發(fā)中頻繁使用。

app.use(async ctx => {
  ctx; // 這是 Context
  ctx.request; // 這是 koa Request
  ctx.response; // 這是 koa Response
});

為方便起見許多上下文的訪問器和方法直接委托給它們的 ctx.request或 ctx.response。ctx.type 和 ctx.length 委托給 response 對象,ctx.path 和 ctx.method 委托給 request。

ctx.req // Node 的 request 對象.
ctx.res // Node 的 response 對象.

繞過 Koa 的 response 處理是不被支持的.應(yīng)避免使用以下 node 屬性:
res.statusCode
res.writeHead()
res.write()
res.end()

ctx.request koa 的 Request 對象.
ctx.response koa 的 Response 對象.

ctx.cookies.get(name, [options]) 通過 options 獲取 cookie name:
signed 所請求的cookie應(yīng)該被簽名
ctx.cookies.set(name, value, [options])通過 options 設(shè)置 cookie name 的 value :

  • maxAge 一個數(shù)字表示從 Date.now() 得到的毫秒數(shù)
  • signed cookie 簽名值
  • expires cookie 過期的 Date
  • path cookie 路徑, 默認(rèn)是'/'

request.header 請求標(biāo)頭對象。
request.header = 設(shè)置請求標(biāo)頭對象。

request.method 請求方法。
request.method = 設(shè)置請求方法,對于實現(xiàn)諸如 methodOverride() 的中間件是有用的。

request.url 獲取請求 URL.
request.url = 設(shè)置請求 URL, 對 url 重寫有用。

request.origin 獲取URL的來源,包括 protocol 和 host。
ctx.request.origin // => http://example.com

request.href 獲取完整的請求URL,包括 protocol,host 和 url。
ctx.request.href;// => http://example.com/foo/bar?q=1

request.path 獲取請求路徑名。

request.querystring 根據(jù) ? 獲取原始查詢字符串.

request.type 獲取請求 Content-Type 不含參數(shù) "charset"。
const ct = ctx.request.type;// => "image/png"

request.query 獲取解析的查詢字符串, 當(dāng)沒有查詢字符串時,返回一個空對象。
例如 "color=blue&size=small":

{
  color: 'blue',
  size: 'small'
}

request.accepts(types)
檢查給定的 type(s) 是否可以接受,如果 true,返回最佳匹配,否則為 false。

response.header 響應(yīng)標(biāo)頭對象。

response.status
獲取響應(yīng)狀態(tài)。默認(rèn)情況下,response.status 設(shè)置為 404 而不是像 node 的 res.statusCode 那樣默認(rèn)為 200。

response.message 獲取響應(yīng)的狀態(tài)消息.
response.body 獲取響應(yīng)主體。

response.redirect(url, [alt]) 執(zhí)行 [302] 重定向到 url.

response.type 獲取響應(yīng) Content-Type 不含參數(shù) "charset"。
const ct = ctx.type;// => "image/png"

response.length
以數(shù)字返回響應(yīng)的 Content-Length,或者從ctx.body推導(dǎo)出來,或者undefined。


起步填坑

項目生成器: npm install -g koa-generator
在你的工作目錄下,輸入:koa2 HelloKoa2
成功創(chuàng)建項目后,進(jìn)入項目目錄,并執(zhí)行npm install命令cd HelloKoa2 npm install
啟動項目:npm start

koa聲明說要在v3版本中取消對generator中間件的支持,所以為了長久考慮還是用async語法的好。
如果想要繼續(xù)使用function*語法,可以使用 koa-convert 這個中間件進(jìn)行轉(zhuǎn)換。

const convert = require('koa-convert');
app.use(convert(bodyparser));
app.use(convert(json()));
app.use(convert(logger()));

Context封裝了node中的request和response。
koa@1.x使用this引用Context對象:

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

koa@2.x中使用ctx來訪問Context對象:

app.use(async (ctx, next) => {
  await next();
  ctx.body = 'Hello World';
});

項目配置

這里的配置指的是運行環(huán)境的配置,比如我們在開發(fā)階段使用本地的數(shù)據(jù)庫,測試要使用測試庫,發(fā)布上線時候使用線上的庫,也會有不同的端口號。
npm start 就會運行package.json中scripts對象對應(yīng)的start字段后面的內(nèi)容。

在npm中,有四個常用的縮寫
npm start是npm run start
npm stop是npm run stop的簡寫
npm test是npm run test的簡寫
npm restart是npm run stop && npm run restart && npm run start的簡寫

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容