安裝建立項目:javascript
1.必定要全局安裝(koa1.2和koa2都己經支持)java
npm install koa-generator -g
2.
koa1 生成一個test項目,切到test目錄並下載依賴node
koa1建立項目mysql
koa test
cd test
npm install
運行:npm start
訪問:http://localhost:3000
Koa是一個相似於Express的Web開發框架,創始人也是同一我的。它的主要特色是,使用了ES6的Generator函數,進行了架構的從新設計。也就是說,Koa的原理和內部結構很像Express,可是語法和內部結構進行了升級。git
官方faq有這樣一個問題:」爲何koa不是Express 4.0?「,回答是這樣的:」Koa與Express有很大差別,整個設計都是不一樣的,因此若是將Express 3.0按照這種寫法升級到4.0,就意味着重寫整個程序。因此,咱們以爲創造一個新的庫,是更合適的作法。「github
一個Koa應用就是一個對象,包含了一個middleware數組,這個數組由一組Generator函數組成。這些函數負責對HTTP請求進行各類加工,好比生成緩存、指定代理、請求重定向等等。sql
1 var koa = require('koa'); 2 var app = koa(); 3 4 app.use(function *(){ 5 this.body = 'Hello World'; 6 }); 7 8 app.listen(3000);
要安裝koa才能測試
上面代碼中,變量app就是一個Koa應用。它監聽3000端口,返回一個內容爲Hello World的網頁。express
app.use方法用於向middleware數組添加Generator函數。npm
listen方法指定監聽端口,並啓動當前應用。它實際上等同於下面的代碼數組
1 var http = require('http'); 2 var koa = require('koa'); 3 var app = koa(); 4 http.createServer(app.callback()).listen(3000);
下面是一個兩個中間件級聯的例子
1 app.use(function *() { 2 this.body = "header\n"; 3 yield saveResults.call(this); 4 this.body += "footer\n"; 5 }); 6 7 function *saveResults() { 8 this.body += "Results Saved!\n"; 9 }
上面代碼中,第一個中間件調用第二個中間件saveResults,它們都向this.body
寫入內容。最後,this.body
的輸出以下。
1 header 2 Results Saved! 3 footer
只要有一箇中間件缺乏yield next
語句,後面的中間件都不會執行,這一點要引發注意。
若是想跳過一箇中間件,能夠直接在該中間件的第一行語句寫上return yield next
。
1 app.use(function* (next) { 2 if (skip) return yield next; 3 })
能夠經過this.path
屬性,判斷用戶請求的路徑,從而起到路由做用。
1 app.use(function* (next) { 2 if (this.path === '/') { 3 this.body = 'we are at home!'; 4 } else { 5 yield next; 6 } 7 }) 8 9 // 等同於 10 11 app.use(function* (next) { 12 if (this.path !== '/') return yield next; 13 this.body = 'we are at home!'; 14 })
下面是多路徑的例子。
1 let koa = require('koa') 2 3 let app = koa() 4 5 // normal route 6 app.use(function* (next) { 7 if (this.path !== '/') { 8 return yield next 9 } 10 11 this.body = 'hello world' 12 }); 13 14 // /404 route 15 app.use(function* (next) { 16 if (this.path !== '/404') { 17 return yield next; 18 } 19 20 this.body = 'page not found' 21 }); 22 23 // /500 route 24 app.use(function* (next) { 25 if (this.path !== '/500') { 26 return yield next; 27 } 28 29 this.body = 'internal server error' 30 }); 31 32 app.listen(8080)
上面代碼中,每個中間件負責一個路徑,若是路徑不符合,就傳遞給下一個中間件。
複雜的路由須要安裝koa-router插件。
1 var app = require('koa')(); 2 var Router = require('koa-router'); 3 4 var myRouter = new Router(); 5 6 myRouter.get('/', function *(next) { 7 this.response.body = 'Hello World!'; 8 }); 9 10 app.use(myRouter.routes()); 11 12 app.listen(3000);
上面代碼對根路徑設置路由。
Koa-router實例提供一系列動詞方法,即一種HTTP動詞對應一種方法。典型的動詞方法有如下五種。
這些動詞方法能夠接受兩個參數,第一個是路徑模式,第二個是對應的控制器方法(中間件),定義用戶請求該路徑時服務器行爲。
1 router.get('/', function *(next) { 2 this.body = 'Hello World!'; 3 });
上面代碼中,router.get
方法的第一個參數是根路徑,第二個參數是對應的函數方法。
注意,路徑匹配的時候,不會把查詢字符串考慮在內。好比,/index?param=xyz
匹配路徑/index
。
有些路徑模式比較複雜,Koa-router容許爲路徑模式起別名。起名時,別名要添加爲動詞方法的第一個參數,這時動詞方法變成接受三個參數。
1 router.get('user', '/users/:id', function *(next) { 2 // ... 3 });
上面代碼中,路徑模式\users\:id的名字就是user。路徑的名稱,能夠用來引用對應的具體路徑,好比url方法能夠根據路徑名稱,
結合給定的參數,生成具體的路徑。
Koa-router容許爲路徑統一添加前綴。
1 var router = new Router({ 2 prefix: '/users' 3 }); 4 5 router.get('/', ...); // 等同於"/users" 6 router.get('/:id', ...); // 等同於"/users/:id"
路徑的參數經過this.params
屬性獲取,該屬性返回一個對象,全部路徑參數都是該對象的成員。
// 訪問 /programming/how-to-node router.get('/:category/:title', function *(next) { console.log(this.params); // => { category: 'programming', title: 'how-to-node' } });
param方法能夠針對命名參數,設置驗證條件。
1 router 2 .get('/users/:user', function *(next) { 3 this.body = this.user; 4 }) 5 .param('user', function *(id, next) { 6 var users = [ '0號用戶', '1號用戶', '2號用戶']; 7 this.user = users[id]; 8 if (!this.user) return this.status = 404; 9 yield next; 10 })
上面代碼中,若是/users/:user
的參數user對應的不是有效用戶(好比訪問/users/3
),param方法註冊的中間件會查到,就會返回404錯誤。
redirect方法會將某個路徑的請求,重定向到另外一個路徑,並返回301狀態碼。
1 router.redirect('/login', 'sign-in'); 2 3 // 等同於 4 router.all('/login', function *() { 5 this.redirect('/sign-in'); 6 this.status = 301; 7 });
redirect方法的第一個參數是請求來源,第二個參數是目的地,二者均可以用路徑模式的別名代替。
1 app.use(function *() { 2 try { 3 yield saveResults(); 4 } catch (err) { 5 this.throw(400, '數據無效'); 6 } 7 });
上面代碼自行部署了try…catch代碼塊,一旦產生錯誤,就用this.throw
方法拋出。該方法能夠將指定的狀態碼和錯誤信息,返回給客戶端。
對於未捕獲錯誤,能夠設置error事件的監聽函數。
1 app.on('error', function(err){ 2 log.error('server error', err); 3 });
this.throw方法用於向客戶端拋出一個錯誤。
1 this.throw(403); 2 this.throw('name required', 400); 3 this.throw(400, 'name required'); 4 this.throw('something exploded'); 5 6 this.throw('name required', 400) 7 // 等同於 8 var err = new Error('name required'); 9 err.status = 400; 10 throw err;
this.throw
方法的兩個參數,一個是錯誤碼,另外一個是報錯信息。若是省略狀態碼,默認是500錯誤。
this.assert
方法用於在中間件之中斷言,用法相似於Node的assert模塊
1 this.assert(this.user, 401, 'User not found. Please login!');
上面代碼中,若是this.user屬性不存在,會拋出一個401錯誤。
cookie的讀取和設置。
1 this.cookies.get('view'); 2 this.cookies.set('view', n);
get和set方法均可以接受第三個參數,表示配置參數。其中的signed參數,用於指定cookie是否加密。若是指定加密的話,必須用app.keys
指定加密短語。
1 app.keys = ['secret1', 'secret2']; 2 this.cookies.set('name', '張三', { signed: true });
this.cookie的配置對象的屬性以下。
1 var session = require('koa-session'); 2 var koa = require('koa'); 3 var app = koa(); 4 5 app.keys = ['some secret hurr']; 6 app.use(session(app)); 7 8 app.use(function *(){ 9 var n = this.session.views || 0; 10 this.session.views = ++n; 11 this.body = n + ' views'; 12 }) 13 14 app.listen(3000); 15 console.log('listening on port 3000');
1 能夠把session存到mysql中 2 安裝npm install koa-generic-session --save-dev 3 2.app.js中 4 var session = require('koa-generic-session'); 5 6 app.keys = ['my secret key']; // needed for cookie-signing,設置一個簽名 Cookie 的密鑰 7 app.use(session()); 8 9 3. 10 this.session.loginbean 11 12 方法二: 13 session映射到mysql 14 1.加安裝 15 npm install mysql --save-dev 16 npm install koa-mysql-session --save-dev 17 18 app.js中: 19 var session = require('koa-generic-session'); 20 const mysql = require('mysql'); 21 const MysqlStore = require('koa-mysql-session'); 22 23 app.keys = ['my secret key']; // needed for cookie-signing,設置一個簽名 Cookie 的密鑰 24 app.use(session({store:new MysqlStore({ 25 host: 'localhost', //主機 26 user: 'root', //MySQL認證用戶名 27 password: 'root', //MySQL認證用戶密碼 28 database: 'kameng', 29 port: '3306', //端口號 30 acquireTimeout:0 31 })}));
Request對象表示HTTP請求。
(1)this.request.header
返回一個對象,包含全部HTTP請求的頭信息。它也能夠寫成this.request.headers
。
(2)this.request.method
返回HTTP請求的方法,該屬性可讀寫。
(3)this.request.length
返回HTTP請求的Content-Length屬性,取不到值,則返回undefined。
(4)this.request.path
返回HTTP請求的路徑,該屬性可讀寫。
(5)this.request.href
返回HTTP請求的完整路徑,包括協議、端口和url。
1 this.request.href 2 // http://example.com/foo/bar?q=1
(6)this.request.querystring
返回HTTP請求的查詢字符串,不含問號。該屬性可讀寫。
(7)this.request.search
返回HTTP請求的查詢字符串,含問號。該屬性可讀寫。
(8)this.request.host
返回HTTP請求的主機(含端口號)。
(9)this.request.hostname
返回HTTP的主機名(不含端口號)。
(10)this.request.type
返回HTTP請求的Content-Type屬性
1 var ct = this.request.type; 2 // "image/png"
(11)this.request.charset
返回HTTP請求的字符集。
1 this.request.charset 2 // "utf-8
能夠經過this.path
屬性,判斷用戶請求的路徑,從而起到路由做用。
app.use(function* (next) { if (this.path === '/') { this.body = 'we are at home!'; } else { yield next; } }) // 等同於 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)
上面代碼中,每個中間件負責一個路徑,若是路徑不符合,就傳遞給下一個中間件。
複雜的路由須要安裝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);
上面代碼對根路徑設置路由。
Koa-router實例提供一系列動詞方法,即一種HTTP動詞對應一種方法。典型的動詞方法有如下五種。
這些動詞方法能夠接受兩個參數,第一個是路徑模式,第二個是對應的控制器方法(中間件),定義用戶請求該路徑時服務器行爲。
router.get('/', function *(next) { this.body = 'Hello World!'; });
上面代碼中,router.get
方法的第一個參數是根路徑,第二個參數是對應的函數方法。
注意,路徑匹配的時候,不會把查詢字符串考慮在內。好比,/index?param=xyz
匹配路徑/index
。
有些路徑模式比較複雜,Koa-router容許爲路徑模式起別名。起名時,別名要添加爲動詞方法的第一個參數,這時動詞方法變成接受三個參數。
router.get('user', '/users/:id', function *(next) { // ... });
上面代碼中,路徑模式\users\:id
的名字就是user
。路徑的名稱,能夠用來引用對應的具體路徑,好比url方法能夠根據路徑名稱,結合給定的參數,生成具體的路徑。
router.url('user', 3); // => "/users/3" router.url('user', { id: 3 }); // => "/users/3"
上面代碼中,user就是路徑模式的名稱,對應具體路徑/users/:id
。url方法的第二個參數3,表示給定id的值是3,所以最後生成的路徑是/users/3
。
Koa-router容許爲路徑統一添加前綴。
var router = new Router({ prefix: '/users' }); router.get('/', ...); // 等同於"/users" router.get('/:id', ...); // 等同於"/users/:id"
路徑的參數經過this.params
屬性獲取,該屬性返回一個對象,全部路徑參數都是該對象的成員。
// 訪問 /programming/how-to-node router.get('/:category/:title', function *(next) { console.log(this.params); // => { category: 'programming', title: 'how-to-node' } });
param方法能夠針對命名參數,設置驗證條件。
router .get('/users/:user', function *(next) { this.body = this.user; }) .param('user', function *(id, next) { var users = [ '0號用戶', '1號用戶', '2號用戶']; this.user = users[id]; if (!this.user) return this.status = 404; yield next; })
上面代碼中,若是/users/:user
的參數user對應的不是有效用戶(好比訪問/users/3
),param方法註冊的中間件會查到,就會返回404錯誤。
redirect方法會將某個路徑的請求,重定向到另外一個路徑,並返回301狀態碼。
router.redirect('/login', 'sign-in'); // 等同於 router.all('/login', function *() { this.redirect('/sign-in'); this.status = 301; });
redirect方法的第一個參數是請求來源,第二個參數是目的地,二者均可以用路徑模式的別名代替。
中間件當中的this表示上下文對象context,表明一次HTTP請求和迴應,即一次訪問/迴應的全部信息,均可以從上下文對象得到。context對象封裝了request和response對象,而且提供了一些輔助方法。每次HTTP請求,就會建立一個新的context對象。
app.use(function *(){ this; // is the Context this.request; // is a koa Request this.response; // is a koa Response });
context對象的不少方法,實際上是定義在ctx.request對象或ctx.response對象上面,好比,ctx.type和ctx.length對應於ctx.response.type和ctx.response.length,ctx.path和ctx.method對應於ctx.request.path和ctx.request.method。
context對象的全局屬性。
this.state.user = yield User.find(id);
上面代碼中,user屬性存放在this.state
對象上面,能夠被另外一箇中間件讀取。
context對象的全局方法。
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;
assert方法的例子。
// 格式 ctx.assert(value, [msg], [status], [properties]) // 例子 this.assert(this.user, 401, 'User not found. Please login!');
如下模塊解析POST請求的數據。
var parse = require('co-body'); // in Koa handler var body = yield parse(this);
Koa提供內置的錯誤處理機制,任何中間件拋出的錯誤都會被捕捉到,引起向客戶端返回一個500錯誤,而不會致使進程中止,所以也就不須要forever這樣的模塊重啓進程。
app.use(function *() { throw new Error(); });
上面代碼中,中間件內部拋出一個錯誤,並不會致使Koa應用掛掉。Koa內置的錯誤處理機制,會捕捉到這個錯誤。
固然,也能夠額外部署本身的錯誤處理機制。
app.use(function *() { try { yield saveResults(); } catch (err) { this.throw(400, '數據無效'); } });
上面代碼自行部署了try…catch代碼塊,一旦產生錯誤,就用this.throw
方法拋出。該方法能夠將指定的狀態碼和錯誤信息,返回給客戶端。
對於未捕獲錯誤,能夠設置error事件的監聽函數。
app.on('error', function(err){ log.error('server error', err); });
error事件的監聽函數還能夠接受上下文對象,做爲第二個參數。
app.on('error', function(err, ctx){ log.error('server error', err, ctx); });
若是一個錯誤沒有被捕獲,koa會向客戶端返回一個500錯誤「Internal Server Error」。
this.throw方法用於向客戶端拋出一個錯誤。
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
方法的兩個參數,一個是錯誤碼,另外一個是報錯信息。若是省略狀態碼,默認是500錯誤。
this.assert
方法用於在中間件之中斷言,用法相似於Node的assert模塊。
this.assert(this.user, 401, 'User not found. Please login!');
上面代碼中,若是this.user屬性不存在,會拋出一個401錯誤。
因爲中間件是層級式調用,因此能夠把try { yield next }
當成第一個中間件。
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'); })
CSRF攻擊是指用戶的session被劫持,用來冒充用戶的攻擊。
koa-csrf插件用來防止CSRF攻擊。原理是在session之中寫入一個祕密的token,用戶每次使用POST方法提交數據的時候,必須含有這個token,不然就會拋出錯誤。
1 var koa = require('koa'); 2 var session = require('koa-session'); 3 var csrf = require('koa-csrf'); 4 var route = require('koa-route'); 5 6 var app = module.exports = koa(); 7 8 app.keys = ['session key', 'csrf example']; 9 app.use(session(app)); 10 11 app.use(csrf()); 12 13 app.use(route.get('/token', token)); 14 app.use(route.post('/post', post)); 15 16 function* token () { 17 this.body = this.csrf; 18 } 19 20 function* post() { 21 this.body = {ok: true}; 22 } 23 24 app.listen(3000);
POST請求含有token,能夠是如下幾種方式之一,koa-csrf插件就能得到token。