Data Validation数据验证(mongoose)

Data Validation

Validation

const courseSchema = new mongoose.Schema({
   
  name: String,
  author: String,
  tags: [String],
  date: {
    type: Date, default: Date.now() },
  isPublished: Boolean,
});

这是一个Schema,我们规定了一个课程应该具有什么属性,但是这些字段默认都是可选的,所以其实完全可以提交一个空的对象给MongoDB。MongoDB不会在意我们有什么属性,所以来看下如何使用required验证器来验证数据。

我们先让name字段必须

name: {
    type: String, required: true },

现在如果提交一个没有name的课程,会提示失败。提示失败,所以我们要记得把提交处理的代码放到try catch代码块里。

try {
   
  const result = await course.save();
  console.log(result);
  // await course.validate()
} catch (ex) {
   
  console.log(ex.message);
}

这里有一个另一个注释掉的代码,course的validate方法,有了它我们不需要另外两行。如果出错他会直接跳转到catch方法。注意:这个方法返回一个空地Promise对象,也就是说我们拿不到任何返回值。我们无法拿回一个布尔值做判断,只能使用callback的形式。

// const isValid = await course.validate()
course.validate((err) => {
   
if (err) {
    ....... }
})

注意:此处对name的验证只在mongoose有意义,MongoDB才不管有什么,所有如果直接在数据库内操作添加数据,没有name属性一样会通过。

Built in Validators

我们可以在required里做内建验证器,可以做一个函数,使其返回布尔值。例如,我们只需要价格在已发布的课程内必要。

price: {
   
    type: Number,
    required: function () {
   
      // Using function
      return this.isPublished;
    }
  },

注意:这里的required不可以使用箭头函数,箭头函数没有自己的this,所以它会把this指向离它最近的对象。mongoose在别的地方执行这个函数的化,就会无法指向到具体的course对象。

同时,根据属性的不同类型,还有很多附加验证器,例如对于String可以限定其长度。

name: {
   
    type: String,
    required: true,
    minlength: 5,
    maxlength: 255,
    // match: /pattern/,
  },

另一个有用的就是enum,可以限定其值只能为特定的几个。新建一个category属性。

category: {
   
  type: String,
  required: true,
  enum: ["web", "mobile", "network"],
},

现在category只能为web、mobile、network这几个。

对于数字也有他自己的附加验证器。

price: {
   
    type: Number,
    required: function () {
   
      // Using function
      return this.isPublished;
    },
    min: 20,
    max: 200,
  },

Custom Validators

tags: [String],

对于这个tags属性,我们要验证他不可以使用required属性,我们如果就算只给一个空数组,也可以

通过认证。所以我们要自己做验证器,使用validate对象的validator属性。

tags: {
   
  type: String,
  validate: {
   
    validator: function (v) {
   
      return v.length > 0;
    },
  },
},

我们也可以给它一个验证信息,使用message属性。

tags: {
   
  type: String,
  validate: {
   
    validator: function (v) {
   
      return v && v.length > 0;
    },
    message: "A course should be have at least one tag",
  },
},

Async Validators

有时候验证逻辑可能需要读取数据库或者远端HTTP服务,这时候就需要异步验证器了。看看如何将其改为一个异步验证器。设定isAsync为true,同时我们需要一个callback来处理异步的返回结果。

validate: {
   
    isAsync: true,
    validator: function (v, callback) {
   
      // Do some async work
      const result = v && v.length > 0;
      callback(result);
    },
    message: "A course should be have at least one tag",
  },
tags: {
   
  type: String,
  validate: {
   
    isAsync: true,
    validator: function (v, callback) {
   
      // Do some async work
      const result = v && v.length > 0;
      callback(result);
    },
    message: "A course should be have at least one tag",
  },
},

Validation Errors

我们来让try catch代码块里的错误处理信息更加相信。

在mongoose返回的error里有errors属性,在这个属性又可以单独获得特定属性错误。例如

ex.errors.name

所以我们可以采用for来输出错误消息,或者更详细的错误报告,堆栈信息。

try {
   
  const result = await course.save();
  console.log(result);
  // await course.validate()
} catch (ex) {
   
  //----------------------------------------------------------------
  for (field in ex.errors) {
   
    console.log(ex.errors[field].message);
  }
}

Schema Type Options

之前我们已经知道,可以把Schema里的属性设置成一个对象,对于特定的类型,还可以有不同的Schema属性对数据做处理。例如lowercase,可以把传入的数据自动传为小写,对应就有uppercase。还有trim属性,把字符串的前后空格都删掉。

category: {
   
  type: String,
  required: true,
  enum: ["web", "mobile", "network"],
  // lowercase: true,
  // uppercase: true,
  // trim:true ------------> Delete blank before and after the string.
},

再或者对于price,我们永远对price的数值四舍五入小数部分,就可以使用get和set

price: {
   
  type: Number,
  required: function () {
   
    // Using function
    return this.isPublished;
  },
  min: 20,
  max: 200,
  get: (v) => Math.round(v),
  set: (v) => Math.round(v),
},

这样我们在输入数据的时候,set就会自动去掉小数。如果数据库有了有小数的课程,那我们在访问的时候,get就会把请求回来的price小数去掉。

Project - Add Persistence to Genres API

我们在我们之前做到vidly中已经有一个genres的路由句柄。现在来尝试把数据库移到MongoDB后修改路由句柄来对数据库做操作。

// routes/genres.js
const express = require('express');
const router = express.Router();

const genres = [
  {
    id: 1, name: 'Action' },  
  {
    id: 2, name: 'Horror' },  
  {
    id: 3, name: 'Romance' },  
];

router.get('/', (req, res) => {
   
  res.send(genres);
});

router.post('/', (req, res) => {
   
  const {
    error } = validateGenre(req.body); 
  if (error) return res.status(400).send(error
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值