Node.js Koa2開發微信小程序服務端


1、promise、async、await

const Koa = require('koa')

const app = new Koa()

// 應用程序對象 有很多中間件

// 發送HTTP KOA 接收HTTP(使用中間件,中間件實際就是函數)

// await: 1、求值關鍵字,不僅是promise,表達式也可以(100*100)
//        2、阻塞當前線程

// async 只要函數前面加了async,返回的值就會被promise包裹

// 注冊
app.use(async (ctx, next) => {
  // ctx 上下文
  console.log('1')
  const a =await next()
  console.log(a)
  console.log('2')
})

app.use(async (ctx, next) => {
  console.log('3')
  console.log('4')
  return 'abc'
})

app.listen(3300)
async 只要函數前面加了async,返回的值就會被promise包裹

await: 1、求值關鍵字,不僅是promise,表達式也可以(100*100)
2、阻塞當前線程

 

2、

第一種情況

app.use((ctx, next) => {
  // ctx 上下文
  console.log('1')
  next()
  console.log('2')
})

app.use(async (ctx, next) => {
  console.log('3')
  next()
  console.log('4')
})

app.listen(3300)

這是因為node的洋蔥模型,next()為中間分割點

第二種情況

// 注冊
app.use((ctx, next) => {
  // ctx 上下文
  console.log('1')
  next()
  console.log('2')
})

app.use(async (ctx, next) => {
  console.log('3')
  const axios = require('axios')
  const res =await axios.get('http://7yue.pro')
  next()
  console.log('4')
})

app.listen(3300)

async await阻塞了當前線程,所以就跳轉到其他線程

第三章情況:要想讓中間件一直都執行洋蔥模型,就需要在next前面使用await

// 注冊
app.use(async (ctx, next) => {
  // ctx 上下文
  console.log('1')
  await next()
  console.log('2')
})

app.use(async (ctx, next) => {
  console.log('3')
  const axios = require('axios')
  const res =await axios.get('http://7yue.pro')
  await next()
  console.log('4')
})

app.listen(3300)

 

 3、koa-router

https://www.npmjs.com/package/koa-router

const Koa = require('koa')
const Router = require('koa-router')

const app = new Koa()
const router = new Router()

router.get('/classic/latest', (ctx, next) => {
  ctx.body = {key: 'classic'} // 返回的信息
})

app
  .use(router.routes())
  .use(router.allowedMethods)

app.listen(3300)

 4、exports和module.exports的區別

真正的是module.exports

module.exports是一個對象  module.exports={}

exports是module.exports的引用,類似於: var a= {},b=a;

 5、使用require-directory批量加載router

https://www.npmjs.com/package/require-directory

const Koa = require('koa')
const requireDirectory = require('require-directory')
const Router = require('koa-router')

const app = new Koa()

// 通過requireDirectory獲取app/api/v1下的所有routers
// visit: whenLoadModule 函數
const modules = requireDirectory(module, './app/api/v1', {
  visit: whenLoadModule
})

// 用來判斷引入的是router
function whenLoadModule(obj) {
  if(obj instanceof Router) {
    app.use(obj.routes())
  }
}

app.listen(3300)

 6、NodeJs中process.cwd()與__dirname的區別

process.cwd() 是當前執行node命令時候的文件夾地址 ——工作目錄,保證了文件在不同的目錄下執行時,路徑始終不變
__dirname 是被執行的js 文件的地址 ——文件所在目錄

Nodejs官方文檔上的解釋:

=> process.cwd(): The process.cwd() method returns the current working directory of theNode.js process.

意思很明了啦,就是說process.cwd()返回的是當前Node.js進程執行時的工作目錄

那么來看看__dirname的官方解釋:

=> __dirname: 當前模塊的目錄名。 等同於 __filename 的 path.dirname()__dirname 實際上不是一個全局變量,而是每個模塊內部的。

7、Node koa2中獲取參數

http://localhost:3300/v1/:1/classic/latest?param=8yue

router.post('/v1/:id/classic/latest', (ctx, next) => {

  const path = ctx.params  // 獲取的是:id里的值 1
  const query = ctx.request.query // 獲取的是?param的值 8yue
  const headers = ctx.request.header // header傳遞的值
  const body = ctx.request.body // body里的值,json格式

  ctx.body = {
    key: 'classic'
  }
})

 8、如果是異步操作,而且用的是promise,一定要加上async、await

而且要用try ctach處理異常

9、定義錯誤基類

