问题
I'm trying to make a catch-all of sorts to return data to my Author endpoint. If the url that is passed to the endpoint contains no query parameters, I want the router to return the full list of authors available. If the url contains firstName and lastName parameters, I want the controller to find the authors that match and, pass that data back to the router.
Currently if I send the urls http://localhost:3001/authors or http://localhost:3001/authors?firstName=tom&lastName=dooly, I get an error Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client.
Can anyone tell me why this is happening and how to fix it?
main:
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var app = express();
var dev_db_url = 'mongodb://localhost:27017/'
var mongoDB = process.env.MONGODB_URI || dev_db_url;
mongoose.connect(dev_db_url);
mongoose.Promise = global.Promise;
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
var index = require('./routes/index');
var users = require('./routes/users');
var feedEntries = require('./routes/feedEntries');
var authors = require('./routes/authors');
app.use('/', index);
app.use('/users', users);
app.use('/feedEntries', feedEntries);
app.use('/authors', authors);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not not Found');
err.status = 404;
next(err);
});
app.use(function(err, req, res, next) {
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
route:
var express = require('express');
var router = express.Router();
var authorController = require('../controllers/authorController');
authorController.findAuthorsByFirstAndLastName);
router.get('/', function (req, res) {
if(req.query.firstName||req.query.lastName) {
res.send(authorController.findAuthorsByFirstAndLastName(req,res));
}else{
res.send(authorController.author_list(req,res));
}
});
module.exports = router;
controller:
var Author = require('../models/author')
var async = require('async')
exports.author_list = function(req, res, next) {
Author.find({},function(err, authors) {
if (err){
res.send(err);
}
return.json(authors);
});
};
exports.findAuthorsByFirstAndLastName = function (req, res, next){
var query = {}
if(req.query.firstName||req.query.lastName) {
query = {$or:[{firstName:{$regex: req.query.firstName, $options: 'i'}},
{lastName:{$regex: req.query.lastName, $options: 'i'}}]}
}
else {
return res.status(500).send({ error: 'Unable to parse data'});
}
var firstName = req.body.firstName;
var lastName = req.body.lastName;
Author.find(query , function (err, authors) {
if(err) {
res.send(err);
}
res.json(authors);
});
};
回答1:
You get cannot set headers after they are sent when you have two res.[whatever]s in your route. So you have res.send(functionCallThatAlsoDoesRes.Send). That's what's causing the error.
If you want a route to take multiple actions between the request and the response, you can write those as separate middlewares. Middlewares always take the arguments req, res, and next (a function that says to go to the next middleware in the list).
So, you might write:
authorController.findAuthorsByFirstAndLastName = function(req, res, next) {
if (!(req.query.firstName || req.query.lastName)) {
res.locals.getFullAuthorList = true
return next()
} else {
const query = /* whatever */
Author.find(query, (err, authors) => {
if (err) return next(err)
res.locals.authors = authors
next()
})
}
}
authorController.author_list = function(req, res, next) {
if (!res.locals.getFullAuthorList) return next() // if we already have authors we don't need to do anything
Author.find({}, (err, authors) => {
if (err) return next(err)
res.locals.authors = authors
next()
})
}
Then in your route, you'd say:
router.get('/', authorController.findAuthorsByFirstAndLastName, authorController.author_list, (req, res) => {
res.json({ authors: res.locals.authors })
})
If you haven't seen res.locals before, it's just a property on the response object that is available for you to attach things to. It persists throughout the request/response cycle and is cleared for each new request.
来源:https://stackoverflow.com/questions/49369121/how-do-i-pass-data-returned-from-a-controller-to-express-router