【问题标题】:Retrieving multiple JSON objects from a database results in Express error从数据库中检索多个 JSON 对象会导致 Express 错误
【发布时间】:2019-08-09 18:49:20
【问题描述】:

我正在使用私有 api 密钥从外部数据库中检索 JSON 对象。我使用 express 作为我的 HTTP。我正在构建一个营养应用程序,它收集用户界面输入给出的食物列表。当返回多个食物对象时,会发生错误,即我无法在将标头发送到客户端后设置标头。我的目标是在用户输入食物时有一个食物列表的下拉列表。

我尝试只限制一种食物对象的输出;没有错误发生。我尝试将食物对象设置为单独的对象数组,但错误仍然存​​在。如果要发生先前的结果以防止执行其他快速路由,我已确保使用 return 仅捕获一个响应。不确定此处的添加图片按钮在哪里,但这里是 fetchFoods.js 中输出的示例:

{ foodName: 'ARMADILLO EGGS, UPC: 732170002859',
  foodNum: '45220998' }
{ foodName: 'ARMADILLO EGGS, UPC: 732170003856',
  foodNum: '45201506' }
{ foodName: 'ARTESANIA, TOASTED ALMOND EGG NOUGHT, UPC: 815961010209',
  foodNum: '45161494' }
...

但是,当从 index.js 执行时,就会出现错误。当提到我提到的错误时,只有在 index.js 处执行 fetchFoods.js 时。否则,我可以直接从 fetchFoods.js 文件中从数据库中输出多个食物对象。

_http_outgoing.js:482
    throw new ERR_HTTP_HEADERS_SENT('set');
    ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:482:11)

就限制一种食物结果而言,从 fetchFoods.js 或 index.js 执行都不会发生错误。我认为我的问题在于我的快速配置。我可以在我的终端和控制台中查看食物结果(仅限于一个),但如果有多个,则不能。

编辑

这是原始 JSON 对象:

list": {
"q": "eggs",
"sr": "1",
"ds": "any",
"start": 0,
"end": 25,
"total": 1956,
"group": "",
"sort": "n",
"item": [
{
"offset": 0,
"group": "Branded Food Products Database",
"name": "AHOLD, DEVILED EGG POTATO SALAD, UPC: 688267141584",
"ndbno": "45044170",
"ds": "LI",
"manu": "Ahold USA, Inc."
},

profile.hbs

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <link rel="icon" href="/img/app_logo.png"/>
        <link rel="stylesheet" href="/css/styles.css">
        <title>Profile</title>
    </head>
    <body>
        <div class="main-content">
            {{>header}}

            <p>{{title}}</p>
            <p>{{greeting}}</p>

            <form>
                <input placeholder="Search for foods">
                <button>Search</button>
            </form>
        </div>       

        {{>footer}}
        <script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/3.0.1/mustache.min.js"></script>
        <script src="/js/app.js"></script>
    </body>
</html>

index.js

app.get('/fetchFoods', (req, res, next) => {
    if (!req.query.food){
        return res.send({
            error: 'You must provide a food!'
        });
    }
    next();
     fetchFoods(req.query.food, (error, {foodName, foodNum} = {}) => {
        if (error) {
            return res.send({error});
        } else {
            return res.send({
                foodName,
                foodNum
            });
        }
    }); 
});

app.js

const foodForm = document.querySelector('form');
const search = document.querySelector('input');

foodForm.addEventListener('submit', (e) => {
    console.log('testing');
    e.preventDefault();

    const food = search.value;

    fetch('http://localhost:3000/fetchFoods?food=' + food).then((response) => {
        response.json().then((data) => {
            if (data.error) {
                console.log(data.error);
            } else {
                console.log(data.foodName);
                console.log(data.foodNum);
            }
        }).catch(err => {
            console.log("Error Reading data " + err);
        });
    });
});

app.listen(port, () => console.log(`Listening on port ${port}`));

fetchFoods.js

const request = require('request');


const fetchFoods = (food, callback) => {
    const url = 'https://api.nal.usda.gov/ndb/search/?format=json&q=' + encodeURIComponent(food) + '&sort=n&max=25&offset=0&api_key=###';

    request({url:url, json:true}, (error, {body}) => {

        if (error) {
            callback('Unable to connect to USDA database!', undefined);
        } else if (body.errors) {
            callback(body.errors.error[0].message);
        }

        var foodItem = body.list.item;

        for (var i = 0; i < foodItem.length; i++){          
            callback(undefined, {
                foodName: foodItem[i].name,
                foodNum: foodItem[i].ndbno
            });  
        }        
    });
};

fetchFoods('eggs', (error, data) => {
    if (error) {
        return console.log(error);
    } else {
        return console.log(data);
    }
});

module.exports = fetchFoods;

package.json

{
  "name": "nutrition-app-heroku",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "nodemon src/index.js -e js,hbs",
    "dev": "nodemon src/index.js -e js,hbs"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "hbs": "^4.0.4",
    "mongodb": "^3.2.7",
    "mongoose": "^5.6.4",
    "mustache": "^3.0.1",
    "request": "^2.88.0",
    "validator": "^11.1.0"
  },
  "devDependencies": {
    "nodemon": "^1.19.1"
  }
}

