【问题标题】:Node/Express.js : Can\'t set headers after they are sentNode/Express.js:发送后无法设置标头
【发布时间】:2016-09-19 13:39:24
【问题描述】:

我正在开发一个小型节点和 express.js 应用程序。 我发现在我的app.get('/lastName/:lastName',(req,res) =>{...}); 如果我使用:

res.render('employeeList',{data:employees.lookupByLastName(paramsLastName)});

总是和res.format({...});冲突 它给了我一个错误:

throw new Error('Can\'t set headers after they are sent.');

当我单独使用它们时,在http://localhost:3000/lastName/Smith 我可以得到正确的视图。如果我只使用res.format({...});,我也可以通过以下方式获得正确的api反馈:

curl -X GET -H "Accept:application/xml" "http://localhost:3000/lastName/Smith"

但是,我不能同时使用它们,这与分配要求相冲突。 有人可以给我一些线索吗?太感谢了!请看下面的代码:

'use strict';
const express = require('express');
const _= require('underscore');
const handlebars = require('express-handlebars');
const employees = require('./employeeModule.js');
const bodyParser = require('body-parser');
const app = express();

app.engine('handlebars',
    handlebars({defaultLayout: 'main'}));

app.set('view engine', 'handlebars');

app.use(express.static(__dirname + '/public'));
app.use(bodyParser.urlencoded({extended:false}));
app.use(bodyParser.json());

// GET request to the homepage
app.get('/', (req, res) => {
    res.render('home');
});
app.get('/addEmployee',(req,res) => {
    res.render('newEmployee');
});
//..........................Problem here.........................
app.get('/id/:id',(req,res)=>{
    let paramsId = parseInt(req.params.id);
    //res.render('employeeList',{data:employees.lookupById(paramsId)});
    //res.send(employees.lookupById(paramsId));
    res.format({
        'application/json': () => {
            res.json(employees.lookupById(paramsId));
        },
        'application/xml': () => {
            let employeeXml =
                '<?xml version="1.0"?>\n<employees>\n' +
                employees.lookupById(paramsId).map((e)=>{
                    return ' <employee id="' + e.id + '">' +
                        '<firstName>' + e.firstName + '</firstName>'+ '<lastName>' + e.lastName + '</lastName>' + '</employee>';
                }).join('\n') + '\n</employees>\n';
            res.type('application/xml');
            res.send(employeeXml);
        },
        'text/html': () => {
            let employeeHtml = '<ul>\n' +
                employees.lookupById(paramsId).map((e)=>{
                    return ' <li>' + e.id + ' - ' +
                        e.firstName + ' - ' + e.lastName+'</li>';
                }).join('\n') + '\n</ul>\n';

            res.type('text/html');
            res.send(employeeHtml);
        },
        'text/plain': () => {
            let employeeText =
                employees.lookupById(paramsId).map((e)=>{
                    return e.id + ': ' + e.firstName + e.lastName;
                }).join('\n') + '\n';
            res.type('text/plain');
            res.send(employeeText);
        },
        'default': () => {
            res.status(404);
            res.send("<b>404 - Not Found</b>");
        }
    });

});
//..........................Problem here.........................
app.get('/lastName/:lastName',(req,res) =>{
    let paramsLastName  = req.params.lastName;
    res.render('employeeList',{data:employees.lookupByLastName(paramsLastName)});
    res.format({
        'application/json': () => {
            res.json(employees.lookupByLastName(paramsLastName));
        },
        'application/xml': () => {
            let employeeXml =
                '<?xml version="1.0"?>\n<employees>\n' +
                employees.lookupByLastName(paramsLastName).map((e)=>{
                    return ' <employee id="' + e.id + '">' +
                        '<firstName>' + e.firstName + '</firstName>'+ '<lastName>' + e.lastName + '</lastName>' + '</employee>';
                }).join('\n') + '\n</employees>\n';
            res.type('application/xml');
            res.send(employeeXml);
        },
        'text/html': () => {
            let employeeHtml = '<ul>\n' +
                employees.lookupByLastName(paramsLastName).map((e)=>{
                    return ' <li>' + e.id + ' - ' +
                        e.firstName + ' - ' + e.lastName+'</li>';
                }).join('\n') + '\n</ul>\n';

            res.type('text/html');
            res.send(employeeHtml);
        },
        'text/plain': () => {
            let employeeText =
                employees.lookupByLastName(paramsLastName).map((e)=>{
                    return e.id + ': ' + e.firstName + e.lastName;
                }).join('\n') + '\n';
            res.type('text/plain');
            res.send(employeeText);
        },
        'default': () => {
            res.status(404);
            res.send("<b>404 - Not Found</b>");
        }
    });
});

