Express(节点)-通过中间件周期发送错误消息的正确方法

时间:2018-09-23 21:24:24

标签: javascript node.js express error-handling

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

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

示例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。这不是我想要的,它应该在出现错误的情况下停止中间件循环。然后,如果需要在视图上显示错误,则应使用示例1中的错误消息调用Controller.getIndex()。如果错误具有自己的视图(如404、500 ...),则不应调用{{1 }},但使用适当的视图。无论如何,如果otherMiddlewareX中发生错误,则不应调用“之后”的中间件(otherMiddlewareX + 1,otherMiddlewareX + 2 ... otherMiddlewareN)。

任何人都可以帮助我找到一种“正确”的方式吗?

编辑:similar problem similar stack overflow question

可能的答案(如有必要,我可以提供实施示例):

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

也许有一种方法可以使用Express应用程序轻松地做到这一点,但是我无法找到正确的方法。你能帮我吗?

谢谢。

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

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

2 个答案:

答案 0 :(得分:2)

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

// 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);

答案 1 :(得分:0)

如果仅使用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();
};