node express4.0 遇到一个偶发的问题,post有时会得到404 not found报错
发布于 9 年前 作者 tonyzhan 12440 次浏览 最后一次编辑是 8 年前 来自 问答

在我编程OA系统中出现了一个偶发的问题,让我头痛。一个简单的数据update功能,在大多数情况下工作正常,偶尔会发生提交post数据不成功,浏览器报404 Not Found, 但nodejs没有报错,log里也没有记录。为此,我升级了mongodb到3.0, 也升级express到4.12.3,但这个问题还是存在。请各位高手帮忙诊断一下:

以下是Chrome控制台的记录404 Not Found信息:

Headers
General
Remote Address:123.56.132.188:2000
Request URL:http://ff.yinova.cn:2000/kafapiaoDetail
Request Method:POST
Status Code:404 Not Found

Response Headers
Connection:keep-alive
Content-Length:28
Content-Type:text/html; charset=utf-8
Date:Sun, 03 May 2015 03:07:38 GMT
ETag:W/"vXOLRVt8K03ixCcpAP5ICQ=="
X-Content-Type-Options:nosniff
X-Powered-By:Express

Request Headers
POST /kafapiaoDetail HTTP/1.1
Host: ff.yinova.cn:2000
Connection: keep-alive
Content-Length: 588
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Origin: http://ff.yinova.cn:2000
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://ff.yinova.cn:2000/k/554203dba9e620351d690c70
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.6
Cookie: connect.sid=s%3AJqC8e7eTZtAFxqRmX5juQ5t0.KkHhy5TSbYJF1stJm8zZGpP6sfyP2J92t2MN7a7u2tY

Form Data
item:XXXX投资有限公司
unit:会议费
price:343177
client:XX
address:北京市朝阳区XXXXXXXXXXXXXXX层
remark:发票备注
company:1
status:5
bank:1

过了一分钟,我再次提交这个数据,就成功了。 以下是Chrome控制台的记录信息:

Headers

General
Remote Address:123.56.132.188:2000
Request URL:http://ff.yinova.cn:2000/kafapiaoDetail
Request Method:POST
Status Code:302 Moved Temporarily

Response Headers
Connection:keep-alive
Content-Length:68
Content-Type:text/html; charset=utf-8
Date:Sun, 03 May 2015 03:24:28 GMT
Location:/kList
Vary:Accept
X-Powered-By:Express

Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate
Accept-Language:zh-CN,zh;q=0.8,zh-TW;q=0.6
Cache-Control:max-age=0
Connection:keep-alive
Content-Length:588
Content-Type:application/x-www-form-urlencoded
Cookie:connect.sid=s%3AJqC8e7eTZtAFxqRmX5juQ5t0.KkHhy5TSbYJF1stJm8zZGpP6sfyP2J92t2MN7a7u2tY
Host:ff.yinova.cn:2000
Origin:http://ff.yinova.cn:2000
Referer:http://ff.yinova.cn:2000/k/554203dba9e620351d690c70
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36

Form Data
item:XXXX投资有限公司
unit:会议费
price:343177
client:XX
address:北京市朝阳区XXXXXXXXXXXXXXX层
remark:发票备注
company:1
status:5
bank:1
oid:554203dba9e620351d690c70

router.js 的post代码:

router.post("/kafapiaoDetail",function(req,res) {
  var currentUser = req.session.user;
  var t = new Date();
  var y = t.getFullYear();
  var m = t.getMonth()+1;
  var d = t.getDate();
  var h = t.getHours();
  if (h < 10) {h = '0'+h;}
  var minute = t.getMinutes();
  if (minute<10) { min = '0'+min;}
  var insert_time = y + '-' + m + '-' +d +' '+ h +':'+minute;
  var status = req.body.status;
  var auditor = null;
  var auditor_time = null;
  var accountant = null;
  var accountant_time = null;
  if(currentUser.type === '3') {
    auditor = currentUser.username;
    auditor_time = insert_time;
  }
  if(currentUser.type === '2') {
    accountant = currentUser.username;
    accountant_time = insert_time;
  }
  var newAccount = {};
  newAccount.oid = req.body.oid;
  newAccount.item = req.body.item;
  newAccount.price = req.body.price;
  newAccount.unit = req.body.unit;
  newAccount.company = req.body.company;
  newAccount.status = status;
  newAccount.remark = req.body.remark;
  newAccount.address = req.body.address;
  newAccount.mobile = req.body.mobile;
  newAccount.client = req.body.client;
  newAccount.username = currentUser.username;
  newAccount.auditor = auditor;
  newAccount.auditor_time = auditor_time;
  newAccount.accountant = accountant;
  newAccount.accountant_time = accountant_time;
  newAccount.bank =req.body.bank;
  console.log('kafapiaoDetail Update:');  
  console.log(newAccount); 
  Accounting.update(newAccount.oid, newAccount, function(err, result) {
      console.log('update result:');
      if (err) {
        req.flash('error', err);
        return res.redirect('/k/'+newAccount.oid); 
      }
      if (result) {
        req.flash('success','数据已更新!');
        return res.redirect('/kList');
      }
      req.flash('error', '数据更新失败,请稍后再试!');
      res.redirect('/k/'+newAccount.oid); 
  });   
});

以下是jade页面代码:

extends bloglayout
block bcontent
  include narbar.jade
  include alert.jade
  form(method='post' role='form' action='/kafapiaoDetail')
    h2.form-signin-heading 发票审批
    - if(account.length>0||typeof(account) != 'undefined')
      - for(var i=0; i<account.length; i++)
        div.input-group  
          span.input-group-addon 抬头
          input(id='item' name='item' type='text' class='form-control' value=account[i].item)
        div.input-group  
          span.input-group-addon 项目
          input(id='unit' name='unit' type='text' class='form-control' value=account[i].unit)
        div.input-group  
          span.input-group-addon 金额
          input(id='price' name='price' type='text' class='form-control' value=account[i].price)
        div.input-group  
          span.input-group-addon 收件人
          input(id='client' name='client' type='text' class='form-control' value=account[i].client)
        div.input-group  
          span.input-group-addon 手机
          input(id='mobile' name='mobile' type='text' class='form-control' value=account[i].mobile)
        div.input-group  
          span.input-group-addon 地址
          input(id='address' name='address' type='text' class='form-control' value=account[i].address)
        div.input-group  
          span.input-group-addon 备注
          input(id='remark' name='remark' type='text' class='form-control' value=account[i].remark)
        div.input-group  
          span.input-group-addon 公司
          select(id='company' name='company' class='form-control')
            - if(account[i].company === '1')
              option(value='0') 请选择
              option(value='1'  selected='selected') 云动
              option(value='2') 会贰
              option(value='3') 会小二
              option(value='4') 会万
            - else if(account[i].company === '2')
              option(value='0') 请选择
              option(value='1') 云动
              option(value='2'  selected='selected') 会贰
              option(value='3') 会小二
              option(value='4') 会万
            - else if(account[i].company === '3')
              option(value='0') 请选择
              option(value='1') 云动
              option(value='2') 会贰
              option(value='3' selected='selected') 会小二
              option(value='4') 会万
            - else if(account[i].company === '4')
              option(value='0') 请选择
              option(value='1') 云动
              option(value='2') 会贰
              option(value='3') 会小二
              option(value='4' selected='selected') 会万
            - else
              option(value='0' selected='selected') 请选择
              option(value='1') 云动
              option(value='2') 会贰
              option(value='3') 会小二
              option(value='4') 会万
        div.input-group  
          span.input-group-addon 状态
          select(id='status' name='status' class='form-control')
            - if(account[i].status === '1')
              option(value='0') 未开票取消
              option(value='2') 已开票
              option(value='3') 当月退票
              option(value='4') 跨月退票
              //- if(user.type === '3')
              option(value='1' selected='selected') 新建申请
              option(value='5') 已审批
            - if(account[i].status === '5')
              option(value='0') 未开票取消
              option(value='2') 已开票
              option(value='3') 当月退票
              option(value='4') 跨月退票
              //- if(user.type === '3')
              option(value='1') 新建申请
              option(value='5' selected='selected') 已审批
            - if(account[i].status === '2')
              option(value='0') 未开票取消
              option(value='2' selected='selected') 已开票
              option(value='3') 当月退票
              option(value='4') 跨月退票
              //- if(user.type === '3')
              option(value='1') 新建申请
              option(value='5') 已审批       
            - if(account[i].status === '3')
              option(value='0') 未开票取消
              option(value='2') 已开票
              option(value='3' selected='selected') 当月退票
              option(value='4') 跨月退票
              //- if(user.type === '3')
              option(value='1') 新建申请
              option(value='5') 已审批
            - if(account[i].status === '4')
              option(value='0') 未开票取消
              option(value='2') 已开票
              option(value='3') 当月退票
              option(value='4' selected='selected') 跨月退票
              //- if(user.type === '3')
              option(value='1') 新建申请
              option(value='5') 已审批            
            - if(account[i].status === '0')
              option(value='0' selected='selected') 未开票取消
              option(value='2') 已开票
              option(value='3') 当月退票
              option(value='4') 跨月退票
              //- if(user.type === '3')
              option(value='1') 新建申请
              option(value='5') 已审批     
        div.input-group  
          span.input-group-addon 类型
          select(id='bank' name='bank' class='form-control')
            - if(account[i].bank === '2')
              option(value='1') 增值税普票
              option(value='2' selected='selected') 增值税专票
            - else
              option(value='1' selected='selected') 增值税普票
              option(value='2') 增值税专票
        input(type='text' class='input_hide' id='oid' name='oid' value=account[i]._id readonly hidden)
        button(class='btn btn-lg btn-primary' type='submit') 修改
        a(href='/kList' class='btn btn-default' type='button') 返回
