• 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也可以在每个表的模型文件中定义

  • 相关阅读:
    响应式笔记(1)
    javascript复制文章加版权声明代码
    div的水平和垂直居中
    javascript随机打乱数组
    javascript操作字符串的方法
    《Javascript高级程序设计》读书笔记(1-3章)
    一个将 footer 保持在底部的最好方法
    Python内置的字符串处理函数整理
    c c++怎么判断一个字符串中是否含有汉字
    shell的if判断
  • 原文地址:https://www.cnblogs.com/zhaobao1830/p/11026864.html
Copyright © 2020-2023  润新知