app.post('/data',function (req,res) {
    let bodyData = req.body;
    let bodyDataFirstName  = bodyData.firstName;
    let bodyDataLastName  = bodyData.lastName;
    employees.addEmployee(bodyDataFirstName,bodyDataLastName);
    res.redirect('/lastName/'+bodyDataLastName);
})
app.get('/api/employees',(req,res) =>{
    res.format({
        'application/json': () => {
            res.json(employees.getAllEmployee());
        },
        'application/xml': () => {
            let employeeXml =
                '<?xml version="1.0"?>\n<employees>\n' +
                employees.getAllEmployee().map((e)=>{
                    return ' <employee id="' + e.id + '">' +
                        e.firstName + e.lastName + '</employee>';
                }).join('\n') + '\n</employees>\n';
            res.type('application/xml');
            res.send(employeeXml);
        },
        'text/html': () => {
            let employeeHtml = '<ul>\n' +
                employees.getAllEmployee().map((e)=>{
                    return ' <li>' + e.id + ' - ' +
                        e.firstName + ' - ' + e.lastName+'</li>';
                }).join('\n') + '\n</ul>\n';

            res.type('text/html');
            res.send(employeeHtml);
        },
        'text/plain': () => {
            let employeeText =
                employees.getAllEmployee().map((e)=>{
                    return e.id + ': ' + e.firstName + e.lastName;
                }).join('\n') + '\n';
            res.type('text/plain');
            res.send(employeeText);
        },
        'default': () => {
            res.status(404);
            res.send("<b>404 - Not Found</b>");
        }
    });
});


app.use((req, res) => {
    res.status(404);
    res.render('404');
});


app.listen(3000, () => {
    console.log('http://localhost:3000');
});


/*
 curl -X GET "http://localhost:3000/api/employees"

 curl -X GET -H "Accept:application/json" "http://localhost:3000/api/employees"

 curl -X GET -H "Accept:application/xml" "http://localhost:3000/api/employees"

 curl -X GET -H "Accept:text/html" "http://localhost:3000/api/employees"

 curl -X GET -H "Accept:text/plain"   "http://localhost:3000/api/employees"

 */
/*
 curl -X GET "http://localhost:3000/api/employees"

 curl -X GET -H "Accept:application/json" "http://localhost:3000/lastName/Smith"
 curl -X GET -H "Accept:application/xml" "http://localhost:3000/lastName/Smith"

 curl -X GET -H "Accept:application/json" "http://localhost:3000/id/2"
 curl -X GET -H "Accept:application/xml" "http://localhost:3000/id/2"

 */

【问题讨论】:

    标签: node.js express


    【解决方案1】:

    您发送了两次响应。

    您的res.render 呈现employeeList,然后将其作为响应发送。之后您尝试发送另一个响应,这是不允许的,并且不符合请求-响应周期。

    此外,您正在使用模板引擎来呈现您的页面:

    let paramsLastName  = req.params.lastName;
    res.render('employeeList',{data:employees.lookupByLastName(paramsLastName)});
    

    然后您尝试在此处手动操作:

    'text/html': () => {
                let employeeHtml = '<ul>\n' +
                    employees.lookupById(paramsId).map((e)=>{
                        return ' <li>' + e.id + ' - ' +
                            e.firstName + ' - ' + e.lastName+'</li>';
                    }).join('\n') + '\n</ul>\n';
    
                res.type('text/html');
                res.send(employeeHtml);
            }
    

    您可以在 res.format 内的 'text/html' 下调用 res.render, 或在 res.format 块之外使用 app.render - 它呈现 html 但不将其作为响应发送。

    app.render('index', {data:employees.lookupByLastName(paramsLastName)}, function(err, result) {
        // result is the resulting html from rendering your data.
        // save it to some variable to use later or do something with it here
    });
    

    【讨论】:

      【解决方案2】:

      您对同一个请求发送了两次响应。

      您收到的错误消息是由于您的代码尝试对同一请求发送两个响应而导致的。因此,您必须删除代码中可能发生这种情况的所有位置。

      在您的app.get('/lastName/:lastName', ...) 处理程序中,您首先调用res.render(),然后尝试执行res.send()。但是,按照您调用res.render() 的方式,该调用已经发送了对请求的响应,因此您不能再次调用res.send()res.json(),因为这将尝试向同一请求发送另一个响应这将触发您看到的错误消息。

      如果您尝试仅呈现 HTML,然后使用该呈现的 HTML 作为稍后发送的响应的一部分,那么您需要使用不同形式的 res.render() 向其传递回调并调用您可以稍后发送 HTML。

      请参阅res.render() 的文档以了解如何使用回调选项来获取 HTML 而不将其作为响应发送,以便以后在发送要发送的响应时使用它。

      大致的方案是这样的:

      app.get('/lastName/:lastName',(req,res) =>{
          res.render(file, options, function(err, html) {
              if (!err) {
                  // response has not been sent yet
                  // put your res.format() code here and use the above html argument
              }
          })
      })
      

      【讨论】:

      • 为什么投反对票?这显然是 OP 代码中的一个问题。
      猜你喜欢
      • 1970-01-01
      • 2015-08-10
      • 1970-01-01
      • 1970-01-01
      • 2017-08-11
      • 1970-01-01
      • 2016-05-01
      • 2015-09-08
      • 2018-01-20
      相关资源
      最近更新 更多