class HttpException extends Error {
  constructor (msg = '服務器異常', errorCode = 10000, code = 400) {
    super()
    this.msg = msg
    this.errorCode = errorCode
    this.code = code
  }
}

使用

const { HttpException } = require('../../../core/http-exception')

const error = new HttpException()
throw error

在中間件里throw出error,這樣才能被try...catch捕獲

10、特定異常類

class ParameterException extends HttpException {
  constructor (msg, errorCode) {
    super()
    this.errorCode = errorCode || 10000
    this.msg = msg || '參數錯誤'
    this.code = 400
  }
}

module.exports = {
  HttpException,
  ParameterException
}

使用

const { ParameterException } = require('../../../core/http-exception')

const error = new ParameterException()
throw error

優化

每次使用錯誤的時候,都需要引入,然后new,可以把這個錯誤放到global里面

static loadHttpException() {
    const errors = require('./http-exception')
    global.errs = errors
  }

使用

const error = new global.errs.ParameterException()
throw error

這倆種方法都可以使用,看自己喜歡

11、async、await進行全局異常處理可以使用try...catch

在項目中,我們如果給每一個使用async、await的函數使用try...catch,這樣太麻煩,我們可以定義一個全局異常處理中間件

const { HttpException } = require('../core/http-exception')

const catchError = async (ctx, next) => {
  try {
    await next() // 有了next,函數調用后就會觸發
  } catch (error) {
    // 判斷error是否是HttpException
    if (error instanceof HttpException) {
      // 返回的錯誤信息
      ctx.body = {
        mag: error.msg,
        error_code: error.errorCode,
        request: `${ctx.method} ${ctx.path}`
      }
      ctx.status = error.code
    }
  }
}

module.exports = catchError

app.js

const catchError = require('./middlewares/exception')

app.use(catchError)

這樣就注冊了一個全局異常處理中間件,只要Node里有中間件運行時拋出異常,就會被這個中間件捕獲

 

12、LinValidator校驗器  http://doc.cms.7yue.pro/lin/server/koa/validator.html#類校驗

是用方式:

  (1)、npm install validator --save-dev

  (2)、創建lin-validator.js和util.js

   lin-validator.js

   

/**
 * Lin-Validator v1
 * 作者:7七月
 * 微信公眾號:林間有風
 */

const validator = require('validator')
const {
  ParameterException
} = require('./http-exception')
const {
  get,
  last,
  set,
  cloneDeep
} = require("lodash")
const {
  findMembers
} = require('./util')


class LinValidator {
  constructor() {
    this.data = {}
    this.parsed = {}
  }


  _assembleAllParams(ctx) {
    return {
      body: ctx.request.body,
      query: ctx.request.query,
      path: ctx.params,
      header: ctx.request.header
    }
  }

  get(path, parsed = true) {
    if (parsed) {
      const value = get(this.parsed, path, null)
      if (value == null) {
        const keys = path.split('.')
        const key = last(keys)
        return get(this.parsed.default, key)
      }
      return value
    } else {
      return get(this.data, path)
    }
  }

  _findMembersFilter(key) {
    if (/validate([A-Z])\w+/g.test(key)) {
      return true
    }
    if (this[key] instanceof Array) {
      this[key].forEach(value => {
        const isRuleType = value instanceof Rule
        if (!isRuleType) {
          throw new Error('驗證數組必須全部為Rule類型')
        }
      })
      return true
    }
    return false
  }

  validate(ctx, alias = {}) {
    this.alias = alias
    let params = this._assembleAllParams(ctx)
    this.data = cloneDeep(params)
    this.parsed = cloneDeep(params)

    const memberKeys = findMembers(this, {
      filter: this._findMembersFilter.bind(this)
    })

    const errorMsgs = []
    // const map = new Map(memberKeys)
    for (let key of memberKeys) {
      const result = this._check(key, alias)
      if (!result.success) {
        errorMsgs.push(result.msg)
      }
    }
    if (errorMsgs.length != 0) {
      throw new ParameterException(errorMsgs)
    }
    ctx.v = this
    return this
  }