【问题讨论】:

    标签: javascript node.js express request handlebars.js


    【解决方案1】:

    错误表示你多次调用res.send,只能发送一次。

    我认为错误在这里:

    for (var i = 0; i < foodItem.length; i++){          
        callback(undefined, {
                 foodName: foodItem[i].name,
                 foodNum: foodItem[i].ndbno
        });  
    } 
    

    你应该把对象发回:

    callback(undefined, foodItem);
    

    然后将 foodItems 发送给客户端:

    fetchFoods(req.query.food, (error, foodItems) => {
            if (error) {
                return res.send({error});
            } else {
                res.send(foodItems);
            }
        }); 
    

    【讨论】:

    • 我以前试过这个方法。我专门针对 JSON 列表中的 name 和 ndbno,因此针对 foodName 和 foodNum。当我自定义 fetchFoods.js 以仅从结果中获取第一个对象时,这两个对象都被成功检索。但是,当我遍历 JSON 列表并尝试请求多个结果并从 index.js 执行时;那是一个问题。顺便说一句,根据您建议的更改,我的控制台中的错误显示: profile:1 Uncaught (in promise) SyntaxError: Unexpected token
    【解决方案2】:

    注释 index.js 文件中的所有 res.sends 并编写 console.log('sth') 以便您知道正在执行哪个代码块。当服务器出于某种原因同时执行两个 res 方法(即发送两个响应)时,就会发生这种情况。因为这个快递必须再次设置标题,你不知道它应该是怎样的。在通常的 HTTP 请求响应周期中,客户端向服务器发送请求,服务器处理该请求并发送响应,仅此而已。

    你在 fetch foods 函数中使用了一个 for 循环,里面是回调函数。所以对于每个循环迭代,回调被再次调用,浏览器必须再次发送响应。应该是这样的。试试看,看看它是否有效。我经常遇到这个 set headers 错误,这是我找到的唯一原因。

    【讨论】:

    • 试过了,在我的 index.js 文件中注释了所有 res.sends。同样的错误。有趣的是,当从 fetchFoods.js 中取消注释 fetchFoods() 时,命令行终端会在执行 index.js 时显示食物的 json 列表。当单击我网页上的按钮时,出现“错误 [ERR_HTTP_HEADERS_SENT]:在将标头发送到客户端后无法设置标头”。我在 app.js 上添加了一个 catch 块,控制台显示:Error Reading data SyntaxError: Unexpected token
    • 顺便说一下,通过注释我的 index.js 中的所有 res.sends,我只指 /fetchFoods 路由。 index.js 文件中还有其他路由,其中​​有 res.sends。由于 /fetchFoods 中的 fetchFoods() 期间其他路线不受影响,我觉得没有必要提及。但为了澄清起见,我会提出来。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-03
    • 2020-06-08
    • 2022-10-05
    • 2022-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多