FIREBASE警告:用户回调引发了异常。错误:发送后无法设置标头

时间:2016-02-19 14:04:47

标签: node.js rest express firebase

我们正在开发一个应用程序,我们将Firebase用作数据库,并表示作为中间件/后端,用于路由我们在Reactjs中开发的前端使用的RESTful API。

以下是我们的server.js文件:

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

//Used for getting POST variables from forms as well as query parameters
var bodyParser = require('body-parser');
var validator = require('express-validator');

//Contains all the routes
var routes = require('./routes/routes');
var path = require('path');

//Used for serving jade files
app.set('view engine', 'jade');
//For serving static resources like scripts, styleSheets, html
app.use(express.static(__dirname + '/views'));

app.all('/*', function(req, res, next) {
 res.header("Access-Control-Allow-Origin", "*");
 res.header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, Accept");
 res.header("Access-Control-Allow-Methods", "POST, GET");
 next();
});

// configure app to use bodyParser()
// this will let us get the data from a POST
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

app.use(validator({
    customValidators: {
        onlyAlphabets: function(value) {
            if (value.match('^[a-zA-Z ]*$')) {
                return true;
            } else {
                return false;
            }
        },
        password: function(value) {
            if (value.length >= 6 && value.length <=25 && value.match('^[\x20-\x7F]*$')) {
                return true;
            } else {
                return false;
            }
        }
    }
}));
app.use(routes);

var port = process.env.PORT || 8080; //Set our port

app.listen(port);
console.log('Magic happens on port ' + port);

以下是route.js中的路由代码:

var express = require('express');
var views = __dirname;
// Node.js path library - https://nodejs.org/api/path.html
var path = require('path');
var Firebase = require("firebase");
var myFirebaseRef = new Firebase("https://crackling-inferno-8454.firebaseio.com/vendor_details");
var router = express.Router();
//Password Encryption and decryption helpers
var hashFunction = require('../helpers/encrypt');


// middleware to use for all requests
router.use(function(req, res, next) {
  // do logging
  console.log('Something is happening.');
  next(); // make sure we go to the next routes and don't stop here
});

router.get('/', function(req, res) {
  res.render('vendor_form');
});

router.route('/get_vendors').get(function(request, response) {
    myFirebaseRef.on("value", function(snapshot) {
        var store = snapshot.val();
        // Looping to get the firebaseId generated while push
        for(var key in store){
            store[key].id = key;    //  Store firebaseID generated during push to store in JSON object
        }
        response.send(Object.keys(store).map(function(k) { return store[k]; }));
    }, function (errorObject) {
        response.send("The read failed: " + errorObject.code);
    });
});

router.route('/post_vendor').post(function(request, response) {

    request.checkBody({
        'vendor_name': {
            notEmpty : {
                errorMessage: 'Please enter a vendor\'s name'
            },
            onlyAlphabets : {
                errorMessage: 'Please enter only alphabets'
            }
        },
        'enterprise_name': {
            notEmpty : {
                errorMessage: 'Please enter an enterprise\'s name'
            },
            onlyAlphabets : {
                errorMessage: 'Please enter only alphabets'
            }
        },
        'vendor_email': {
            notEmpty : {
                errorMessage: 'Please enter your email address'
            },
            isEmail : {
                errorMessage: 'please enter an appropriate email format'
            }
        },
        'vendor_password': {
            notEmpty : {
                errorMessage: 'Please enter a password'
            },
            password: {
                errorMessage: 'Password length should be between 6-25 characters'
            }
        },
        'food_preference': {
            notEmpty: {
                errorMessage: 'You must select atleast one food preference'
            }
        }
    });

    var errors = request.validationErrors();

    // var onComplete = function(error) {
    //  if (error) {
    //      response.send('Failed to add stats to the database');
  //     return false;
    //  } else {
    //      // response.render('vendor_form', { success: true });
    //      response.send('Success');
  //     return true;
    //  }
    // };

    if (errors) {
        response.send(errors);
        // response.render('vendor_form', { error: errors });
        return false;
    } else {
        myFirebaseRef.push().set({
            'id': Firebase.ServerValue.TIMESTAMP,
            'vendor_name': request.body.vendor_name,
            'enterprise_name': request.body.enterprise_name,
            'vendor_email': request.body.vendor_email,
            'vendor_password': hashFunction.encrypt(request.body.vendor_password),
            'food_preference': request.body.food_preference
        }, function(err) {
      if (err) {
        response.send('Failed to add stats to the database');
      } else {
        response.send('Success');
      }
    });
    return true;
    }
});

module.exports = router;

以下是我们在前端添加的用于发布数据的代码。我们也在使用whatwg-fetch包:

httpservice.js:

var Fetch = require('whatwg-fetch');
var baseUrl = 'http://192.168.1.134:8080';

var Service = {
  get: function(url) {
    console.log('MAKING A GET REQUEST');
    return fetch(baseUrl + url)
    .then(function(response) {
      return response.json();
    });
  },

  post: function(url, postData) {
    console.log('MAKING A POST REQUEST');
    return fetch(baseUrl + url, {
      headers: {

        'Content-Type': 'application/json'
      },
      method: 'POST',
      body: JSON.stringify(postData)
    }).then(function(response) {
      return response;
    });
  }
}

module.exports = Service;

VendorForm.js(React组件文件)

HTTP.post('/post_vendor', httpRequestBody)
.then(function(response) {
      console.log(response);
 }.bind(this));

我们启动服务器,它通过 nodemon 提供在FIREBASE + EXPRESS中开发的RESTful API。这是我们发布时的错误:

FIREBASE WARNING: Exception was thrown by user callback. Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)
    at ServerResponse.header (/var/www/tutorials/express_firebase/node_modules/express/lib/response.js:718:10)
    at ServerResponse.send (/var/www/tutorials/express_firebase/node_modules/express/lib/response.js:163:12)
    at ServerResponse.json (/var/www/tutorials/express_firebase/node_modules/express/lib/response.js:249:15)
    at ServerResponse.send (/var/www/tutorials/express_firebase/node_modules/express/lib/response.js:151:21)
    at /var/www/tutorials/express_firebase/routes/routes.js:30:12
    at /var/www/tutorials/express_firebase/node_modules/firebase/lib/firebase-node.js:200:710
    at ec (/var/www/tutorials/express_firebase/node_modules/firebase/lib/firebase-node.js:52:165)
    at ac (/var/www/tutorials/express_firebase/node_modules/firebase/lib/firebase-node.js:31:216)
    at bc (/var/www/tutorials/express_firebase/node_modules/firebase/lib/firebase-node.js:30:1259)
    at Ji.h.Mb (/var/www/tutorials/express_firebase/node_modules/firebase/lib/firebase-node.js:220:440)
    at X.set (/var/www/tutorials/express_firebase/node_modules/firebase/lib/firebase-node.js:256:335)
    at /var/www/tutorials/express_firebase/routes/routes.js:96:24
    at Layer.handle [as handle_request] (/var/www/tutorials/express_firebase/node_modules/express/lib/router/layer.js:95:5)
    at next (/var/www/tutorials/express_firebase/node_modules/express/lib/router/route.js:131:13)
    at Route.dispatch (/var/www/tutorials/express_firebase/node_modules/express/lib/router/route.js:112:3) 
/var/www/tutorials/express_firebase/node_modules/firebase/lib/firebase-node.js:52
(d="0"+d),c+=d;return c.toLowerCase()}var zd=/^-?\d{1,10}$/;function td(a){return zd.test(a)&&(a=Number(a),-2147483648<=a&&2147483647>=a)?a:null}function ec(a){try{a()}catch(b){setTimeout(function(){R("Exception was thrown by user callback.",b.stack||"");throw b;},Math.floor(0))}}function S(a,b){if(t(a)){var c=Array.prototype.slice.call(arguments,1).slice();ec(function(){a.apply(null,c)})}};function Ad(a){var b={},c={},d={},e="";try{var f=a.split("."),b=Pb(id(f[0])||""),c=Pb(id(f[1])||""),e=f[2],d=c.d||{};delete c.d}catch(g){}return{oh:b,Dc:c,data:d,ah:e}}function Bd(a){a=Ad(a).Dc;return"object"===typeof a&&a.hasOwnProperty("iat")?z(a,"iat"):null}function Cd(a){a=Ad(a);var b=a.Dc;return!!a.ah&&!!b&&"object"===typeof b&&b.hasOwnProperty("iat")};function Dd(a){this.Y=a;this.g=a.n.g}function Ed(a,b,c,d){var e=[],f=[];Na(b,function(b){"child_changed"===b.type&&a.g.Ad(b.Le,b.Ma)&&f.push(new H("child_moved",b.Ma,b.Ya))});Fd(a,e,"chi

Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)
    at ServerResponse.header (/var/www/tutorials/express_firebase/node_modules/express/lib/response.js:718:10)
    at ServerResponse.send (/var/www/tutorials/express_firebase/node_modules/express/lib/response.js:163:12)
    at ServerResponse.json (/var/www/tutorials/express_firebase/node_modules/express/lib/response.js:249:15)
    at ServerResponse.send (/var/www/tutorials/express_firebase/node_modules/express/lib/response.js:151:21)
    at /var/www/tutorials/express_firebase/routes/routes.js:30:12
    at /var/www/tutorials/express_firebase/node_modules/firebase/lib/firebase-node.js:200:710
    at ec (/var/www/tutorials/express_firebase/node_modules/firebase/lib/firebase-node.js:52:165)
    at ac (/var/www/tutorials/express_firebase/node_modules/firebase/lib/firebase-node.js:31:216)
    at bc (/var/www/tutorials/express_firebase/node_modules/firebase/lib/firebase-node.js:30:1259)
    at Ji.h.Mb (/var/www/tutorials/express_firebase/node_modules/firebase/lib/firebase-node.js:220:440)
    at X.set (/var/www/tutorials/express_firebase/node_modules/firebase/lib/firebase-node.js:256:335)
    at /var/www/tutorials/express_firebase/routes/routes.js:96:24
    at Layer.handle [as handle_request] (/var/www/tutorials/express_firebase/node_modules/express/lib/router/layer.js:95:5)
    at next (/var/www/tutorials/express_firebase/node_modules/express/lib/router/route.js:131:13)
    at Route.dispatch (/var/www/tutorials/express_firebase/node_modules/express/lib/router/route.js:112:3)
[nodemon] app crashed - waiting for file changes before starting...

根据错误,我们知道某些回调是设置标题两次但不确定它是如何发生的。经过几次堆栈溢出问题,但仍未找到解决方案。任何帮助,将不胜感激。感谢您的期待。

1 个答案:

答案 0 :(得分:8)

如果您看到堆栈跟踪

at /var/www/tutorials/express_firebase/routes/routes.js:30:12

这是文件route.js中的第30行

response.send(Object.keys(store).map(function(k) { return store[k]; }));

您需要为on更改方法once,否则每次更新数据时都会触发回调函数,

router.route('/get_vendors').get(function(request, response) {
  myFirebaseRef.on("value", function(snapshot) {

在您调用post方法时中断响应,因为标题已在第30行发送。

参见参考https://firebase.google.com/docs/database/server/retrieve-data#section-reading-once

基本上你使用once方法正在做的是在读取值后立即删除回调。