  _check(key, alias = {}) {
    const isCustomFunc = typeof (this[key]) == 'function' ? true : false
    let result;
    if (isCustomFunc) {
      try {
        this[key](this.data)
        result = new RuleResult(true)
      } catch (error) {
        result = new RuleResult(false, error.msg || error.message || '參數錯誤')
      }
      // 函數驗證
    } else {
      // 屬性驗證, 數組,內有一組Rule
      const rules = this[key]
      const ruleField = new RuleField(rules)
      // 別名替換
      key = alias[key] ? alias[key] : key
      const param = this._findParam(key)

      result = ruleField.validate(param.value)

      if (result.pass) {
        // 如果參數路徑不存在,往往是因為用戶傳了空值,而又設置了默認值
        if (param.path.length == 0) {
          set(this.parsed, ['default', key], result.legalValue)
        } else {
          set(this.parsed, param.path, result.legalValue)
        }
      }
    }
    if (!result.pass) {
      const msg = `${isCustomFunc ? '' : key}${result.msg}`
      return {
        msg: msg,
        success: false
      }
    }
    return {
      msg: 'ok',
      success: true
    }
  }

  _findParam(key) {
    let value
    value = get(this.data, ['query', key])
    if (value) {
      return {
        value,
        path: ['query', key]
      }
    }
    value = get(this.data, ['body', key])
    if (value) {
      return {
        value,
        path: ['body', key]
      }
    }
    value = get(this.data, ['path', key])
    if (value) {
      return {
        value,
        path: ['path', key]
      }
    }
    value = get(this.data, ['header', key])
    if (value) {
      return {
        value,
        path: ['header', key]
      }
    }
    return {
      value: null,
      path: []
    }
  }
}

class RuleResult {
  constructor(pass, msg = '') {
    Object.assign(this, {
      pass,
      msg
    })
  }
}

class RuleFieldResult extends RuleResult {
  constructor(pass, msg = '', legalValue = null) {
    super(pass, msg)
    this.legalValue = legalValue
  }
}

class Rule {
  constructor(name, msg, ...params) {
    Object.assign(this, {
      name,
      msg,
      params
    })
  }

  validate(field) {
    if (this.name == 'optional')
      return new RuleResult(true)
    if (!validator[this.name](field + '', ...this.params)) {
      return new RuleResult(false, this.msg || this.message || '參數錯誤')
    }
    return new RuleResult(true, '')
  }
}

class RuleField {
  constructor(rules) {
    this.rules = rules
  }

  validate(field) {
    if (field == null) {
      // 如果字段為空
      const allowEmpty = this._allowEmpty()
      const defaultValue = this._hasDefault()
      if (allowEmpty) {
        return new RuleFieldResult(true, '', defaultValue)
      } else {
        return new RuleFieldResult(false, '字段是必填參數')
      }
    }

    const filedResult = new RuleFieldResult(false)
    for (let rule of this.rules) {
      let result = rule.validate(field)
      if (!result.pass) {
        filedResult.msg = result.msg
        filedResult.legalValue = null
        // 一旦一條校驗規則不通過,則立即終止這個字段的驗證
        return filedResult
      }
    }
    return new RuleFieldResult(true, '', this._convert(field))
  }

  _convert(value) {
    for (let rule of this.rules) {
      if (rule.name == 'isInt') {
        return parseInt(value)
      }
      if (rule.name == 'isFloat') {
        return parseFloat(value)
      }
      if (rule.name == 'isBoolean') {
        return value ? true : false
      }
    }
    return value
  }

  _allowEmpty() {
    for (let rule of this.rules) {
      if (rule.name == 'optional') {
        return true
      }
    }
    return false
  }

  _hasDefault() {
    for (let rule of this.rules) {
      const defaultValue = rule.params[0]
      if (rule.name == 'optional') {
        return defaultValue
      }
    }
  }
}



module.exports = {
  Rule,
  LinValidator
}

util.js

const jwt = require('jsonwebtoken')
/***
 *
 */
const findMembers = function (instance, {
  prefix,
  specifiedType,
  filter
}) {
  // 遞歸函數
  function _find(instance) {
    //基線條件(跳出遞歸)
    if (instance.__proto__ === null)
      return []

    let names = Reflect.ownKeys(instance)
    names = names.filter((name) => {
      // 過濾掉不滿足條件的屬性或方法名
      return _shouldKeep(name)
    })

    return [...names, ..._find(instance.__proto__)]
  }

  function _shouldKeep(value) {
    if (filter) {
      if (filter(value)) {
        return true
      }
    }
    if (prefix)
      if (value.startsWith(prefix))
        return true
    if (specifiedType)
      if (instance[value] instanceof specifiedType)
        return true
  }

  return _find(instance)
}

const generateToken = function(uid, scope){
  const secretKey = global.config.security.secretKey
  const expiresIn = global.config.security.expiresIn
  const token = jwt.sign({
    uid,
    scope
  },secretKey,{
    expiresIn
  })
  return token
}



