简体   繁体   中英

where to should write res.send() in node.js app

In my app I have a category collection that saves the category title with its image ID. The image is saved in another collection with its meta data like path , type and so on. So for retrieving category I should retrieve category image in image collection by its ID and add image path to category object that is retrieved from category collection and send it to client...But I don't know where should I send categories to client? When I send the response face this error :

 throw er; // Unhandled 'error' event
      ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:561:11)
    at ServerResponse.header (H:\node.js\online-store\app\node_modules\express\lib\response.js:771:10)
    at ServerResponse.send (H:\node.js\online-store\app\node_modules\express\lib\response.js:170:12)
    at ServerResponse.json (H:\node.js\online-store\app\node_modules\express\lib\response.js:267:15)
    at ServerResponse.send (H:\node.js\online-store\app\node_modules\express\lib\response.js:158:21)
    at H:\node.js\online-store\app\controllers\pcategory.controller.js:123:19
    at H:\node.js\online-store\app\node_modules\mongoose\lib\model.js:4845:18
    at processTicksAndRejections (internal/process/task_queues.js:77:11)
Emitted 'error' event on Function instance at:
    at H:\node.js\online-store\app\node_modules\mongoose\lib\model.js:4847:15
    at processTicksAndRejections (internal/process/task_queues.js:77:11) {
  code: 'ERR_HTTP_HEADERS_SENT'
}

This is my code :

exports.getAll = async (req, res) => {
     try{
           
        const categories = await ProductCategory.find({});
   
            categories.map(async(category)=>{
             
           await File.findById(category.imageID).exec(function(err,file){
                if(err){
                    console.log(err)
                
                    
                }else if(file) {
                                            
                   category.imagePath = file.file_path;
                   tempCategories.push(category)
                                
                  
               }
                 res.send(tempCategories);
                        
            })
           
           })
         
         
           return res.send(tempCategories); 
            
    }catch {
        res.json(err =>{
            console.log(err);
            res.status(500).send({
                message:
                  err.message || "There is an error in retrieving category"
              });

    })

    }
   
}

The problem is that nothing in your code is waiting for the asynchronous operations you're doing in your map callback to complete, so it does the res.send at the end right away — and then does res.send again within the map callback later when the async operations complete. Instead, wait for them to finish and send the result.

Also, you're using res.send where I suspect you want res.json , and using res.json later incorrectly (it doesn't take a callback).

See comments:

exports.getAll = async (req, res) => {
    try {
        // Get the categories
        const categories = await ProductCategory.find({});
        // Get the files for the categories, wait for the result
        const result = await Promise.all(categories.map(async (category) => {
            const file = await File.findById(category.imageID).exec();
            // You probably can't modify the `category` object, so let's create
            // and return a new object
            return {...category, imagePath: file.file_path};
        }));
        // Send the result converted to JSON
        return res.json(tempCategories);
    } catch (err) { // Accept the error
        // Send an error response
        res.status(500).json({
            message: err.message || "There is an error in retrieving category"
        });
    }
};

Side note: Your original code was using map without using the array it creates. That's an antipattern (sadly it seems to be one someone somewhere is teaching). I wrote up why and what to do instead here . (In my update to your code, I still use map , but I use the array it creates, passing it to Promise.all so we can wait for all those promises to settle.)

You're getting this error because you called res.send() twice. It's not necessary to do that unless what you're returning undergoes some change in between the two calls pr of there's a condition to be met for the first call. If you have to call res.send() twice, make sure that you add return to the first call, so it becomes:

return res.send(data);

This way, the second call doesn't get executed as the function is immediately exited after this line.An example is shown below:

if (someConditionIsMet()) {
  return res.send(data); // Function exits after this
} 
res.send(differentData);

In your case, however, you're calling res.send() in your map fucntion, which is not something you want. All you need is to return after performing the needed opration within the map fucntion. Like so:

exports.getAll = async (req, res) => {
    try {
        const categories = await ProductCategory.find({});
        categories.map(async (category) => {
            await File.findById(category.imageID).exec(function(err, file) {
                if (err) {
                    console.log(err);
                } else if (file) {
                    category.imagePath = file.file_path;
                    tempCategories.push(category);
                }
            });
            return tempCategories; // Simply return the data here, not res.send()
        });
        res.send(tempCategories); // Send the data back to the client here
    } catch (err) {
        console.log(err);
        res.status(500).send({
            message: err.message || "There is an error in retrieving category"
        });
    }
}

Your Code Like this,

Now Issue is You are sending two times Headers.

You can use like this, Firstly Declare array and push into it what you need and then last of your logic return it or send it.

exports.getAll = async (req, res) => {
try {
    const categories = await ProductCategory.find({});
    let tempCategories = []; // New Line 
    categories.map(async (category) => {
        await File.findById(category.imageID).exec(function (err, file) {
            if (err) {
                console.log(err)
            } else if (file) {
                category.imagePath = file.file_path;
                tempCategories.push(category)
            }
            // res.send(tempCategories); // Old Line 
        })
    })
    res.send(tempCategories);
} catch {
    res.json(err => {
        console.log(err);
        res.status(500).send({
            message:
                err.message || "There is an error in retrieving category"
        });
    })
}
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM