自定义错误未在throw语句或next()调用上正确传播

时间:2012-07-25 15:12:41

标签: node.js coffeescript mongoose

我正在开发一个Node.js / CoffeeScript应用程序,我正在使用类层次结构来处理错误。当我在路由处理程序的根目录中使用throw语句时,一切都很有效:

class APIError extends Error
    constructor: ->

app.error (err, req, res, next) ->
    if err instance of APIError
       console.log 'APIError captured'

app.get '/', (req, res, next) ->
    throw new APIError

但是,使用Mongoose,在回调函数中使用throw语句:

app.get '/', (req, res, next) ->
    UserModel.where('name', 'Joe').findOne (err, doc) ->
        throw new APIError

结果

/usr/local/lib/node_modules/mongoose/lib/utils.js:413
    throw err;
          ^
Error: 

当我拨打next()时,就像在

中一样
app.get '/', (req, res, next) ->
    UserModel.where('name', 'Joe').findOne (err, doc) ->
        return next new APIError

甚至在处理程序的主体中使用它:

app.get '/', (req, res, next) ->
    next new APIError

我在控制台中打印出undefined

将最后一个语句更改为return next Error按预期工作,即在控制台中打印出异常堆栈跟踪:

app.get '/', (req, res, next) ->
    return next Error 'This works as expected'

是Node.js问题还是我在CoffeeScript中定义类的方式?任何想法如何使这种错误层次结构工作?

更新1

我可以确认这是CoffeeScript的类的实现方式。使用标准的JS原型链定义可以解决问题,但感觉不对。

3 个答案:

答案 0 :(得分:2)

在构造函数中设置类的name属性可以解决问题(第3行):

class APIError extends Error
    constructor: ->
        @name = 'APIError'

app.use (err, req, res, next) ->
    if err instance of APIError
        console.log 'Error captured'

app.get '/', (req, res, next) ->
    #This will work: captured by error handler
    throw new APIError

app.get '/in-mongoose-callback', (req, res, next) ->
    UserModel.where('name', 'Joe').findOne (err, doc) ->
        #This one works as expected: captured by error handler
        return next new APIError

这是由one of the changes归因于CoffeeScript 1。3。3(2012年5月15日):

  

由于JavaScript严格模式的新语义,CoffeeScript不再保证构造函数在所有运行时都具有名称。有关讨论,请参阅#2052。

请注意,在Mongoose查询回调中使用throw语句而不是next()将无效

答案 1 :(得分:1)

使用express时,我扩展Error,将名称设置为prototype属性,并执行堆栈跟踪。

class RequestError extends Error
    name: 'RequestError'
    constructor:(@status, message)->
        super message
        Error.captureStackTrace @, @constructor

我可以针对不良用户登录执行new RequestError 403, 'Invalid username/password.',或针对错误的网页请求执行new RequestError 404, 'Page not found.'。当我发现错误时,我做了

require 'colors'
console.error error.stack.red  # logs where any error occured
if error instanceof RequestError
    # proper response for user errors
    response.status(error.status).send(error.message)
else response.status(500).send('Internal Server Error') # for 'other' errors the user only gets back a 500 'Internal Server Error'

答案 2 :(得分:0)

很难说,但是我们遇到了错误问题,直到我们将它们移动到服务器脚本的末尾,即使在启动服务器之后也是如此。这是使用Express with Node,但可以给你一个提示。之前我们在服务器文件的开头附近假设没有问题但在我们将所有错误处理结束后开始工作。不确定框架操作顺序的路由器或中间件是否发布但似乎解决了我们的问题。


/////////////////////////////
// Start Server
/////////////////////////////

app.listen(siteConf.port);

//////////////////////////
// Error Handling
//////////////////////////  
function NotFoundError(req, message){
    this.name = "NotFoundError";
    this.status = 404;
    this.message = message || (req.method + " " + req.url + " could not be found");
    Error.captureStackTrace(this, NotFoundError);
}
NotFoundError.prototype.__proto__ = Error.prototype;

function ProtectedResourceError(req, message){
    this.name = "ProtectedResourceError";
    this.status = 401;
    this.message = message || (req.url + " is protected");
}
ProtectedResourceError.prototype.__proto__ = Error.prototype;

function ValidationError(req, message, errors){
    this.name = "ValidationError";
    this.status = 400;
    this.message = message || ("Error when validating input for " + req.url);
    this.errors = errors;
}
ValidationError.prototype.__proto__ = Error.prototype;

function SearchError(req, message){
    this.name = "SearchError";
    this.status = 400;
    this.message = message || ("Error when searching");
}
SearchError.prototype.__proto__ = Error.prototype;

// If no route matches, throw NotFoundError
app.use(function(req, res, next) {
    console.log("No matching route for " + req.url);
    next(new NotFoundError(req));        
});

// General error handler
app.use(function(err, req, res, next) {
    //console.log("Trapped error : " + err);

    // handle exceptions
    if (err instanceof ValidationError) {
        standardResponse(req, res, {error: err.message, fields: err.errors}, err.status, err.message);
    } else if (err instanceof ProtectedResourceError) {
        standardResponse(req, res, {error: err.message}, err.status, err.message);
    } else if (err instanceof NotFoundError) {
        standardResponse(req, res, {error: err.message}, err.status, err.message);
    } else if (err instanceof SearchError) {
        standardResponse(req, res, {error: err.message}, err.status, err.message);
    } else {
        standardResponse(req, res, {error: err.message}, 500, err.message);
    }
});