Koa

參考資料

Koa

環(huán)境部署

下載安裝NodeJS

下載地址: http://nodejs.cn/download/

$ node -v
v10.15.0

$ npm -v
6.4.1

Koa2

官網(wǎng):https://koa.bootcss.com/

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

Koa2相比較Koa1最大區(qū)別在于中間件的寫(xiě)法,Koa1使用Generator,Koa2使用async/await語(yǔ)法,因此Node.js必須大于v7.6.0+。

安裝

Koa依賴(lài)Node v7.6.0 或ES2015+ 和 async方法支持。

創(chuàng)建工程文件夾并進(jìn)入,使用npm初始化項(xiàng)目。

# 手工創(chuàng)建項(xiàng)目
$ mkdir project && cd  project

# 快速生成package.json文件
$ npm init -y

$ vim package.json
{
  "name": "project",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start":"node app.js" // 添加快捷啟動(dòng)命令
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
  }
}

運(yùn)行腳本

可直接使用node app.js運(yùn)行腳本,也可以使用它的快捷方式npm start運(yùn)行app.js腳本。

$ node app.js
$ npm start

安裝Koa

$ npm install koa -S
$ npm i koa

應(yīng)用程序

Koa應(yīng)用程序是一個(gè)包含一組中間件函數(shù)的對(duì)象,它按照類(lèi)似堆棧的方式組織和執(zhí)行。Koa類(lèi)似中間件系統(tǒng)。

$ vim app.js

導(dǎo)入Koa,在Koa2中由于導(dǎo)入的是一個(gè)類(lèi),所以使用大寫(xiě)的Koa。

const Koa = require("koa");

創(chuàng)建Koa對(duì)象,即Web應(yīng)用本身app

const app = new Koa();

對(duì)于任何HTTP請(qǐng)求,app將調(diào)用異步函數(shù)進(jìn)行處理。

app.use(async (ctx, next)=>{
  await next();
  // 設(shè)置HTTP響應(yīng)的內(nèi)容類(lèi)型
  ctx.response.type = "text/html";
  // 設(shè)置HTTP響應(yīng)的內(nèi)容
  ctx.response.body = "hello world";
});

每當(dāng)Koa收到一個(gè)HTTP請(qǐng)求時(shí),會(huì)調(diào)用通過(guò)app.use()注冊(cè)的async異步函數(shù)并傳入ctxnext參數(shù)。可以針對(duì)ctx進(jìn)行操作并設(shè)置返回內(nèi)容。

async標(biāo)記的函數(shù)稱(chēng)為異步函數(shù),使用await調(diào)用另一個(gè)異步函數(shù),async/await關(guān)鍵字是在ES7中引入的。

ctx是Koa提供的Context對(duì)象,封裝了requestresponse對(duì)象,每次HTTP Request都會(huì)創(chuàng)建一個(gè)Context上下文對(duì)象。

ctx是由Koa傳入的封裝了requestresponse的變量,可通過(guò)ctx.requestctx.response訪(fǎng)問(wèn)請(qǐng)求和響應(yīng)。

中間件

next是Koa傳入將要處理的下一個(gè)異步函數(shù),await next();表示處理下一個(gè)異步函數(shù),然后再設(shè)置response響應(yīng)的Content-Typebody內(nèi)容。

await next();有什么作用呢?

Koa將多個(gè)async異步函數(shù)組成了一個(gè)處理鏈,每個(gè)async異步函數(shù)都可以做自己的事情,然后使用await next()來(lái)調(diào)用下一個(gè)async異步函數(shù)。這里將每個(gè)async異步函數(shù)成為中間件middleware,這些中間件可以組合起來(lái)完成很多實(shí)用的功能。

監(jiān)聽(tīng)3000端口

app.listen(3000);

瀏覽器訪(fǎng)問(wèn):http://127.0.0.1:3000

JS異步發(fā)展史

  • ES5:回調(diào)函數(shù)callback
  • ES6:Promise對(duì)象和Generator函數(shù)
  • ES7:async/await語(yǔ)法

async/await

async用于聲明一個(gè)函數(shù)是異步的,await只能出現(xiàn)在async修改的函數(shù)中。async函數(shù)會(huì)返回一個(gè)promise對(duì)象,如果在async函數(shù)返回一個(gè)直接量它最終將通過(guò)Promise.resolve封裝成promise對(duì)象,進(jìn)而可以直接通過(guò)promise對(duì)象的then方法來(lái)獲取這個(gè)直接量。