module.exports = {
  findMembers,
  generateToken,
}

 (3)、定義自己的validator.js文件

const { LinValidator, Rule } = require('../../core/lin-validator')

class PositiveIntegerValidator extends LinValidator {
  constructor () {
    super()
    this.id = [
      // 校驗的參數是:id,校驗的函數名是:isInt,提示信息是:需要是正整數,其他條件是min: 1
      new Rule('isInt', '需要是正整數', {
        min: 1
      })
    ]
  }
}

module.exports = {
  PositiveIntegerValidator
}

  (4)、使用  

const { PositiveIntegerValidator } = require('../../validators/validator')

const v = new PositiveIntegerValidator().validate(ctx)

// 獲取path里的值 console.log(v.
get('path')) // path、query、body、header

可以使用get()獲取參數的值

 13、倆種獲取參數的方法

const path = ctx.params
const query = ctx.request.query
const headers = ctx.request.header
const body = ctx.request.body

和get()方法

推薦使用get()獲取

原因:現在有個多層嵌套函數,如果用第一種方式,某一層沒有值,會報錯。而使用get(),會報空值

14、LinValidator校驗器中的自定義規則函數  http://doc.cms.7yue.pro/lin/server/koa/validator.html#類校驗

       我們把以validate開頭的類方法稱之為規則函數,我們會在校驗的時候自動的調用 這些規則函數。

      規則函數是校驗器中另一種用於對參數校驗的方式,它比顯示的 Rule 校驗具有更加的靈活 性和可操作性。下面我們以一個小例子來深入理解規則函數:

validateConfirmPassword(data) {
    if (!data.body.password || !data.body.confirm_password) {
      return [false, "兩次輸入的密碼不一致,請重新輸入"];
    }
    let ok = data.body.password === data.body.confirm_password;
    if (ok) {
      return ok;
    } else {
      return [false, "兩次輸入的密碼不一致,請重新輸入"];
    }
  }

首先任何一個規則函數,滿足以validate開頭的類方法,除validate()這個函數外。都 會被帶入一個重要的參數 data。data 是前端傳入參數的容器,它的整體結構如下:

this.data = {
  body: ctx.request.body, // body -> body
  query: ctx.request.query, // query -> query
  path: ctx.params, // params -> path
  header: ctx.request.header // header -> header
};

請記住 data 參數是一個二級的嵌套對象,它的屬性如下:

 

   

data是所有參數的原始數據,前端傳入的參數會原封不動的裝進 data。通過這個 data 我們可以很方便的對所有參數進行校驗,如在validateConfirmPassword這個規則函數中 ,我們便對data.body中的passwordconfirm_password進行了聯合校驗。

我們通過對規則函數的返回值來判斷,當前規則函數的校驗是否通過。簡單的理解,如果規 則返回true,則校驗通過,如果返回false,則校驗失敗。但是校驗失敗的情況下,我們 需要返回一條錯誤信息,如:

return [false, "兩次輸入的密碼不一致,請重新輸入"];

表示規則函數校驗失敗,並且錯誤信息為兩次輸入的密碼不一致,請重新輸入

15、Koa2中使用sequelize創建數據表  

  文檔: https://itbilu.com/nodejs/npm/VkYIaRPz-.html#api-instance-createSchema

   操作mysql

    需要安裝sequelize和mysql2

   

const { Sequelize } = require('sequelize')

const {
  dbName,
  host,
  port,
  username,
  password
} = require('../config/config').database

const sequelize = new Sequelize(dbName, username, password, {
  dialect:'mysql', // 數據庫類型
  host,
  port,
  logging: true, // 記錄操作的sql語句
  timezone: '+08:00' // 默認的時間會比正常時間慢8小時
})

 

// 加了這個,才能把定義的模型同步到數據庫中
sequelize.sync({
  force: false // true會自動運行,通過定義的model修改數據庫中的表
})
force為true的時候,如果我們修改models中定義的屬性值,就會自動同步到數據庫表(會清空數據庫表並重建),我們一般設置為false

    

News.init({
  id: {
    type: Sequelize.INTEGER,
    primaryKey: true, //
    autoIncrement: true // 自增長
  },
  title: Sequelize.STRING,
  summary: Sequelize.STRING
}, {
  sequelize,
  tableName: 'news' // 自定義數據庫表名
})

