javascript - catch - node error middleware



Não é possível restringir um erro usando JSON.stringify? (6)

Reproduzindo o problema

Eu estou correndo em um problema ao tentar passar mensagens de erro em torno de usar sockets da web. Eu posso replicar o problema que estou enfrentando usando JSON.stringify para atender a um público mais amplo:

// node v0.10.15
> var error = new Error('simple error message');
    undefined

> error
    [Error: simple error message]

> Object.getOwnPropertyNames(error);
    [ 'stack', 'arguments', 'type', 'message' ]

> JSON.stringify(error);
    '{}'

O problema é que acabo com um objeto vazio.

O que eu tentei

Navegadores

Primeiro tentei sair do node.js e executá-lo em vários navegadores. A versão 28 do Chrome me dá o mesmo resultado e, curiosamente, o Firefox pelo menos faz uma tentativa, mas deixou de fora a mensagem:

>>> JSON.stringify(error); // Firebug, Firefox 23
{"fileName":"debug eval code","lineNumber":1,"stack":"@debug eval code:1\n"}

Função Replacer

Então eu olhei para o Error.prototype . Isso mostra que o protótipo contém métodos como toString e toSource . Sabendo que as funções não podem ser esticadas, incluí uma função replacer ao chamar JSON.stringify para remover todas as funções, mas depois percebi que também tinha algum comportamento estranho:

var error = new Error('simple error message');
JSON.stringify(error, function(key, value) {
    console.log(key === ''); // true (?)
    console.log(value === error); // true (?)
});

Não parece fazer um loop sobre o objeto como normalmente faria e, portanto, não posso verificar se a chave é uma função e ignorá-la.

A questão

Existe alguma maneira de restringir mensagens de erro nativas com JSON.stringify ? Se não, por que esse comportamento ocorre?

Métodos de contornar este

  • Fique com mensagens de erro simples baseadas em string, ou crie objetos de erro pessoais e não confie no objeto Error nativo.
  • Pull properties: JSON.stringify({ message: error.message, stack: error.stack })

Atualizações

@Ray Toal Sugerido em um comentário que eu dou uma olhada nos descritores de propriedade . Está claro agora porque não funciona:

var error = new Error('simple error message');
var propertyNames = Object.getOwnPropertyNames(error);
var descriptor;
for (var property, i = 0, len = propertyNames.length; i < len; ++i) {
    property = propertyNames[i];
    descriptor = Object.getOwnPropertyDescriptor(error, property);
    console.log(property, descriptor);
}

Saída:

stack { get: [Function],
  set: [Function],
  enumerable: false,
  configurable: true }
arguments { value: undefined,
  writable: true,
  enumerable: false,
  configurable: true }
type { value: undefined,
  writable: true,
  enumerable: false,
  configurable: true }
message { value: 'simple error message',
  writable: true,
  enumerable: false,
  configurable: true }

Chave: enumerable: false .

A resposta aceita fornece uma solução alternativa para esse problema.

https://ffff65535.com


Como ninguém está falando sobre a parte por que , eu vou responder a estas

P: Existe alguma maneira de restringir mensagens de erro nativas com JSON.stringify?

Não.

Q: Se não, por que esse comportamento ocorre?

No documento de JSON.stringify () ,

Para todas as outras instâncias Object (incluindo Map, Set, WeakMap e WeakSet), somente suas propriedades enumeráveis ​​serão serializadas.

e o objeto Error não tem suas propriedades enumeráveis, é por isso que ele imprime um objeto vazio.



Nenhuma das respostas acima pareceu serializar adequadamente propriedades que estão no protótipo de Error (porque getOwnPropertyNames() não inclui propriedades herdadas). Também não consegui redefinir as propriedades como uma das respostas sugeridas.

Esta é a solução que eu criei - ela usa o lodash, mas você pode substituir o lodash por versões genéricas dessas funções.

 function recursivePropertyFinder(obj){
    if( obj === Object.prototype){
        return {};
    }else{
        return _.reduce(Object.getOwnPropertyNames(obj), 
            function copy(result, value, key) {
                if( !_.isFunction(obj[value])){
                    if( _.isObject(obj[value])){
                        result[value] = recursivePropertyFinder(obj[value]);
                    }else{
                        result[value] = obj[value];
                    }
                }
                return result;
            }, recursivePropertyFinder(Object.getPrototypeOf(obj)));
    }
}


Error.prototype.toJSON = function(){
    return recursivePropertyFinder(this);
}

Aqui está o teste que fiz no Chrome:

var myError = Error('hello');
myError.causedBy = Error('error2');
myError.causedBy.causedBy = Error('error3');
myError.causedBy.causedBy.displayed = true;
JSON.stringify(myError);

{"name":"Error","message":"hello","stack":"Error: hello\n    at <anonymous>:66:15","causedBy":{"name":"Error","message":"error2","stack":"Error: error2\n    at <anonymous>:67:20","causedBy":{"name":"Error","message":"error3","stack":"Error: error3\n    at <anonymous>:68:29","displayed":true}}}  

Precisávamos serializar uma hierarquia arbitrária de objetos, onde a raiz ou qualquer uma das propriedades aninhadas na hierarquia poderiam ser instâncias de erro.

Nossa solução foi usar o parâmetro de replacer de JSON.stringify() , por exemplo:

function jsonFriendlyErrorReplacer(key, value) {
  if (value instanceof Error) {
    return {
      // Pull all enumerable properties, supporting properties on custom Errors
      ...value,
      // Explicitly pull Error's non-enumerable properties
      name: value.name,
      message: value.message,
      stack: value.stack,
    }
  }

  return value
}

let obj = {
    error: new Error('nested error message')
}

console.log('Result WITHOUT custom replacer:', JSON.stringify(obj))
console.log('Result WITH custom replacer:', JSON.stringify(obj, jsonFriendlyErrorReplacer))


Você também pode apenas redefinir essas propriedades não enumeráveis ​​para serem enumeráveis.

Object.defineProperty(Error.prototype, 'message', {
    configurable: true,
    enumerable: true
});

e talvez a propriedade de stack também.






error-handling