await會(huì)暫停當(dāng)前async的執(zhí)行,await會(huì)阻塞代碼的執(zhí)行直到await后的表達(dá)式處理完畢,代碼才繼續(xù)往下執(zhí)行。await后的表達(dá)式可以是一個(gè)promise對(duì)象也可以任何需要等待的值。如果await等到的是一個(gè)promise對(duì)象,await就會(huì)阻塞后續(xù)代碼直到promise對(duì)象resolve后得到resolve的值作為await表達(dá)式的運(yùn)算結(jié)果。

async/await只是一種語(yǔ)法糖,其代碼執(zhí)行與回調(diào)函數(shù)嵌套是本質(zhì)上是沒(méi)有區(qū)別的,只是寫(xiě)法上讓人以同步的思維去思考,避開(kāi)回調(diào)地獄。簡(jiǎn)而言之,async/await是以同步的思維去編寫(xiě)異步的代碼,所以async/await并不會(huì)影響Node.js的并發(fā)數(shù)。

中間件

中間件類(lèi)似于過(guò)濾器,用于在客戶(hù)端和應(yīng)用程序之間處理請(qǐng)求和響應(yīng)的方法。中間件的執(zhí)行類(lèi)似剝洋蔥,但并非一層層的執(zhí)行,而是以next為分界,先執(zhí)行本層next之前的部分,當(dāng)下一層中間件執(zhí)行完畢后再執(zhí)行本層next之后的部分。

中間件

中間件是為了解決復(fù)雜應(yīng)用中頻繁回調(diào)而設(shè)計(jì)的級(jí)聯(lián)式代碼,它并不直接將控制權(quán)完全交給下一個(gè)中間件,而是碰到next后去下一個(gè)中間件,等到下面都執(zhí)行完畢后在執(zhí)行next后續(xù)代碼。

例如:使用三個(gè)中間件組成處理鏈,依次打印日志,記錄處理時(shí)間,輸出HTML。

//中間件處理鏈
app.use(async (ctx, next) => {
    console.log(`${ctx.request.method} ${ctx.request.url}`);
    await next();//調(diào)用下一個(gè)中間件
});
app.use(async (ctx, next) => {
    const begin_time = new Date().getTime();
    await next();//處理下一個(gè)中間件
    const spend_ms = new Date().getTime() - begin_time;//耗費(fèi)時(shí)間
    console.log(`Time: ${spend_ms}ms`);
});
app.use(async (ctx, next) => {
    await next();
    ctx.response.type = "text/html";
    ctx.response.body = "request received";
});

注意中間件的順序也就是調(diào)用app.use()的順序?qū)Q定了中間件的執(zhí)行順序。如果某個(gè)中間件沒(méi)有調(diào)用await next()則后續(xù)中間件將不再執(zhí)行。

例如:檢測(cè)用戶(hù)權(quán)限的中間件可以決定是否繼續(xù)處理請(qǐng)求,若不則直接返回403錯(cuò)誤。

app.use(async (ctx, next) => {
    if(await checkAuth(ctx)){
        await next();
    }else{
        ctx.response.status = 403;
    }
});

簡(jiǎn)單來(lái)說(shuō),理解了中間件也就學(xué)會(huì)使用Koa了。

獲取GET請(qǐng)求數(shù)據(jù)

Koa2中GET請(qǐng)求可通過(guò)ctx.request接收,接收方式分為兩種:

  • ctx.request.query 獲取格式化后的參數(shù)對(duì)象
  • ctx.request.querystring 獲取請(qǐng)求字符串
  • ctx.query
  • ctx.querystring

例如:

app.use(async (ctx, next) => {
    const url = ctx.url;
    //使用ctx.request接收
    const rq = ctx.request;
    const rq_query = rq.query;
    const rq_qs = rq.querystring;
    //使用ctx接收
    const ctx_query = ctx.query;
    const ctx_qs = ctx.querystring;

    //返回
    body = {};
    body.rq_query = rq_query;
    body.rq_qs = rq_qs;
    body.ctx_query = ctx_query;
    body.ctx_qs = ctx_qs;
    ctx.body = body;
});

獲取POST請(qǐng)求數(shù)據(jù)

Koa2中沒(méi)有對(duì)POST請(qǐng)求的處理封裝獲取參數(shù)的方法,需要通過(guò)解析上下文ctx中的元素來(lái)獲取,具體可分為三步:

  1. 解析上下文ctx對(duì)象中的原生Node.js對(duì)象req
  2. 將POST表單數(shù)據(jù)解析為querystring查詢(xún)字符串
  3. 將查詢(xún)字符串轉(zhuǎn)換為JSON格式
const Koa = require("koa");
const app = new Koa();