mode中定義好數據庫表的字段以后,需要在其他js文件引入這個model文件,Sequelize才會操作數據庫表

    

 

16、使用lin-validator-v2,new RegisterValidator()前面加await?

router.post('/register', async (ctx) => {
  // 為何要在new RegisterValidator()前面加await,而且使用lin-validator-v2?
  /**
   * 因為RegisterValidator里validateEmail方法中的User.findOne是一個Promise異步操作,
   * 不加await的話,無法阻止錯誤,還是會執行后面的代碼,引起系統報錯
   */
  const v = await new RegisterValidator().validate(ctx)

  const user = {
    email: v.get('body.email'),
    password: v.get('body.password1'),
    nickname: v.get('body.nickname')
  }

  User.create(user)
})

 17、在node項目中的請求,每一個請求都要new一次validate,為什么一定要這樣做,有沒有其他的寫法?

router.post('/register', async (ctx) => {
  // 為何要在new RegisterValidator()前面加await,而且使用lin-validator-v2?
  /**
   * 因為RegisterValidator里validateEmail方法中的User.findOne是一個Promise異步操作,
   * 不加await的話,無法阻止錯誤,還是會執行后面的代碼,引起系統報錯
   */
  const v = await new RegisterValidator().validate(ctx)

  const user = {
    email: v.get('body.email'),
    password: v.get('body.password1'),
    nickname: v.get('body.nickname')
  }

  User.create(user)
})

就有另一種寫法,就是把validate當成中間件

router.post('/register',new RegisterValidator(), async (ctx) => {
  // 為何要在new RegisterValidator()前面加await,而且使用lin-validator-v2?
  /**
   * 因為RegisterValidator里validateEmail方法中的User.findOne是一個Promise異步操作,
   * 不加await的話,無法阻止錯誤,還是會執行后面的代碼,引起系統報錯
   */
  // const v = await new RegisterValidator().validate(ctx)

  const user = {
    email: v.get('body.email'),
    password: v.get('body.password1'),
    nickname: v.get('body.nickname')
  }

  User.create(user)
})

但是這有個很大的問題,node中的中間件只new一次,我們的validate是class,在class保存屬性的時候回出錯

比如validate.a = 1,后面改成validate.a = 2,這樣判斷就會出錯

但是如果我們在每個請求中New一次validate,這就沒問題

這個涉及到面向對象的知識,使用中間件的方式,只new一次,生成一個對象,這樣多個地方會改變里面的值

如果是每個請求new一次,那么就生成單獨的對象,改變屬性值的時候,不會影響其他對象里的值

 

18、使用bcryptjs加密

const bcrypt = require('bcryptjs')

  const salt = bcrypt.genSaltSync(10)
  // 10是位數,標識計算機計算的時候用多久,不宜太大
  const psw = bcrypt.hashSync(v.get('body.password1'), salt)

這樣的寫法每個有password的地方都要這樣寫,有另一種更好的

在定義模型的文件里,使用set方法進行監控

password: {
    type: Sequelize.STRING,
    // 觀察者模式
    set (val) {
      const salt = bcrypt.genSaltSync(10)
      // 10是位數,標識計算機計算的時候用多久,不宜太大
      const psw = bcrypt.hashSync(val, salt)
      this.setDataValue('password', psw)
    }
  }

 使用bcryptjs中的compareSync驗證轉入的密碼和數據庫中加密的密碼是否一致

const correct = bcrypt.compareSync(plainPassword, User.password)

 

19、success效果,執行成功以后返回提示信息

  第一種方案:

ctx.body = {
    msg: '',
    code: ''
  }

使用ctx.body返回成功信息

第二種方案:

把success包裝成exception,返回的時候,傳遞success的msg和code

// exception.js

class Success extends HttpException {
  constructor(msg, errorCode) {
    super()
    this.code = 201
    this.msg = msg || 'ok'
    this.errorCode = errorCode || 0
  }
}


// heaper.hs

function success(msg, errorCode) {
  throw new global.errs.Success(msg, errorCode)
}

module.exports = {
  success
}

在使用的地方引入,直接success()

 

20、權限的判斷

我們可以給不同的角色設置不同的值,比如user(普通用戶)為8,admin(管理員)為16,默認的權限值為1,每個接口可以設置不同的權限值。

登錄的時候,把用戶的權限通過token傳遞到前端。前端請求接口的時候,通過token判斷當前用戶的權限,如果比接口需要的權限值大,那就說明可以訪問,不然就是權限不足

auth.js

