據說koa比express更傻瓜化,真的?html
Koa 框架教程node
自己代碼只有1000多行,全部功能都經過插件實現,很符合 Unix 哲學。web
搭建簡單服務器express
// demos/01.js const Koa = require('koa'); const app = new Koa(); app.listen(3000);
訪問 http://127.0.0.1:3000,顯示:json
From: nodejs搭建web服務器就是這麼簡單!c#
//引入http模塊 var http = require("http");
//設置主機名 var hostName = '127.0.0.1'; //設置端口 var port = 8080; //建立服務 var server = http.createServer(function(req,res){ res.setHeader('Content-Type','text/plain'); res.end("hello nodejs"); // ----> (1) }); server.listen(port,hostName,function(){ console.log(`服務器運行在http://${hostName}:${port}`); });
However, koa省略的步驟,若是使用時,仍是要補回來的。promise
返回的內容瀏覽器
// demos/02.js const Koa = require('koa'); const app = new Koa(); const main = ctx => { ctx.response.body = 'Hello World'; // ----> (1) }; app.use(main); app.listen(3000);
返回的類型bash
根據用戶想要的類型,返回對應類型的內容。服務器
// demos/03.js const main = ctx => { if (ctx.request.accepts('xml')) { ctx.response.type = 'xml'; // the type that client wants to get. ctx.response.body = '<data>Hello World</data>'; // we will respond what they would like.
} 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'; } };
網頁模板
讓 Koa 先讀取模板文件,而後將這個模板返回給用戶。
const fs = require('fs'); const Koa = require('koa'); const app = new Koa(); const main = ctx => { ctx.response.type = 'html'; ctx.response.body = fs.createReadStream('./demos/template.html'); // 先加載模板 }; app.use(main); app.listen(3000);
路由設置
// demos/05.js 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'; } };
一個路徑 ----> 函數調用
// demos/06.js 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));
指定某文件夾,經過路徑直接得到文件夾內的靜態文件。
const Koa = require('koa'); const app = new Koa(); const path = require('path'); const serve = require('koa-static'); const main = serve(path.join(__dirname)); app.use(main); app.listen(3000);
訪問 http://127.0.0.1:3000/12.js,在瀏覽器裏就能夠看到這個腳本的內容。
重定向(redirect)訪問請求。好比,用戶登錄之後,將他重定向到登錄前的頁面。
以下,訪問 http://127.0.0.1:3000/redirect ,瀏覽器會將用戶導向根路由。
const Koa = require('koa'); const route = require('koa-route'); const app = new Koa(); const redirect = ctx => { ctx.response.redirect('/'); // <---- (1) }; const main = ctx => { ctx.response.body = 'Hello World'; }; app.use(route.get('/', main)); app.use(route.get('/redirect', redirect)); // ----> (1) 如上 app.use(main); app.listen(3000);
中間件 - middleware
處在 HTTP Request 和 HTTP Response 中間,用來實現某種中間功能。
基本上,Koa 全部的功能都是經過中間件實現的。
// demos/08.js const logger = (ctx, next) => { console.log(`${Date.now()} ${ctx.request.method} ${ctx.request.url}`); next(); } app.use(logger);
每一箇中間件默認接受兩個參數:
第一個參數,是 Context 對象;
第二個參數,是next
函數。(只要調用next
函數,就能夠把執行權轉交給下一個中間件)
next函數示例:訪問 http://127.0.0.1:3000 ,命令行窗口會有以下輸出。
const Koa = require('koa'); const app = new Koa(); const one = (ctx, next) => { console.log('>> one'); // step 1 next(); console.log('<< one'); // step 6 } const two = (ctx, next) => { console.log('>> two'); // step 2 next(); console.log('<< two'); // step 5 } const three = (ctx, next) => { console.log('>> three'); // step 3 next(); console.log('<< three'); // step 4 }
-------------------------------------------- app.use(one); // <----導入中間件 app.use(two); app.use(three);
異步中間件
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);
Goto: [JS] ECMAScript 6 - Async : compare with c#
中間件的合併
// demos/11.js const compose = require('koa-compose'); const logger = (ctx, next) => { console.log(`${Date.now()} ${ctx.request.method} ${ctx.request.url}`); next(); } const main = ctx => { ctx.response.body = 'Hello World'; }; const middlewares = compose([logger, main]); app.use(middlewares);
暫時用不到,何時才能發揮其優點,不清楚。
錯誤處理
500 錯誤
// demos/14.js const main = ctx => { ctx.throw(500); };
訪問 http://127.0.0.1:3000,你會看到一個500錯誤頁"Internal Server Error"。
404錯誤
// demos/15.js const main = ctx => { ctx.response.status = 404; ctx.response.body = 'Page Not Found'; };
訪問 http://127.0.0.1:3000 ,你就看到一個404頁面"Page Not Found"。
讓最外層的中間件,負責全部中間件的錯誤處理。
const Koa = require('koa'); const app = new Koa(); const handler = async (ctx, next) => { try { await next(); } 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); // 處於最外層 app.listen(3000);
// demos/17.js const main = ctx => { ctx.throw(500); }; app.on('error', (err, ctx) => console.error('server error', err); // 觸發監聽事件 );
若是錯誤被try...catch
捕獲,就不會觸發error
事件
// demos/18.js` const handler = async (ctx, next) => { try { await next(); } catch (err) { // step 2, 截獲錯誤 ctx.response.status = err.statusCode || err.status || 500; ctx.response.type = 'html'; ctx.response.body = '<p>Something wrong, please contact administrator.</p>'; ctx.app.emit('error', err, ctx); // step 3, 補發一個新錯誤信號 } }; const main = ctx => { ctx.throw(500); // step 1, 拋出錯誤 }; app.on('error', function(err) { // step 4, 監聽到錯誤,執行「錯誤處理」 console.log('logging error ', err.message); console.log(err); });
Web App 的功能
// demos/19.js 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
。再刷新,每次都會計數增長1。
表單
表單就是 POST 方法發送到服務器的鍵值對。
const Koa = require('koa');
const koaBody = require('koa-body'); const app = new Koa(); 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()); app.use(main); app.listen(3000);
打開另外一個命令行窗口,運行下面的命令。
$ curl -X POST --data "name=Jack" 127.0.0.1:3000 {"name":"Jack"} $ curl -X POST --data "name" 127.0.0.1:3000 name required
上面代碼使用 POST 方法向服務器發送一個鍵值對,會被正確解析。若是發送的數據不正確,就會收到錯誤提示。
const os = require('os'); const path = require('path'); const Koa = require('koa'); const fs = require('fs'); const koaBody = require('koa-body'); const app = new Koa(); const main = async function(ctx) { const tmpdir = os.tmpdir(); const filePaths = []; const files = ctx.request.body.files || {}; for (let key in files) { const file = files[key]; const filePath = path.join(tmpdir, file.name); const reader = fs.createReadStream(file.path); const writer = fs.createWriteStream(filePath); reader.pipe(writer); filePaths.push(filePath); } ctx.body = filePaths; }; app.use(koaBody({ multipart: true })); app.use(main); app.listen(3000);
打開另外一個命令行窗口,運行下面的命令,上傳一個文件。注意,/path/to/file
要更換爲真實的文件路徑。
$ curl --form upload=@/path/to/file http://127.0.0.1:3000 ["/tmp/file"]