首页 > 解决方案 > Try/catch 发送电子邮件并创建数据库条目,即使它不应该

问题描述

我有一个关于 Node.JS 中的 try/catch 的问题,在 Express 中更准确地说。我有一个销售节日、音乐会等门票的应用程序,每个活动的门票数量有限。但是,当我的服务器检测到没有剩余票证时,无论如何都会创建一个票证并发送带有票证的邮件。

app.post(
  "/api/tickets",
  [
    body("email").trim().isEmail().isLength({ min: 8 }).normalizeEmail(),
    body("firstName")
      .trim()
      .isString()
      .isLength({ min: 2 })
      .matches(/^[^0-9_!¡?÷?¿/\\+=@#$%ˆ&*(){}|~<>;:[\]]{2,}$/),
    body("lastName")
      .trim()
      .isString()
      .isLength({ min: 2 })
      .matches(/^[^0-9_!¡?÷?¿/\\+=@#$%ˆ&*(){}|~<>;:[\]]{2,}$/),
    body("phoneNumber")
      .trim()
      .isString()
      .isLength({ min: 8 })
      .matches(
        /^((?<!\w)(\(?(\+|00)?48\)?)?[ -]?\d{3}[ -]?\d{3}[ -]?\d{3}(?!\w))$/
      ),
  ],
  async (req: any, res: any, next: any) => {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        let err = new StatusError("Error while validating body", 400);
        next(err);
      }

      const { id, email, firstName, lastName, phoneNumber } = req.body;

      let eventFound: any;
      const event = await Event.findById(id, (error, result) => {
        if (!error) eventFound = result;
        else {
          let err = new StatusError("No event found", 404);
          next(err);
        }
      });

      Ticket.find({ eventId: event?.id }, (error, tickets) => {
        if (!error) {
          if (event?.toJSON().maxTicketsAmount - 1 < tickets.length) {
            let err = new StatusError("No tickets left", 403);
            next(err);
          }
        } else {
          let err = new StatusError("No tickets found", 404);
          next(err);
        }
      });

      const paymentIntent = await stripe.paymentIntents.create({
        amount: eventFound.ticketPrice, // NEEDS TO BE ABOVE SOME VALUE!!!!!!!
        currency: "pln",
        payment_method_types: ["card"],
        receipt_email: email,
        metadata: { integration_check: "accept a payment" },
      });
      if (!paymentIntent) {
        let err = new StatusError("Creating payment intent failed", 400);
        next(err);
      }

      const ticket = new Ticket({
        email: email.trim(),
        firstName: firstName.trim(),
        lastName: lastName.trim(),
        phoneNumber: phoneNumber.trim(),
        eventId: eventFound.id,
        purchaseDate: new Date(),
      });
      ticket.save((error) => {
        if (error) {
          let err = new StatusError("Error while saving to DB", 500);
          next(err);
        }
      });

      const qr = await toDataURL(ticket.id);
      console.log(ticket.id);

      if (!qr) {
        let err = new StatusError("Error while creating QR Code", 400);
        next(err);
      }

      const mailTemplate = `
      <h1>Hello ${firstName} ${lastName}</h1>
      <p>Thanks for buying ticket for ${eventFound.nameOfEvent}, in ${eventFound.place}, taking place on ${eventFound.dateOfEvent}</p>
      <img src="${qr}">
      `;

      let message = {
        from: env.email,
        to: email,
        subject: `Ticket for ${eventFound.nameOfEvent}`,
        html: mailTemplate,
      };
      transporter.sendMail(message, (error, info) => {
        if (error) {
          return res.status(500).json({ error: error });
        } else console.log("Mail sent:", info.response);
      });

      return res.status(200).send(paymentIntent.client_secret);
    } catch (error) {
      next(error);
    }
  }
);

错误处理中间件:

app.use(function (err: any, req: any, res: any, next: any) {
  console.error(err.message);
  if (!err.statusCode) err.statusCode = 500;
  res.status(err.statusCode).send(err.message);
});

自定义错误类:

class StatusError extends Error {
  code: number;
  constructor(message: string, code: number) {
    super();
    this.message = message;
    this.code = code;
  }
}

export default StatusError;

标签: node.jsexpress

解决方案


问题是您对票证的检查是异步的,因此无论如何都会执行其余的功能。您需要等待结果才能决定要做什么。最简单的方法是将路由处理程序的其余部分嵌套在以下回调中Ticket.find

async (req: any, res: any, next: any) => {
  try {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      let err = new StatusError("Error while validating body", 400);
      next(err);
    }
    
    const { id, email, firstName, lastName, phoneNumber } = req.body;
    
    let eventFound: any;
    const event = await Event.findById(id, (error, result) => {
      if (!error) eventFound = result;
      else {
        let err = new StatusError("No event found", 404);
        next(err);
      }
    });
    
    Ticket.find({ eventId: event?.id }, (error, tickets) => {
      if (!error) {
        if (event?.toJSON().maxTicketsAmount - 1 < tickets.length) {
          let err = new StatusError("No tickets left", 403);
          next(err);
          return;
        }
      } else {
        let err = new StatusError("No tickets found", 404);
        next(err);
        return;
      }

      const paymentIntent = await stripe.paymentIntents.create({
        amount: eventFound.ticketPrice, // NEEDS TO BE ABOVE SOME VALUE!!!!!!!
        currency: "pln",
        payment_method_types: ["card"],
        receipt_email: email,
        metadata: { integration_check: "accept a payment" },
      });
      if (!paymentIntent) {
        let err = new StatusError("Creating payment intent failed", 400);
        next(err);
      }
      
      const ticket = new Ticket({
        email: email.trim(),
        firstName: firstName.trim(),
        lastName: lastName.trim(),
        phoneNumber: phoneNumber.trim(),
        eventId: eventFound.id,
        purchaseDate: new Date(),
      });
      ticket.save((error) => {
        if (error) {
          let err = new StatusError("Error while saving to DB", 500);
          next(err);
        }
      });
      
      const qr = await toDataURL(ticket.id);
      console.log(ticket.id);
      
      if (!qr) {
        let err = new StatusError("Error while creating QR Code", 400);
        next(err);
      }
      
      const mailTemplate = `
        <h1>Hello ${firstName} ${lastName}</h1>
        <p>Thanks for buying ticket for ${eventFound.nameOfEvent}, in ${eventFound.place}, taking place on ${eventFound.dateOfEvent}</p>
        <img src="${qr}">
        `;
        
      let message = {
        from: env.email,
        to: email,
        subject: `Ticket for ${eventFound.nameOfEvent}`,
        html: mailTemplate,
      };
      transporter.sendMail(message, (error, info) => {
        if (error) {
          return res.status(500).json({ error: error });
        } else console.log("Mail sent:", info.response);
      });
      
      return res.status(200).send(paymentIntent.client_secret);
    });
    
  } catch (error) {
    next(error);
  }
}

推荐阅读