【问题标题】:Express (Node) - Right way to send error messages through middleware cycleExpress (Node) - 通过中间件循环发送错误消息的正确方法
【发布时间】:2018-09-23 21:24:24
【问题描述】:

在 express 应用程序中,向视图发送错误消息的正确方法是什么?

(我们假设代码是同步的)

示例 1:

// Route
Router.route('/')
  .get(controller.getIndex);

// Controller
Controller.getIndex = function (req, res, next) {
  doSomething(function (err, datas) {
    if (err) { res.locals.err = 'error message'; }
    // ...
  });
  res.render(/* view */);
};

// View 
<p class="error">${ out.global.err }</p>

这里没问题:如果有错误,我会在响应中存储一条消息并显示在视图中。

示例 2:

// Route (with multiple middlewares)
Router.route('/')
  .get(firstMiddleware, otherMiddleware/*, otherMiddleware2, etc */, controller.getIndex);

// firstMiddleware (always need to be called)
firstMiddleware = function (req, res, next) {
  doAlsoSomething(function (err, datas) {
    if (err) { res.locals.err = 'error message'; }
    // ...
  });
  next();
};

// otherMiddleware, otherMiddleware2 (need to be called only if no errors)
otherMiddleware = function (req, res, next) {
  next();
};

// Controller (need to be called only if want to display the view)
Controller.getIndex = function (req, res, next) {
  doSomething(function (err, datas) {
    if (err) { res.locals.err = 'error message'; }
    // ...
  });
  res.render(/* view */);
};

// View 
<p class="error">${ out.global.err }</p>

在此示例中,将始终调用 otherMiddleware。这不是我想要的,它应该在出现错误时停止中间件循环。然后,如果需要在视图上显示错误,则应调用 Controller.getIndex() 并带有示例 1 中的错误消息。如果错误有自己的视图(如 404、500...),则不应调用 Controller.getIndex(),而是适当的观点。在任何情况下,如果 otherMiddlewareX 发生错误,则不应调用中间件“之后”(otherMiddlewareX + 1, otherMiddlewareX + 2 ... otherMiddlewareN)。

谁能帮我找到一个“正确”的方法来做到这一点?

编辑:similar problemsimilar stack overflow question

可能的答案(如有必要,我可以给出实现示例):

  • 在每个中间件中放置一个条件if (res.locals.err),以便在发生错误时通过调用next();跳转到下一个。这不是很方便。
  • 使用next('error message'); 调用错误处理中间件,但很难在一个函数中管理每个重定向(这是我使用的解决方案)
  • 使用next('route');每次路由重复来管理错误(类似于错误处理中间件解决方案)。
  • 使用res.redirect(); 和闪存消息(会话、查询字符串、数据库、cookie...)。如果我使用此解决方案,我需要使其无状态。例如,我可以使用查询字符串来做到这一点,但这对我来说也不是很方便。此外,它并没有真正直接回答问题,而是更多地回答问题how to pass message between requests in REST API

使用 express 应用程序可能有一种方法可以轻松地做到这一点,但我无法弄清楚什么是正确的方法。你能帮帮我吗?

提前谢谢你。

编辑:有关可能答案的更多详细信息(第三种解决方案)。

使用next('error message'); 我有一个错误处理程序中间件,我需要在其中显示良好的视图。视图可以是 500、404、406...,也可以是带有错误消息的 200。首先,我需要将状态码提供给这个中间件,然后如果是 200,我需要调用与错误对应的视图。问题是我并不总是知道要调用哪个视图。我可以在 /auth 上有一个需要重定向到 GET /login 的 POST。我需要“硬编码”它并且不能为每种情况编写相同的代码,或者我需要在每个next('error message'); 调用中传递视图/控制器。即使重定向也很难,因为有时我需要调用控制器,但有时我需要通过调用路由而不是控制器来经历另一个中间件循环,我发现的唯一方法是return app._router.handle(req, res, next);,它看起来更像是“黑客” “这是一个官方的解决方案。如果我用 res.redirect() 重定向;我回到了我需要传递一些闪存消息的第二个可能的答案。最后,这是我使用的解决方案 (next(err)),但我希望有更好的方法。

【问题讨论】:

  • 关于您的第三个建议,您所说的“但很难在一个函数中管理每个重定向”是什么意思?
  • 我的意思是我有很多不同的案例要处理,而且我很难找到一个在没有“硬编码”的情况下适用于所有情况的解决方案。我将在我的帖子中提供更多详细信息。

标签: javascript node.js express error-handling


【解决方案1】:

我不确定我是否理解了你的整个问题,但我会像这样使用和错误处理中间件:

// firstMiddleware
var firstMiddleware = function (req, res, next) {
  doAlsoSomething(function (err, datas) {
    if (err) { next('error message'); }
    // ...
    next(); // If err is true calls renderView
  });
};

// otherMiddleware
var otherMiddleware = function (req, res, next) {
  // Do stuff only if no error
  next();
};

// Controller
Controller.getIndex = function (req, res, next) {
  doSomething(function (err, datas) {
    if (err) { next('error message'); }
    // ...
    next(); // If err is true calls renderView
  });
};

// This middleware runs after next('error message')
var handleError = function (err, req, res, next) {
  res.locals.err = err.message;
}

// This middleware always runs
var renderView = function (req, res, next) {
  res.render(/* view */);
}

// Route
Router.route('/')
  .get(firstMiddleware, otherMiddleware/*, otherMiddleware2, etc */, controller.getIndex, handleError, renderView);

【讨论】:

  • 这是一种解决方案,谢谢。这个解决方案令人讨厌的是我需要将handleError 放在每条路线中。
  • 我更新了答案以支持 doAlsoSomethingdoSomething 中的异步回调。由于某种原因,我忘记假设它们是异步的。对此我很抱歉。
【解决方案2】:

如果您只使用一次otherMiddleware,请将其删除,然后将其内容移动到上一个中间件的else 块中:

var checkForm = function (req, res, next) {
  validationResult(function (err, datas) {
    if (err) { res.locals.err = 'error message'; }
    else {
      // Do stuff only if form is valid
      // otherMiddleware code
    }
    // ...
  });
  next();
};

【讨论】:

  • 嘿,谢谢您的回答。我只在这种情况下不使用otherMiddleware。它用于其他中间件周期。事实上,正如您在我的示例中看到的那样,您甚至可以拥有很少的中间件。您完全可以拥有:.get(checkForm, otherMiddleware1, otherMiddleware2, otherMiddleware3, otherMiddleware4, otherMiddleware5, controller.getIndex),每个中间件做不同的事情,并且可以在其他路线中使用。 checkForm 只是一个示例,但如果您愿意,可以将其替换为 otherMiddleware0 ;)
猜你喜欢
  • 2018-07-29
  • 2014-10-02
  • 2020-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-23
  • 1970-01-01
  • 2012-09-23
相关资源
最近更新 更多