使用node+mysql進行后端開發


使用koa:

koa2是一個類,所以引入koa后,要創建實例化“對象”,才能使用koa內部封裝的方法。

 設置監聽端口:

 處理http請求:

1、http請求處理鏈

A、通過app.use()注冊async異步函數

B、每收到一個http請求,koa就會調用通過app.use()注冊的async異步函數,並傳入ctxnext參數。

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必須遵守一套規范:

  1. 統一主鍵,名稱必須是id,類型必須是STRING(50)
  2. 主鍵可以自己指定,也可以由框架自動生成(如果為null或undefined);
  3. 所有字段默認為NOT NULL,除非顯式指定;
  4. 統一timestamp機制,每個Model必須有createdAtupdatedAtversion,分別記錄創建時間、修改時間和版本號。其中,createdAtupdatedAtBIGINT存儲時間戳,最大的好處是無需處理時區,排序方便。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({ ... });

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM