Heroku因POST请求,multer或nodemailer H = 18反复崩溃

时间:2018-07-12 15:35:31

标签: node.js heroku deployment multer nodemailer

当我发送mulitpart表单时,我在Heroku上的部署不断因POST请求而崩溃。我在日志中看不到它是在上载(multer)功能,保存(猫鼬)功能还是sendMail(nodemailer)功能上。 我在日志中唯一看到的是H18错误:内部服务器。

Router.js

const express = require("express");
const mongoose = require("mongoose");
const router = express.Router();
const multer = require("multer");
const path = require("path");
const File = require("../models/Files");
const mail = require("../handlers/mailer");

// Set storage engine
const storage = multer.memoryStorage();

// Init upload
const upload = multer({
  storage: storage
}).single("file");

router.get("/", (req, res) => {
  res.render("index");
});

router.post("/send", async (req, res, next) => {

  await upload(req, res, async err => {
    if (err) {
      console.log("error by uploading file:", err);
    } else {
      console.log(`File is uploaded to the memoryStorage: ${req.file.originalname} `);
    }

    // Create a model to save in the database
    const fileUpload = new File({
      fromEmail: "<dk@bigbrother.nl>",
      fromName: '"Dennis Klarenbeek "',
      email: req.body.email,
      subject: req.body.subject,
      msg: req.body.msg,
      filename: req.file.originalname
    });

    await fileUpload.save((err, file, rows) => {
      if (err) {
        console.log("error on saving in the db");
      } else {
        console.log(`database item has been created: ${file.filename}`);
      }
    });

    // Mail the uploaded attachment
    await mail.send({
      fromEmail: "dennis.klarenbeek@icloud.com",
      fromName: '"Dennis Klarenbeek "',
      toEmail: req.body.email,
      toName: req.body.name,
      subject: req.body.subject,
      msg: req.body.msg,
      template: "attachment",
      attachments: [
        {
          filename: req.file.filename,
          contentType: req.file.mimetype,
          content: req.file.buffer
        }
      ]
    });
  });

  res.redirect("/");
});

module.exports = router;

日志

2018-07-12T15:29:46.104415+00:00 heroku[router]: at=info method=GET path="/css/style.css" host=stormy-ocean-50061.herokuapp.com request_id=57113d1c-9730-40ca-9f41-0d5111854175 fwd="87.251.40.140" dyno=web.1 connect=1ms service=10ms status=304 bytes=237 protocol=https
2018-07-12T15:29:46.103429+00:00 heroku[router]: at=info method=GET path="/css/normalize.css" host=stormy-ocean-50061.herokuapp.com request_id=44a0f90b-1973-4daf-9f40-1e5e5398b9e4 fwd="87.251.40.140" dyno=web.1 connect=1ms service=7ms status=304 bytes=238 protocol=https
2018-07-12T15:29:46.487118+00:00 app[web.1]: [0mGET /webfonts/fa-light-300.woff2 [36m304 [0m0.353 ms - -[0m
2018-07-12T15:29:46.489183+00:00 heroku[router]: at=info method=GET path="/webfonts/fa-light-300.woff2" host=stormy-ocean-50061.herokuapp.com request_id=885eae11-3e4c-4efa-9b60-b3950d9f256d fwd="87.251.40.140" dyno=web.1 connect=1ms service=2ms status=304 bytes=239 protocol=https
2018-07-12T15:29:56.919861+00:00 app[web.1]: [0mPOST /send [36m302 [0m9.041 ms - 46[0m
2018-07-12T15:29:57.100559+00:00 heroku[router]: sock=backend at=error code=H18 desc="Server Request Interrupted" method=POST path="/send" host=stormy-ocean-50061.herokuapp.com request_id=aaafb074-b538-4983-bef1-fa1abf1f2413 fwd="87.251.40.140" dyno=web.1 connect=1ms service=191ms status=503 bytes=234 protocol=https

有人知道这可能是什么吗?

3 个答案:

答案 0 :(得分:2)

在响应完成之前销毁套接字时,抛出heroku H18错误。从heroku文档中指出:

  

通常,H18表示响应具有多个阶段-对于   例如,流式传输大量响应-而其中之一   阶段抛出了错误。”

     

https://help.heroku.com/18NDWDW0/debugging-h18-server-request-interrupted-errors-in-nodejs-applications

我们可以执行一些步骤来重构代码,因此我们将multer用作中间件并改善错误处理,从而了解错误的实际发生位置。

要捕获解决等待时引发的错误,您需要将其包装在try...catch块周围。它的工作原理与Promise.catch完全一样。

const express = require("express");
const mongoose = require("mongoose");
const router = express.Router();
const multer = require("multer");
const path = require("path");
const File = require("../models/Files");
const mail = require("../handlers/mailer");

// Set storage engine
const storage = multer.memoryStorage();

// Init upload
const upload = multer({
  storage: storage
}).single("file");

router.get("/", (req, res) => {
  res.render("index");
});

router.post("/send", async (req, res, next) => {

  // No need to await  this middleware
  upload(req, res, err => {
    // Refactor to using recommended multer error handling
    // https://github.com/expressjs/multer#error-handling
    if (err instanceof multer.MulterError) {
      // A Multer error occurred when uploading.
      console.log("multer error when uploading file:", err);
      return res.sendStatus(500);
    } else if (err) {
      // An unknown error occurred when uploading.
      console.log("unknown error when uploading file:", err);
      return res.sendStatus(500);
    }

    console.log(`File is uploaded to the memoryStorage: ${req.file.originalname} `);

    // Create a model to save in the database
    const fileUpload = new File({
      fromEmail: "<dk@bigbrother.nl>",
      fromName: '"Dennis Klarenbeek "',
      email: req.body.email,
      subject: req.body.subject,
      msg: req.body.msg,
      filename: req.file.originalname
    });

    // Try executing awaits or catch thrown errors
    try {
      await fileUpload.save((err, file, rows) => {
        if (err) {
          console.log("error on saving in the db");
        } else {
          console.log(`database item has been created: ${file.filename}`);
        }
      });

      // Mail the uploaded attachment
      await mail.send({
        fromEmail: "dennis.klarenbeek@icloud.com",
        fromName: '"Dennis Klarenbeek "',
        toEmail: req.body.email,
        toName: req.body.name,
        subject: req.body.subject,
        msg: req.body.msg,
        template: "attachment",
        attachments: [
          {
            filename: req.file.filename,
            contentType: req.file.mimetype,
            content: req.file.buffer
          }
        ]
      });

      // Return res here to signify end of function execution
      return res.redirect("/");
    } catch (err) {
      console.log('Error occured in saving to DB or with mail send ', err);
      return res.sendStatus(500);
    }
  });

});

module.exports = router;

这应该使您看到正在发生的实际错误。您当然也可以将错误发送回响应中,现在我只发送状态500来完成响应。

答案 1 :(得分:0)

在nodejs中捕获所有未捕获的异常,以更好地了解发生问题的地方。

将此行添加到您的nodejs文件中

process.on('uncaughtException', function (err) {
  console.error(err.stack); // either logs on console or send to other server via api call.
  process.exit(1)
})

答案 2 :(得分:0)

我已经意识到,大多数错误是在尝试上传大文件时发生的,尽管heroku说“上传文件的大小没有限制” ,但您的应用可能存储了大文件对于 Heroku平台上的所有请求,超出Heroku时间限制的文件必须在30秒内返回第一个字节,然后导致后端套接字关闭,该套接字属于应用程序的Web进程,位于后端之前返回了HTTP响应。

但是Heroku给出了更多可能导致该错误的原因,我建议您阅读该错误以进一步澄清H18 - Server Request Interrupted

分辨率

  

H18错误类似于H13,两者均表示   套接字在响应完成之前已被破坏。使用H13,   套接字已连接,然后销毁而不发送任何数据。 H18   表示套接字已连接,一些数据作为   应用程序响应,但随后套接字被破坏,没有   完成回复。

     

通常,H18表示响应具有多个阶段-对于   例如,流式传输大量响应-而其中之一   阶段抛出了错误。

     

要查找错误,请首先检查日志中是否有堆栈跟踪   H18。如果看不到任何内容,则需要更仔细地查看处理程序   对于失败的特定请求。记录每个步骤   响应(包括x-request-id标头)可以提供帮助。

     

属于您应用的网络进程的后端套接字已关闭   在后端返回HTTP响应之前。