Node+express项目,提交表单信息时Cannot read property '_id' of undefined

Vagor
  • 726

前言

这是我在学习imooc上node实战时遇到的问题,视频中老师用了以下代码

var app = express()
var express = require('express')

app.use(express.bodyParser())

最后在视频中运行无误。

已解决的问题

我已经知道express.bodyParser()在新版本中被取消
图片描述

也知道了替代方案中的extend:true的作用
图片描述

仍然存在的问题

可是当我把代码修正过来之后,仍然报错

修改的代码
var bodyParser = require('body-parser')

app.use(bodyParser.urlencoded({ extended: true }))//将表单数据格式化
报错
^[{ [ValidationError: Movie validation failed]
  message: 'Movie validation failed',
  name: 'ValidationError',
  errors: 
   { year: 
      { [CastError: Cast to Number failed for value "2015/12/31" at path "year"]
        message: 'Cast to Number failed for value "2015/12/31" at path "year"',
        name: 'CastError',
        kind: 'Number',
        value: '2015/12/31',
        path: 'year',
        reason: undefined } } }
/Users/Vagor/Desktop/easy_movie/app.js:123
      res.redirect('/movie/' + movie._id)
                                    ^

TypeError: Cannot read property '_id' of undefined
    at /Users/Vagor/Desktop/easy_movie/app.js:123:37
    at /Users/Vagor/Desktop/easy_movie/node_modules/mongoose/lib/document.js:1747:19
    at handleError (/Users/Vagor/Desktop/easy_movie/node_modules/hooks-fixed/hooks.js:40:22)
    at _next (/Users/Vagor/Desktop/easy_movie/node_modules/hooks-fixed/hooks.js:46:22)
    at fnWrapper (/Users/Vagor/Desktop/easy_movie/node_modules/hooks-fixed/hooks.js:186:18)
    at /Users/Vagor/Desktop/easy_movie/node_modules/mongoose/lib/schema.js:234:13
    at complete (/Users/Vagor/Desktop/easy_movie/node_modules/mongoose/lib/document.js:1131:7)
    at /Users/Vagor/Desktop/easy_movie/node_modules/mongoose/lib/document.js:1160:20
    at ObjectId.SchemaType.doValidate (/Users/Vagor/Desktop/easy_movie/node_modules/mongoose/lib/schematype.js:654:22)
    at /Users/Vagor/Desktop/easy_movie/node_modules/mongoose/lib/document.js:1156:9
    at nextTickCallbackWith0Args (node.js:433:9)
    at process._tickCallback (node.js:362:13)
出错的区块代码
app.post('/admin/movie/new', function(req, res) {
  var id = req.body.movie._id
  var movieObj = req.body.movie
  var _movie
 
  if (id !== 'undefined') {
    Movie.findById(id, function(err, movie) {
      if (err) {
        console.log(err)
      }
 
      _movie = _.extend(movie, movieObj)
      _movie.save(function(err, movie) {
        if (err) {
          console.log(err)
        }
 
        res.redirect('/movie/' + movie._id)
      })
    })
  }
  else {
    _movie = new Movie({
      doctor: movieObj.doctor,
      title: movieObj.title,
      country: movieObj.country,
      language: movieObj.language,
      year: movieObj.year,
      poster: movieObj.poster,
      summary: movieObj.summary,
      flash: movieObj.flash
    })
 
    _movie.save(function(err, movie) {
      if (err) {
        console.log(err)
      }
 
      res.redirect('/movie/' + movie._id)
    })
  }
})
整个app.js代码
var express = require('express')
var port = 3000
var path = require('path')
var bodyParser = require('body-parser')
var _ = require('underscore')
var mongoose = require('mongoose')
var Movie = require('./models/movie.js')
var app = express()


mongoose.connect('mongodb://localhost/imooc')

app.set('views','./views/pages')
app.set('view engine','jade')
// app.use(express.bodyParser())
// app.use(bodyParser.urlencoded())
app.use(bodyParser.urlencoded({ extended: true }))//将表单数据格式化
app.use(express.static(path.join(__dirname,'bower_components')))//告诉浏览器请求样式就到此文件夹查找,dirname就是当前目录
app.listen(port)
app.locals.moment = require('moment')

console.log('imooc started on port:' + port)

//index page
app.get('/',function(req,res){
    Movie.fetch(function(err,movies){
        if (err) {
            console.log(err)
        }
    
        res.render('index',{
            title:'imovie 首页',
            movies:movies
        })
    })
})

//detail page
app.get('/movie/:id',function(req,res){
    var id = req.params.id

    Movie.findById(id, function(err,movie){
        if (err) {
            console.log(err)
        }
        res.render('detail',{
            title:'imovie' + movie.title,
            movie:movie
        })
    })
    
})

//admin page
app.get('/admin/movie',function(req,res){
    res.render('admin',{
        title:'imovie 后台录入页',
        movie:{
            title: '',
            doctor: '',
            country: '',
            year: '',
            language: '',
            summary: '',
            poster: '',
            flash: ''
        }
    })
})
//admin update movie
app.get('/admin/update/:id',function(req,res){
    var id = req.params.id

    if (id) {
        Movie.findById(id,function(err,movie){
            res.render('admin',{
                title:'imovie 后台更新页',
                movie:movie
            })
        })
    }
})
//admin post movie
// admin post movie
app.post('/admin/movie/new', function(req, res) {
  var id = req.body.movie._id
  var movieObj = req.body.movie
  var _movie
 
  if (id !== 'undefined') {
    Movie.findById(id, function(err, movie) {
      if (err) {
        console.log(err)
      }
 
      _movie = _.extend(movie, movieObj)
      _movie.save(function(err, movie) {
        if (err) {
          console.log(err)
        }
 
        res.redirect('/movie/' + movie._id)
      })
    })
  }
  else {
    _movie = new Movie({
      doctor: movieObj.doctor,
      title: movieObj.title,
      country: movieObj.country,
      language: movieObj.language,
      year: movieObj.year,
      poster: movieObj.poster,
      summary: movieObj.summary,
      flash: movieObj.flash
    })
 
    _movie.save(function(err, movie) {
      if (err) {
        console.log(err)
      }
 
      res.redirect('/movie/' + movie._id)
    })
  }
})

//list page
app.get('/admin/list',function(req,res){
    Movie.fetch(function(err,movies){
        if (err) {
            console.log(err)
        }

        res.render('list',{
            title:'imovie 列表页',
            movies:movies
        })
    })
})

//list delete movie
app.delete('/admin/list',function(req,res){
    var id = req.query.id

    if (id) {
        Movie.remove({_id:id},function(err,movie){
            if (err) {
            console.log(err)
            }

            else{
                res.json({success:1})
            }
        })
    };
})

情各位指出错误的根源并指正,谢谢!

问题休正及补充

当我submit(post方式)表单数据为数字时,可以提交并返回,但仍有文件传输错误。
图片描述

但是当我提交的是字符串(或者说是中文时,我不知道是什么原因),就会产生上述错误,服务器被强制关闭
图片描述
图片描述

我已经将代码上传至github,希望各位能帮我查看一下问题

回复
阅读 7.4k
8 个回答

除了bodyParser(urlencoded()),还需要一个bodyParser(json()),把Express升级到4.x使用生成器生成一个项目就知道了。

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

雨碎江南
  • 1.8k

看了下代码逻辑上没有问题,但是缺少安全验证.主要问题是你发送请求的id不对啊,正确的Object_id的格式是24个字符的16进制数,例如:568761de48b492fd35046a45.程序崩溃的原因就是没有做url地址栏的校验.因为在正常情况下,客户端传递的id参数是从后台获取的ObjectId,但是为了严谨性现在做以下处理:

 var id = req.params.id;

 if (id.match(/^[0-9a-fA-F]{24}$/)) {
   Notice.findById(id, function(err, notice) {
     if (err) {
       res.json({
         no: 0,
         msg: '获取失败:' + err
       });
     } else {
       var result = {
         no: 1
       };
       result.obj = notice;
       res.json(result);
     }
   });
 } else {
   res.json({
     error: true,
     msg: id + '不存在'
   });
 }
ifaaron
  • 0
新手上路,请多包涵

遇到一模一样的问题了,请问你是怎么解决啊,困扰我两天了

楼主报的错是 [{ [ValidationError: Movie validation failed],传入的数据跟没没有通过 Movie 的属性验证。

楼主在写路由的时候,如果有 err,请务必要使用 return,比如楼主 _movie.save(),需要写成下面的形式,否则出现错误会把自己看懵掉。

_movie.save(function(err, movie) {
    if (err) return console.log(err);

    //    ...
});
bamking
  • 76

mogoose的 model.save(function(err){}) 的回调参数只有 err 一个吧

http://mongoosejs.com/docs/models.html Constructing documents这一节

_id在new的时候已经生成了。
所以写成下面这样:

_movie.save(function(err) {
  if (err) {
    console.log(err)
    // 这边应该跳转至错误页面
    return res.redirect('/some/error/handler/page/' + err + JSON. stringify(_movie));
  }

  res.redirect('/movie/' + _movie._id);
})

不知道题主解决问题了没~
我也遇到了相同的问题。
不过最后发现是admin.jade的缩进有问题
如果题主还没解决问题的话可以试一下(●'◡'●)

其实你可以不用着急贴一大堆代码,先追踪下你的数据就知道了哪里有问题了。错误提示很明显是你用的东西没有_id这个字段,那你就查看下你当前用的东西到底是什么就可以了。当数据从数据库出来就开始追踪,控制台输出看下数据结构和类型。然后每次对数据有操作都看一下数据的结构和类型是否变化了。最后只要追踪到你出问题的地方就可以了,看看输出的到底是不是你想要的东西。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