koa面向物件式程式設計
阿新 • • 發佈:2018-11-17
koa面向物件式程式設計
最近由於在用egg.js,所以十分羨慕它裡面面向物件的邏輯,
自己參照著簡單封裝了一個面向物件的koa2-node的 寫法
封裝思想,將一些基礎的方法(list, insert, update, delete)封裝至一個父類(基類),子類繼承它便可以獲得對應的方法
-
route層
定義了一堆頁面路由
-
使用者請求對應的路由
-
進入對應的server.action方法
-
檔案目錄
-
admin/index.js
const model = require('../model') // 獲取基本路由 // 模組匯出 module.exports = { ...model, }
-
model/index.js
// enum http function: post、get const methods = require('../methods') module.exports = { // search 'list': { method: methods.get }, // add 'insert': { method: methods.post }, // update 'update': { method: methods.post }, // delete 'delete': { method: methods.post }, }
-
method.js
module.exports = { get: 'get', post: 'post', }
index.js
// 使用koa-router middleware const router = require('koa-router')() // 獲取對應的service層 const services = { admin: require('../services/AdminService'), } // 配置所有的routes檔案 const routes = (config => { return config.reduce((copy, name) => { const obj = require(`./${name}`) // 獲取對應個單個檔案 admin.js etc.. const newArr = Object.keys(obj).reduce((total, each) => { // 配置path,method etc.. // { path: '/api/admin/list', method: post, action: list, server: 'admin' } let item = { path: `/api/${name.toLowerCase()}/${each}`, method: obj[each].method, action: each, service: name } total.push(item) return total }, []) copy = copy.concat(newArr) return copy }, []) })(Object.keys(services)) // 配置最終的路由,形式為 // router.get(url, service.action) routes.forEach(item => { const { method, path, service: serviceName, action } = item const service = services[serviceName] router[method](path, service[action]) }) module.exports = router
-
-
service層
對請求引數的操作層
對返回資料的加工層
-
拿到request.body裡面的引數
-
呼叫對應的controller
-
拿到返回的資料,加工返回
-
檔案目錄
-
BaseService.js
const { pojo,filterUnderLine,} = require('../helper') // 獲取輔助類裡面的一些方法 const { success, failed, successWithCode } = pojo // 獲取訊息集裡的一些輔助方法 // 需要繫結this的方法 const funcs = [ 'list', 'insert', 'update', 'delete', ] class BaseService { constructor() { this.controller = null; // 迴圈遍歷繫結this funcs.forEach(item => { this[item] = this[item].bind(this) }) } // 查詢方法 async list(ctx) { // controller返回的是一個物件,success(成功為true, 失敗為false), data(成功則有此資料), err(失敗則有此物件) const { success: flag, data, err } = await this.controller.list() if (flag) { // success 為pojo訊息集的成功返回 ctx.body = success( data.map(item => // 篩選下劃線屬性,返回駝峰 filterUnderLine(item) ) ) } else { // failed 為pojo訊息集的失敗返回,下同 ctx.body = failed(err) } } // 插入方法 async insert(ctx) { const { row } = ctx.request.body const { success, err } = await this.controller.insert(row) if (success) { // successWithCode 為沒有資料返回時的成功返回 ctx.body = successWithCode('新增成功') // 沒有資料則返回 } else { // 同上 ctx.body = failed(err) } } // 更新方法 // 同上 async update(ctx) { const { row } = ctx.request.body const { success, err } = await this.controller.update(row) if (success) { ctx.body = successWithCode('新增成功') } else { ctx.body = failed(err) } } // 刪除方法 // 同上 async delete(ctx) { const { row } = ctx.request.body const { success, err } = await this.controller.delete(row) if (success) { ctx.body = successWithCode('新增成功') } else { ctx.body = failed(err) } } } module.exports = BaseService
-
AdminService.js
// 匯入基類 const BaseService = require('./BaseService') // 匯入對應的controller const Controller = require('../controller/AdminController') // 生成一次controller const AdminController = new Controller() class AdminService extends BaseService { constructor() { super() // 繫結對應的controller this.controller = AdminController; } } module.exports = new AdminService()
-
-
controller層
對資料庫的操作層
-
接收service層傳來的引數
-
對引數進行處理
-
執行sql語句,返回資料庫結果
-
檔案目錄
-
BaseController.js
const pool = require('../lib/mysql') // 匯入封裝好的mysql庫 const { query } = pool // 匯入query方法 const { STATUS } = require('../enum') // 匯入列舉型別STATUS const { NtNUpdate,filterCamel, } = require('../helper') // 匯入helper內相關方法 class BaseController { constructor() { this.table = '' } // 查詢表內所有資料(非刪除) async list() { const sql = `select * from ${this.table} where status != ?` return await query(sql, [STATUS.DELED]) } // 插入新資料 async insert(row) { const { keys, vals } = filterCamel(row) // 將駝峰命令轉換為下劃線 const names = keys.join(',') // 對應的引數 const questions = keys.map(item => '?').join(',') // 對應的引數佔位符 // 補全sql語句 insert into table (x, xx) values(x, xx) const sql = `insert into ${this.table}(${names},create_time,status) values(${questions},now(),?)` return await query(sql, [...vals, STATUS.NORMAL]) } async update(row) { const { id, } = row; // 獲取資料內的id // 刪除id delete row.id // 啟始sql let _sql = `update ${this.table} set ` const { sql, args } = NtNUpdate(row, _sql)// 獲取物件內非空值,加工sql 語句 update table set name=?, val= ? // 補全sql語句 update table set name = ?, val = ? where id = ? _sql = sql + 'where id = ?' return await query(_sql, [...args, id]) } async delete(row) { const { id } = row // 獲取資料內的id // 補全sql update table set status = ? where id = ? const sql = `update ${this.table} set status = ? where id = ?` return await query(sql, [STATUS.DELED, id]) } excute(sql, vals) { // 執行方法 return await = query(sql, vals) } // log 方法 log({func, err}) { console.log(`excute function[${func}] occured error : ${err.message || err}`) } } module.exports = BaseController
-
AdminController
const BaseController = require('./BaseController') // 獲得基類 class AdminController extends BaseController { constructor() { super(); this.table = 'tour_admin' // 賦值table } } module.exports = AdminController
-
-
helper類
-
helper/index.js
const pojo = require('./pojo') /** * * @param {Object} params 引數物件 * @param {String} sql sql語句 * @description 根據引數物件去改變sql語句,最後返回對應的sql語句 * @return 返回處理後的sql語句 */ const update = (params, sql) => { let keys = Object.keys(params) let arr = [] keys.forEach((key) => { if (key) { sql = sql + `${key} = ? ,` arr.push(params[key]) } }) sql = sql.substring(0, sql.length - 1) return { args: arr, sql, } } /** * * @param {String} val 原下劃線值 * @param {String} char 要替換的字元 * @description 根據原key去替換下劃線後轉為駝峰 * @return 返回處理後的key */ const replaceUnderLine = (val, char = '_') => { const arr = val.split('') const index = arr.indexOf(char) arr.splice(index, 2, arr[index+1].toUpperCase()) val = arr.join('') return val } /** * * @param {String} val 原下劃線值 * @description 下劃線轉駝峰 * @return 返回處理後的key */ const underline2Camel = (val) => { return val.replace(/\_(\w)/g, (all, letter) => { return letter.toUpperCase() }) } /** * * @param {String} val 原key * @param {String} char 要替換的字元 * @description 駝峰轉下劃線 * @return 返回處理後的key */ const camel2UnderLine = (val, char = '_') => { return val.replace(/([A-Z])/g,`${char}$1`).toLowerCase(); } /** * * @param {Object} obj 原物件 * @param {String} char 要替換的字元 * @description 物件駝峰轉下劃線 * @return 返回處理後的keys 和 對應的vals { aboutExample: 'example' }, ['about_example'], ['example'] * @return { Array } keys * @return { Array } vals */ const fileterCamel = (obj, char = '_') => { const keys = Object.keys(obj) return keys.reduce((init, item) => { const str = item if (~item.indexOf(char)) { str = camel2UnderLine(item) } init.keys.push(str) init.vals.push(obj[item]) return init }, { keys: [], vals: [], }) } /** * * @param {Object} obj 原物件 * @param {String} char 要替換的字元 * @description 物件下劃線轉駝峰 * @return 返回處理後的物件 { about_example: 'example' } { aboutExample: 'example' } * @return { Object } obj */ const filterUnderLine = (obj, char = '_') => { const arr = Object.keys(obj).filter(item => ~item.indexOf(char)) arr.forEach(item => { const val = obj[item] const key = underline2Camel(item) obj[key] = val delete obj[item] }) return obj } module.exports = { NtNUpdate: update, filterUnderLine, replaceUnderLine, replaceCamel: fileterCamel, pojo, }
-
helper/pojo.js
// 成功返回 const success = (result) => { return { retCode: 200, retValue: result } } // 成功沒資料返回 const successWithCode = msg => { return { retCode: 200, msg, } } // 失敗返回 const failed = (error) => { console.log(error) return { retCode: 500, msg: error.message || error || '伺服器異常' } } module.exports = { success, failed, successWithCode, }
-
-
enum列舉
-
enum/index.js
const status = require('./status') // 獲取狀態 const type = require('./type')// 獲取型別 // 匯出 module.exports = { STATUS: status, TYPES: type, }
-
enum/status.js
module.exports = { DELED: 404, // 刪除狀態 NORMAL: 0, // 普通狀態 }
-
enum/type.js
module.exports = { MANAGER: 999, // 管理員 USER: 0, // 使用者 }
-
- lib/mysql.js
-
const mysql = require('mysql'); const config = require('../config/default.js') // 連線池 const pool = mysql.createPool({ host : config.database.HOST, // 資料庫連線埠 user : config.database.USERNAME, // 資料庫使用者名稱 password : config.database.PASSWORD, // 資料庫密碼 database : config.database.DATABASE // 資料庫名稱 }); /** * * @param sql 接收的sql語句 * @param {Array} values sql語句引數 * @return { Object } { success: boolean, err || data } */ const query = function( sql, values ) { return new Promise(( resolve, reject ) => { pool.getConnection(function(err, connection) { if (err) { resolve( { success: false, err, } ) } else { connection.query(sql, values, ( err, rows) => { if ( err ) { resolve({ success: false, err, } ) } else { resolve( { success: true, data: rows, } ) } connection.release() }) } }) }) } module.exports={ query, }
-
config
-
config/default.js const config = { // 啟動埠 port: 1200, // 資料庫配置 database: { DATABASE: 'xxx', USERNAME: 'root', PASSWORD: 'xxx', PORT: 'xxx', HOST: 'xxx' } } module.exports = config
-
src/index.js
var Koa=require('koa');
var path=require('path')
var bodyParser = require('koa-bodyparser');
var session = require('koa-session-minimal');
var MysqlStore = require('koa-mysql-session');
var config = require('./config/default.js');
var koaStatic = require('koa-static')
var app=new Koa()
const routers = require('./routes/index')
// session儲存配置
const sessionMysqlConfig= {
user: config.database.USERNAME,
password: config.database.PASSWORD,
database: config.database.DATABASE,
host: config.database.HOST,
}
// 配置session中介軟體
app.use(session({
key: 'USER_SID',
store: new MysqlStore(sessionMysqlConfig)
}))
// 配置跨域
app.use(async (ctx, next) => {
ctx.set('Access-Control-Allow-Headers', 'Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With')
ctx.set('Access-Control-Allow-Origin', 'http://localhost:9000');
ctx.set('Access-Control-Allow-Methods', 'PUT,DELETE,POST,GET');
ctx.set('Access-Control-Allow-Credentials', true);
ctx.set('Access-Control-Max-Age', 3600 * 24);
await next();
});
// 配置靜態資源載入中介軟體
app.use(koaStatic(
path.join(__dirname , './public')
))
// 使用表單解析中介軟體
app.use(bodyParser())
// 使用新建的路由檔案
app.use(routers.routes()).use(routers.allowedMethods())
// 監聽在1200
app.listen(config.port)
console.log(`listening on port ${config.port}`)
到這裡專案就可以完整跑起來了,
最後奉上專案地址
喜歡就Star一下吧~