[React] 06 - Route: koa makes your life easier

據說koa比express更傻瓜化,真的?html

 

Koa 框架教程node

自己代碼只有1000多行,全部功能都經過插件實現,很符合 Unix 哲學。web

 

搭建簡單服務器express

  • Koa, 架設一個簡單的服務器
// demos/01.js
const Koa = require('koa');
const app = new Koa();

app.listen(3000);

訪問 http://127.0.0.1:3000,顯示:json

 

  • Express呢?

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

 

  • koa-route 模塊

一個路徑 ----> 函數調用

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

 

Ref: Javascript中的async await

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

 

  • error 事件的監聽
// 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 的功能

  • Cookies
// 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"]
相關文章
相關標籤/搜索