使用koa:
koa2是一個類,所以引入koa后,要創建實例化“對象”,才能使用koa內部封裝的方法。
設置監聽端口:
處理http請求:
1、http請求處理鏈
A、通過app.use()
注冊async異步函數
B、每收到一個http請求,koa就會調用通過app.use()
注冊的async異步函數,並傳入ctx
和next
參數。
2、koa使用異步方法處理請求
async:異步函數
ctx:是由koa傳入的封裝了request和response的變量
3、next()
用await next()
來調用下一個async函數
4、middleware
我們把每個async函數稱為middleware
針對不同的url申請調用不同的異步函數:
底層的處理方式,是通過if判斷請求的地址來進行區分的
使用if判斷非常麻煩,使用koa-router省略這一個步驟
koa-router只能省略掉if判斷的部分,最后要使用app.use()去調用koa-router這個異步函數
同理可以處理post請求
用post請求時,會遇到一個問題:post請求通常會發送一個表單,或者JSON,它作為request的body發送
但無論是Node.js提供的原始request對象,還是koa提供的request對象,都不提供解析request的body的功能!
引入另一個middleware ——koa-bodyparser解析request的body
用var name = ctx.request.body.name || ''
拿到表單的name
字段,如果該字段不存在,默認值設置為''
隨着需要處理的url越來越多,app.js會顯得特別的臃腫
在controller文件中集中描寫url:
app.js不再放置url的異步函數處理方式
app.js只用於將所有controller文件夾中的url異步函數,引入,並使用app.use為其注冊
引入+注冊:就兩個函數封裝這一些功能
進一步簡化app.js
將處理引入與注冊url異步函數的方法,作為一個
Controller Middleware
進一步封裝到controller.js中,去處理controller文件夾中的url異步函數
const fs = require('fs'); // add url-route in /controllers: function addMapping(router, mapping) { for (var url in mapping) { if (url.startsWith('GET ')) { var path = url.substring(4); router.get(path, mapping[url]); console.log(`register URL mapping: GET ${path}`); } else if (url.startsWith('POST ')) { var path = url.substring(5); router.post(path, mapping[url]); console.log(`register URL mapping: POST ${path}`); } else if (url.startsWith('PUT ')) { var path = url.substring(4); router.put(path, mapping[url]); console.log(`register URL mapping: PUT ${path}`); } else if (url.startsWith('DELETE ')) { var path = url.substring(7); router.del(path, mapping[url]); console.log(`register URL mapping: DELETE ${path}`); } else { console.log(`invalid URL: ${url}`); } } } function addControllers(router, dir) { fs.readdirSync(__dirname + '/' + dir).filter((f) => { return f.endsWith('.js'); }).forEach((f) => { console.log(`process controller: ${f}...`); let mapping = require(__dirname + '/' + dir + '/' + f); addMapping(router, mapping); }); } module.exports = function (dir) { let controllers_dir = dir || 'controllers', router = require('koa-router')(); addControllers(router, controllers_dir); return router.routes(); };
這樣app.js中只需要引入這個middleware,再調用就可以完成url的處理
操作數據庫:
ORM技術:Object-Relational Mapping,把關系數據庫的表結構映射到js對象上。
ORM框架:Sequelize
數據庫配置信息,在使用Sequelize操作MySQL需要放在參數里,告訴函數應該如何做
將其保存在config.js中
var config = { database: 'test', // 使用哪個數據庫 username: 'www', // 用戶名 password: 'www', // 口令 host: 'localhost', // 主機名 port: 3306 // 端口號,MySQL默認3306 }; module.exports = config;
1、創建Sequelize對象實例
//引入
const Sequelize = require('sequelize'); const config = require('./config'); //對象實例化 var sequelize = new Sequelize(config.database, config.username, config.password, { host: config.host, dialect: 'mysql', pool: { max: 5, min: 0, idle: 30000 } });
new Sequelize(database, [username=null], [password=null], [options={}])
//option可選填

2、定義數據模型model讓數據庫可以創建表
//第一個參數傳入名稱Pet,默認的表名是Pets
//第二個參數指定列名和數據類型,如果是主鍵,需要更詳細地指定。
//第三個參數是額外的配置,我們傳入timestamps: false是為了關閉Sequelize的自動添加timestamp的功能
var Pet = sequelize.define('pet', { id: { type: Sequelize.STRING(50), primaryKey: true }, name: Sequelize.STRING(100), gender: Sequelize.BOOLEAN, birth: Sequelize.STRING(10), createdAt: Sequelize.BIGINT, updatedAt: Sequelize.BIGINT, version: Sequelize.BIGINT }, {
timestamps: false });
3、插入數據
promise方式
var now = Date.now(); Pet.create({ id: 'g-' + now, name: 'Gaffey', gender: false, birth: '2007-07-07', createdAt: now, updatedAt: now, version: 0 }).then(function (p) { console.log('created.' + JSON.stringify(p)); }).catch(function (err) { console.log('failed: ' + err); });
await方式
(async () => { var dog = await Pet.create({ id: 'd-' + now, name: 'Odie', gender: false, birth: '2008-08-08', createdAt: now, updatedAt: now, version: 0 }); console.log('created: ' + JSON.stringify(dog)); })();
4、查詢數據
(async () => { var pets = await Pet.findAll({ where: { name: 'Gaffey' } }); console.log(`find ${pets.length} pets:`); for (let p of pets) { console.log(JSON.stringify(p)); } })();
5、更新數據
如果要更新數據,可以對查詢到的實例調用save()方法: (async () => { var p = await queryFromSomewhere(); p.gender = true; p.updatedAt = Date.now(); p.version ++; await p.save(); })();
6、刪除數據
(async () => { var p = await queryFromSomewhere(); await p.destroy(); })();
直接使用Sequelize雖然可以創建model,但是存在一些問題
混亂,不方便管理,不規范,無法復用
制定一個規范
A、model統一存放在models文件夾中
B、其次,每一個Model必須遵守一套規范:
- 統一主鍵,名稱必須是
id
,類型必須是STRING(50)
; - 主鍵可以自己指定,也可以由框架自動生成(如果為null或undefined);
- 所有字段默認為
NOT NULL
,除非顯式指定; - 統一timestamp機制,每個Model必須有
createdAt
、updatedAt
和version
,分別記錄創建時間、修改時間和版本號。其中,createdAt
和updatedAt
以BIGINT
存儲時間戳,最大的好處是無需處理時區,排序方便。version
每次修改時自增。
這一套規范,不需要去記憶,而是通過一個db.js統一Model的定義:
const Sequelize = require('sequelize'); console.log('init sequelize...'); var sequelize = new Sequelize('dbname', 'username', 'password', { host: 'localhost', dialect: 'mysql', pool: { max: 5, min: 0, idle: 10000 } }); const ID_TYPE = Sequelize.STRING(50); function defineModel(name, attributes) { var attrs = {}; for (let key in attributes) { let value = attributes[key]; if (typeof value === 'object' && value['type']) { value.allowNull = value.allowNull || false; attrs[key] = value; } else { attrs[key] = { type: value, allowNull: false }; } } attrs.id = { type: ID_TYPE, primaryKey: true }; attrs.createdAt = { type: Sequelize.BIGINT, allowNull: false }; attrs.updatedAt = { type: Sequelize.BIGINT, allowNull: false }; attrs.version = { type: Sequelize.BIGINT, allowNull: false }; return sequelize.define(name, attrs, { tableName: name, timestamps: false, hooks: { beforeValidate: function (obj) { let now = Date.now(); if (obj.isNewRecord) { if (!obj.id) { obj.id = generateId(); } obj.createdAt = now; obj.updatedAt = now; obj.version = 0; } else { obj.updatedAt = Date.now(); obj.version++; } } } }); }
怎么調用呢db.js創建model?舉個例子!
//引入db.js
const db = require('../db'); //調用db.js中的defineModel,定義並暴露model module.exports = db.defineModel('users', { email: { type: db.STRING(100), unique: true }, passwd: db.STRING(100), name: db.STRING(100), gender: db.BOOLEAN });
在使用model進行數據庫操作時候,每一次都要導入model,如果同時使用多個model,還要寫多條語句去導入,顯得特別麻煩
創建一個model.js自動化導入所有的model
const fs = require('fs'); const db = require('./db'); let files = fs.readdirSync(__dirname + '/models'); let js_files = files.filter((f)=>{ return f.endsWith('.js'); }, files); module.exports = {}; for (let f of js_files) { console.log(`import model from file ${f}...`); let name = f.substring(0, f.length - 3); module.exports[name] = require(__dirname + '/models/' + f); } module.exports.sync = () => { db.sync(); };
使用model.js
//引入model.js
const model = require('./model'); //掉用導入 let Pet = model.Pet, User = model.User; var pet = await Pet.create({ ... });