接收"错误:发送后无法设置标头。"进行多次API调用时

时间:2018-04-05 00:58:31

标签: node.js api express

我正在重构一个Express项目,我需要进行多个API调用。我目前有9个单独的函数来进行每个单独的API调用,然后在每个API调用之后将其设置为res.locals。

我创建了一个包含我需要点击的所有API URL的数组,然后我有一个迭代数组的函数来进行所有的fetch调用。但是,我遇到问题设置标题,以便JSON数据通过,因为我收到以下消息:

Error: Can't set headers after they are sent.

根据我设置控制器的方式,我可以获取控制器头和第一个API响应以在我的页面上呈现,或者我只是获取控制器头来呈现。鉴于我的情况,我还没能找到解决方案,并且想知道是否有人有建议要纠正这个问题。我已经粘贴了我认为应该足够的代码进行故障排除,但是如果我能提供其他任何内容,请告诉我。

API-Helper文件(进行API调用的地方):

function getData(req, res, next) {
    for (let i = 0; i < links.length; i ++) {
        fetch(links[i])
        .then(res => res.json())
        .then(fetchRes => {
            res.locals.i = fetchRes
            next()
        })        
        .catch(err => {
            res.json({err})
        })
    }
}

以下是从第一个API调用和标题中获取数据的控制器:

for (let i = 0; i < 10; i ++) {  
cryptoController.sendApiData = (req, res) => {          
    res.json({
        message: 'data returned for crypto',            
        i: res.locals.i
    })  
    }
}

以下是控制器只返回标题:

for (let i = 0; i < 10; i ++) {  
cryptoController.sendApiData = (req, res) => {          
    res.json({
        message: 'data returned for crypto',            
        i: res.locals[i]
    })  
    }
}

更新以显示更多代码:

完全控制器:

// import model
const Crypto = require('../models/Crypto')

// initiate controller object
const cryptoController = {}

// find latest cap coin entry
cryptoController.latest = (req, res, next) => {
    Crypto.findRecent()
    .then(crypto => {
        res.json({
            message: 'retrieved entry',
            data: { crypto }
        })
    }).catch(next)
}

// send api data
// for (let i = 0; i < 10; i ++) {  
cryptoController.sendApiData = (req, res) => { 
    console.log(res.locals)         
    res.json({
        message: 'data returned for crypto',            
        i: res.locals.i
    })  
    // }
}

// create new entry
cryptoController.create = (req, res) => {
    console.log(req.body, ' req.body from cryptoController#create')
    Crypto.create({
        // time made
        time_made: Date.now(),
        // crypto data
        usd: req.body.usd,
        us_high: req.body.us_high,
        us_low: req.body.us_low,
        eur: req.body.eur,
        eur_high: req.body.eur_high,
        eur_low: req.body.eur_low,
        trades: req.body.trades,
        one_hour: req.body.one_hour,
        one_day: req.body.one_day,
        seven_days: req.body.Seven_days,
        crypto_id: req.body.crypto_id
    })
}


module.exports = cryptoController

路线档案:

// import dependencies
const express = require('express')
const cryptoController = require('../controllers/crypto-controller')
const cryptoHelpers = require('../services/crypto-helpers')

// set router variable
const cryptoRouter = express.Router()

// set routes to fetch and store API data
cryptoRouter.post('/crypto', cryptoHelpers.getData, cryptoController.create)
cryptoRouter.get('/crypto', cryptoHelpers.getData, cryptoController.sendApiData)

module.exports = cryptoRouter;

引入路由的服务器文件:

const cryptoRouter = require('./routes/crypto-routes')
app.use('/', cryptoRouter)

模型文件没有被点击,因为此时没有任何内容插入到数据库中。

1 个答案:

答案 0 :(得分:1)

好的,这里有一个问题:

function getData(req, res, next) {
    for (let i = 0; i < links.length; i ++) {
        fetch(links[i])
        .then(res => res.json())
        .then(fetchRes => {
            res.locals.i = fetchRes
            next()
        })        
        .catch(err => {
            res.json({err})
        })
    }
}

您正在循环中调用next()。完成所有操作后,您只能拨打next()一次。由于看起来这些服务器端fetch()调用可以并行执行,因此您可以使用Promise.all()查看它们何时完成。

然后,您还有将数据放入res.locals的问题。您只是在一个循环中分配相同的res.locals.i变量,而这个循环并没有真正做任何特别有用的事情。

我认为您必须为数据使用数组,然后在路由链中更改使用res.locals的代码来访问数组。

function getData(req, res, next) {
    let promises = links.map(function(link) {
        return fetch(link).then(res => res.json())
    });
    Promise.all(promises).then(results => {
        res.locals.data = results;    // res.locals.data is an array of link json
        next();
    }).catch(err => {
        res.status(500).json({err});
    });
}

然后,在您的.get()路由处理程序中,我并不确切知道您为什么要尝试实现的结果类型,但要返回一系列结果,您可以执行以下操作:

cryptoController.sendApiData = (req, res) => { 
    console.log(res.locals.data)         
    res.json({
        message: 'data returned for crypto',            
        data: res.locals.data
    }); 
}