const parseQueryString = (querystring) => {
    const queryData = {};
    const qsList = querystring.split("&");
    for(let [index, qs] of qsList.entries()){
        let kvList = qs.split("=");
        queryData[kvList[0]] = decodeURIComponent(kvList[1]);
    }
    return queryData;
};

const parsePostData = (ctx) => {
    return new Promise((resolve, reject) => {
        try{
            let postData = "";
            ctx.req.on("data", (data)=>{
                postData += data;
            })
            ctx.req.addListener("end", ()=>{
                let parseData = parseQueryString(postData);
                resolve(parseData);
            });
        }catch(error){
            reject(error);
        }
    });
};

app.use(async(ctx, next) => {
    if(ctx.method === "POST"){
        let postData = await parsePostData(ctx);
        ctx.body = postData;
    }
});

app.listen(3000, ()=>{
    console.log("server is running");
});

另一種方式是使用koa-bodyparser中間件,對(duì)于POST請(qǐng)求的處理,koa-bodyparser中間件可以將Koa上下文中的formData數(shù)據(jù)解析到ctx.request.body中。

koa-bodyparser

安裝

$ npm i koa-bodyparser -S

使用

const Koa = require("koa");
const app = new Koa();

const bodyParser = require("koa-bodyparser");
app.use(bodyParser());

app.use(async(ctx, next) => {
    if(ctx.method === "POST"){
        ctx.body = ctx.request.body;
    }
});

app.listen(3000, ()=>{
    console.log("server is running");
});

supervisor

使用supervisor自動(dòng)重啟模塊

$ npm i supervisor -g
# 修改package.json,設(shè)置scripts選項(xiàng)下的start。
"scripts":
{
    "test": "echo \"Error: no test specified\" && exit 1",
    "start":"supervisor app.js" //設(shè)置npm start啟動(dòng)項(xiàng)
}

koa-router

koa-router 路由組件

$ npm i koa-router -S
var Koa = require("koa");
var app = new Koa();
var router = require("koa-router")();

router.get("/", (ctx, next) => {
  console.log("homepage");
});

app.use(router.routes()).use(router.allowedMethods());

參考資料

koa-router是Koa的路由組件,路由的功能是指定具體的訪(fǎng)問(wèn)路徑,通過(guò)訪(fǎng)問(wèn)路徑指向特定的功能模塊。

安裝

# 安裝Koa
$ npm install koa --save

# 安裝koa-router
$ npm install koa-router --save

使用

var Koa = require("koa");
var Router = require("koa-router");

var app = new Koa();
var router = new Router();

app.use(router.routes()).use(router.allowedMethods());

router.get("/", (ctx, next)=>{

});

koa-simple-router

路由koa-simple-router

$ npm i koa-simple-router -S

koa-cors 跨域組件

安裝

$ npm install --save koa2-cors

使用

var Koa = require('koa');
var cors = require('koa2-cors');

var app = new Koa();
app.use(cors());

koa-static

koa-static為原生koa2實(shí)現(xiàn)靜態(tài)資源服務(wù)器

一個(gè)HTTP請(qǐng)求訪(fǎng)問(wèn)Web服務(wù)靜態(tài)資源,一般響應(yīng)結(jié)果有3種情況:

  • 訪(fǎng)問(wèn)文本,如css、js、image...
  • 訪(fǎng)問(wèn)靜態(tài)目錄
  • 找不到資源則拋出404錯(cuò)誤

為Koa2實(shí)現(xiàn)靜態(tài)資源加載的服務(wù),用于訪(fǎng)問(wèn)css/js/img等靜態(tài)資源,若訪(fǎng)問(wèn)圖片則返回二進(jìn)制,其他文件則直接輸出字符串。

$ npm i koa-static -S
# 設(shè)置static目錄用于存儲(chǔ)靜態(tài)資源,app.js添加中間件。
app.use(require("koa-static")(__dirname + "/static"));

koa-views

koa-views為Koa2加載的模板引擎

$  npm i koa-views -S

根目錄下創(chuàng)建views目錄用于存儲(chǔ)視圖模板文件,并在app.js文件中添加中間件配置。

//模板引擎
app.use(require("koa-views")(__dirname+"/views"));

koa-logger

//日志配置
app.use(require("koa-logger")());

koa-bodyparser

$ npm i koa-bodyparser@2 -S
app.use(require("koa-bodyparser")())

koa-session

$ npm i koa-session -S

mongoose

https://mongoosejs.com

$ npm i mongoose -S

config


$ npm i config -S

mysql

mysql模塊是node操作MySQL的引擎,可在node.js環(huán)境下對(duì)MySQL數(shù)據(jù)庫(kù)進(jìn)行CURD等操作。

$ npm i mysql -S
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容