2015-11-19 14 views
8

Sto tentando di implementare un modulo per errori personalizzati.Gli errori personalizzati e la cattura di bluebird con ErrorClass causano un comportamento involontario

Dovrebbe essere possibile creare un'istanza di un errore particolare ai richiedere-dichiarazione delle app utilizzando questo modulo:

var MyCustomError = require('custom-error')('MyCustomError'); 

Questo è il modulo:

'use strict'; 

var _CACHE = {}; 

function initError(name) { 
    function CustomError(message) { 
    this.name = name; 
    this.message = message; 
    } 
    CustomError.prototype = Object.create(Error.prototype); 
    CustomError.prototype.constructor = CustomError; 
    _CACHE[name] = CustomError; 
} 

function createCustomError(name) { 
    if (!_CACHE[name]) { 
    initError(name); 
    } 
    return _CACHE[name]; 
} 

module.exports = createCustomError; 

L'richiedere-one-liner sopra sta funzionando finora.

Ora, nel mio servizio, voglio prendere questo errore in modo esplicito:

var MyCustomError = require('custom-error')('MyCustomError') 
// ... 
return fooService.bar() 
    .catch(MyCustomError, function (error) { 
     logger.warn(error); 
     throw error; 
    }) 

Se rifiuto la promessa di fooService.bar nel mio test lanciando una MyCustomError questo sta lavorando molto.

MA, questo funziona solo perché il mio test e il servizio utilizzano la stessa istanza di MyCustomError.

Ad esempio, se rimuovo il meccanismo di memorizzazione nella cache nel mio modulo di errore personalizzato, il catch non verrà più raggiunto/eseguito, perché bluebird non capisce che i due errori sono dello stesso tipo:

function createCustomError(name) { 
    //if (!_CACHE[name]) { 
    initError(name); 
    //} 
    return _CACHE[name]; 
} 

Il codice specifico di gestione bluebird si trova nel catch_filter.js, è possibile dare un'occhiata a right here.

Sebbene l'approccio funzioni all'interno della mia app, questo causerà problemi prima che più moduli utilizzino il modulo errore personalizzato e la condivisione delle stesse istanze non venga più fornita.

Come posso ottenere questo concetto operativo entro non confrontando le istanze, ma il tipo di errore stessa?

Cheers,
Christopher

risposta

3

ho finalmente si avvicinò con un approccio leggermente diverso. Per le persone che la pensano questo è il risultato:

ErrorFactory

var 
    vsprintf = require("sprintf-js").vsprintf; 

function CustomErrorFactory(code, name, httpCode, message) { 

    // Bluebird catcher 
    this.predicate = function (it) { 
    return it.code === code; 
    }; 

    this.new = function (messageParameters, details) { 
    return new CustomError(messageParameters, details); 
    }; 

    this.throw = function (messageParameters, details) { 
    throw new CustomError(messageParameters, details); 
    }; 

    function CustomError(messageParameters, details) { 
    this.code = code; 
    this.name = name; 
    this.message = vsprintf(message, messageParameters); 
    this.httpCode = httpCode; 
    this.details = details || {}; 

    // Important: Do not swallow the stacktrace that lead to here. 
    // @See http://stackoverflow.com/questions/8802845/inheriting-from-the-error-object-where-is-the-message-property 
    Error.captureStackTrace(this, CustomError); 
    } 

    // CustomError must be instance of the Error-Object 
    CustomError.prototype = Object.create(Error.prototype); 
    CustomError.prototype.constructor = CustomError; 
} 

module.exports = CustomErrorFactory; 

errori

var 
    ErrorFactory = require("./ErrorFactory"); 

function initErrors() { 
    return { 
    Parameter: { 
     Missing: new ErrorFactory('1x100', 'ParameterMissing', 400, 'Parameter "%s" missing'), 
     Invalid: new ErrorFactory('1x200', 'ParameterInvalid', 400, 'Parameter "%s" invalid') 
     //.. 
    }, 
    Access: { 
     NotAccessible: new ErrorFactory('3x100', 'AccessNotAccessible', 403, 'Resource "%s" is not accessible for "%s"'), 
     //.. 
    }, 
    // ... 
    Request: { 
     //.. 
    } 
    }; 
} 

module.exports = initErrors(); 

creo un modulo separato che contiene queste classi.

Poi, nella mia implementazione, posso individuare gli errori in questo modo singolarmente:

function foo(request, reply) { 

    return bluebird 
    .resolve(bar) 
    .then(reply) 

    .catch(Errors.Parameter.Missing.predicate, function() { 
     return reply(boom.badRequest()); 
    }) 

    .catch(Errors.Entity.NotFound.predicate, function() { 
     return reply({}).code(204); 
    }) 

    .catch(Errors.Entity.IllegalState.predicate, function (error) { 
     return reply(boom.badImplementation(error.message)); 
    }) 

    // any other error 
    .catch(function (error) { 
     return reply(boom.badImplementation(error.message)); 
    }); 
} 

Lanciare

Errors.Entity.IllegalState.throw(['foo', 'bar']); 
// or 
throw Errors.Entity.IllegalState.new(['foo', 'bar']); 

Richiede

Errors = require('errors'); // all 
EntityErors = require('errors').Entity; // one group 
EntityNotFoundError = require('errors').Entity.NotFound; // one particular 

L'unica cosa che quello che ancora non lo faccio capire id perché è necessario utilizzare una funzione di predicato piuttosto che passare l'oggetto-errore al catc h-clausola. Ma posso vivere con quello.

3

è anche possibile utilizzare le funzioni di predicato con la cattura

function isMyCustomError(f) { 
    return f instanceof Error && f.name === "MyCustomError"; 
} 
+0

Hi Esailija, dato che non ho collegato un paio di giorni la reputazione ha ottenuto automaticamente assegnato. Fortunato per te, ma dal momento che sei lo sviluppatore di Bluebird te lo meriti abbastanza :) Il codice: non vedo il tuo punto su come questo approvi la manipolazione? Parli del classico pilotaggio del catch-catch indipendentemente dal bluebird? –

+0

@ChristopherQuesto è solo per bluebird – Esailija

+0

Quindi questo è esattamente quello che sto facendo nell'esempio sopra - come 'catch (Errors.Entity.IllegalState.predicate, ..)' –