8 回复

检查一下 router.post("/kafapiaoDetail",function(req,res) {}); 这一部分是在什么时候执行的,有没有可能程序启动后没有立即执行(如果它没被执行,那么 /kafapiaoDetail 这个路由肯定没被注册,那么访问就会返回 404。 另外检查 router 是一 express() 返回的对象还是 eexpress.Router() 返回返回的对象?如果是后者,那么是在什么时候执行 app.use(router) 的?

@leizongmin 以下是我的app.js的代码:

var fs = require('fs');
var accessLogfile = fs.createWriteStream('access.log', {flags: 'a'});
var errorLogfile = fs.createWriteStream('error.log', {flags: 'a'});
var express = require('express');
var path = require('path');
var favicon = require('static-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session    = require('express-session');
var MongoStore = require('connect-mongo')(session);
var settings = require('./settings');
var flash = require('connect-flash');
var routes = require('./routes/index');
var users = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(favicon());
//app.use(logger('dev'));
app.use(logger({stream: accessLogfile}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());


//cookie解析的中间件
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(flash());

//提供session支持
app.use(session({
  secret: settings.cookieSecret,
  store: new MongoStore({
      db: settings.db,
  })
}));


app.use(function(req, res, next){
  console.log("app.usr local");
  res.locals.user = req.session.user;
  res.locals.post = req.session.post;
  var error = req.flash('error');
  res.locals.error = error.length ? error : null;
 
  var success = req.flash('success');
  res.locals.success = success.length ? success : null;
  next();
});


app.use('/', routes);
if (!module.parent) {
  app.listen(2000);
  console.log("Express服务器启动, 开始监听2000端口");
}

app.use('/users', users);


/// catch 404 and forward to error handler
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

/// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
    app.use(function(err, req, res, next) {
        var meta = '[' + new Date() + ']' +req.url + '\n';
        errorLogfile.write(meta +err.stack + '\n');
        next();
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
    var meta = '[' + new Date() + ']' +req.url + '\n';
    errorLogfile.write(meta +err.stack + '\n');
    next();
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});


module.exports = app;

楼主下次记得学会Markdown语法怎么贴代码

@leizongmin 如果/kafapiaoDetail 这个路由肯定没被注册,那么这应该是一个可以被重现的问题。但这是偶尔才发生的问题,在大部分情况下,都是运行正常。

一时看不出是啥问题

问题解决了, 是一个变量没有先声明造成的。奇怪的是,node没有报错。

@tonyzhan 那怎么会是偶发的呢?

这是让我想不明白的地方。

@tonyzhan 有句老话,不明不白消失的,还会不明不白回来的 :)

回到顶部