const basicAuth = require('basic-auth')
const jwt = require('jsonwebtoken')

class Auth {
  constructor (level) {
    this.level = level || 1 // 設置默認的權限值,可以由接口自定義
    Auth.USER = 8
    Auth.ADMIN = 16
    Auth.SUPER_ADMIN = 32
  }

  get m () {
    return async (ctx, next) => {
      const userToken = basicAuth(ctx.req)
      let errMsg = 'token不合法'

      if (!userToken || !userToken.name) {
        throw new global.errs.Forbbiden(errMsg)
      }
      try {
        var decode = jwt.verify(userToken.name, global.config.security.secretKey)
      } catch (error) {
        if (error.name === 'TokenExpiredError'){
          errMsg = 'token已過期'
        }
        throw new global.errs.Forbbiden(errMsg)
      }

      // 判斷用戶的權限是否比接口需要的權限小
      if(decode.scope < this.level){
        errMsg = '權限不足'
        throw new global.errs.Forbbiden(errMsg)
      }

      // 獲取token里的uid,scope
      ctx.auth = {
        uid: decode.uid,
        scope: decode.scope
      }

      await next()
    }
  }
}

module.exports = {
  Auth
}

classic.js

const { Auth } = require('../../../middlewares/auth')

// auth也是一個中間件,一定要寫在后面的中間件前面,這樣才能阻止后面的中間件
// 可以在new Auth()里傳遞值,確定訪問當前接口需要什么權限
router.get('/latest', new Auth(2).m, async (ctx, next) => {

  const v = new PositiveIntegerValidator().validate(ctx)
  const id = await v.get('path.id', parsed = false) // path、query、body、header
  ctx.body = {
    msg: 'success',
    id: v.get('path.id')
  }
})

 

21、微信小程序從登錄到獲取token一系列操作

https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html

微信登錄后調用wx.login方法,獲取臨時憑證code,傳遞給后端服務器

后端服務器調用 auth.code2Session接口,換取 用戶唯一標識 OpenID 和 會話密鑰 session_key

使用OpenID生成token,返回給前端

 

22、獲取openid每次都會返回errcode:40013 錯誤

檢查appId的值

23、訪問Node項目的時候會突然奔潰,並報錯ctx.onerror is not a function  地址:https://segmentfault.com/q/1010000009716118

原因是:koa-bodyparser不對

正確應該是:

const parser = require('koa-bodyparser')

app.use(parser())

 24、使用了Sequelize創建的模型,如果要給模型添加額外的屬性,就需要使用setDataValue

  art.setDataValue('index', flow.index)

給art模型添加index屬性並賦值

25、每次引入數據的時候,都會寫很長的路徑,為了方便,我們可以起別名,node里的別名是寫在package.json里的

"_moduleAliases":{
    "@root":".",
    "@models":"app/models",
    "@validator":"app/validators/validator.js"
  }

使用

const {
    Favor
} = require('@models/favor')

不過我在使用中發現,一直在報錯,無法找到路徑,不知道為什么

26。我們在查詢表的時候,會返回表中所有的字段,有時候一些字段不需要,就要過濾掉,這是我們用Sequelize中的Scopes

https://sequelize.org/master/manual/scopes.html

在db表里統一進行設置

const sequelize = new Sequelize(dbName, username, password, {
  dialect:'mysql', // 數據庫類型
  host,
  port,
  logging: true, // 記錄操作的sql語句
  timezone: '+08:00', // 默認的時間會比正常時間慢8小時
  define: {
    // timestamps: false // 設置為false,就不會生成createdAt和updateAt了
    timestamps: true, // 管理 createdAt和updateAt
    paranoid: true, // 管理deletedAt
    createdAt: 'created_at',
    updatedAt: 'updated_at',
    deletedAt: 'deleted_at',
    underscored: true, // 把駝峰轉換成下划線
    scopes: {
      bh: {
        attributes:{
          exclude:['updated_at','deleted_at','created_at']
        }
      }
    }
  }
})
scopes: {
      bh: {
        attributes:{
          exclude:['updated_at','deleted_at','created_at']
        }
      }
    }

在scopes里我們可以定義多個函數,現在定義的是bh,exclude表示過濾掉這些字段

 

使用的時候

art = await Movie.scope('bh').findOne(finder)

在調用Sequelize自帶的查詢方法錢調用scope(),並傳入之前定義好的函數名

備注:scopes也可以在每個表的模型文件中定義


免責聲明!

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



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