Reproduzindo o problema
Estou com um problema ao tentar passar mensagens de erro usando soquetes da web. Posso replicar o problema que estou enfrentando 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 eu acabo com um objeto vazio.
O que eu tentei
Navegadores
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 tenta, mas deixa de fora a mensagem:
>>> JSON.stringify(error); // Firebug, Firefox 23
{"fileName":"debug eval code","lineNumber":1,"stack":"@debug eval code:1\n"}
Função substituidora
Então olhei para o erro.protótipo . Isso mostra que o protótipo contém métodos como toString e toSource . Sabendo que as funções não podem ser restringidas, incluí uma função substituta ao chamar JSON.stringify para remover todas as funções, mas depois percebi que ela também tinha um comportamento estranho:
var error = new Error('simple error message');
JSON.stringify(error, function(key, value) {
console.log(key === ''); // true (?)
console.log(value === error); // true (?)
});
Parece não fazer um loop sobre o objeto como faria normalmente 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
? Caso contrário, por que esse comportamento ocorre?
Métodos para contornar isso
- Atenha-se a mensagens de erro simples baseadas em string ou crie objetos de erro pessoais e não confie no objeto Error nativo.
- Propriedades de extração:
JSON.stringify({ message: error.message, stack: error.stack })
Atualizações
@Ray Toal Sugerido em um comentário que dê uma olhada nos descritores de propriedades . Agora está claro por que 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);
}
Resultado:
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 }
Key: enumerable: false
.
A resposta aceita fornece uma solução alternativa para esse problema.
fonte
Respostas:
Você pode definir a
Error.prototype.toJSON
para recuperar uma planícieObject
representandoError
:O uso de
Object.defineProperty()
adicionatoJSON
sem que seja umaenumerable
propriedade em si.Em relação à modificação
Error.prototype
, emboratoJSON()
não possa ser definidoError
especificamente para s, o método ainda é padronizado para objetos em geral (ref: etapa 3). Portanto, o risco de colisões ou conflitos é mínimo.Porém, para evitá-lo completamente,
JSON.stringify()
oreplacer
parâmetro pode ser usado:fonte
.getOwnPropertyNames()
vez de.keys()
, obterá propriedades não enumeráveis sem precisar defini-las manualmente.key
infunction replaceErrors(key, value)
para evitar conflitos de nomenclatura.forEach(function (key) { .. })
; oreplaceErrors
key
parâmetro não é utilizado nesta resposta.key
deste exemplo, embora permitida, é potencialmente confusa, pois deixa dúvidas se o autor pretendia se referir à variável externa ou não.propName
seria uma escolha mais expressiva para o loop interno. (BTW, acho que @ 404NotFound significava "linter" (ferramenta de análise estática) e não "linker" ). De qualquer forma, o uso de umareplacer
função personalizada é uma excelente solução para isso, pois resolve o problema em um local apropriado e não altera os recursos nativos. / comportamento global.parece funcionar
[ de um comentário de / u / ub3rgeek em / r / javascript ] e o comentário de felixfbecker abaixo
fonte
JSON.stringify(err, Object.getOwnPropertyNames(err))
ValidationError
tipos. Isso não especificará oerrors
objeto aninhado em um objeto de erro do Mongoose do tipoValidationError
.var spam = { a: 1, b: { b: 2, b2: 3} };
e executarObject.getOwnPropertyNames(spam)
, ficará["a", "b"]
enganoso aqui, porque ob
objeto possui o seub
. Você receberia os dois em sua ligação com string, mas perderiaspam.b.b2
. Isso é ruim.message
estack
estar incluído no JSON.Como ninguém está falando sobre a parte do porquê , eu vou responder.
Por que isso
JSON.stringify
retorna um objeto vazio?Responda
No documento JSON.stringify () ,
e o
Error
objeto não tem suas propriedades enumeráveis, é por isso que imprime um objeto vazio.fonte
JSON.stringify
usando seureplacer
parâmetro.Modificando a ótima resposta de Jonathan para evitar o patch de macacos:
fonte
monkey patching
:)toJSON
, diretamente aoError
protótipo 's , que muitas vezes não é uma ótima idéia. Talvez alguém já tem, que este cheques, mas então você não sabe o que que outra versão faz ou se alguém inesperadamente recebe seu, ou assume protótipo de erro tem propriedades específicas, as coisas poderiam Bork)..Há um grande pacote Node.js para que:
serialize-error
.Ele lida com objetos de erro bem aninhados, o que eu realmente precisava de muito no meu projeto.
https://www.npmjs.com/package/serialize-error
fonte
Você também pode redefinir as propriedades não enumeráveis para serem enumeráveis.
e talvez
stack
propriedade também.fonte
Precisávamos serializar uma hierarquia arbitrária de objetos, onde a raiz ou qualquer uma das propriedades aninhadas na hierarquia poderia ser instâncias de Error.
Nossa solução foi usar os
replacer
parâmetros deJSON.stringify()
, por exemplo:fonte
Nenhuma das respostas acima parecia serializar corretamente as propriedades que estão no protótipo de Erro (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 encontrei - ele usa o lodash, mas você pode substituí-lo por versões genéricas dessas funções.
Aqui está o teste que fiz no Chrome:
fonte
Eu estava trabalhando em um formato JSON para anexadores de log e acabei aqui tentando resolver um problema semelhante. Depois de um tempo, percebi que podia fazer o Node fazer o trabalho:
fonte
instanceof
e nãoinstanceOf
.