Reproduciendo el problema
Me encuentro con un problema al intentar pasar mensajes de error usando los sockets web. Puedo replicar el problema que estoy usando JSON.stringify
para atender a un público más amplio:
// 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);
'{}'
El problema es que termino con un objeto vacío.
Lo que he intentado
Navegadores
Primero intenté dejar node.js y ejecutarlo en varios navegadores. La versión 28 de Chrome me da el mismo resultado y, curiosamente, Firefox por lo menos hace un intento pero dejó el mensaje:
>>> JSON.stringify(error); // Firebug, Firefox 23
{"fileName":"debug eval code","lineNumber":1,"stack":"@debug eval code:1\n"}
Función de reemplazo
Luego miré el prototipo Error.prototype . Muestra que el prototipo contiene métodos como toString y toSource . Sabiendo que las funciones no pueden ser encadenadas, incluí una función de reemplazo al llamar a JSON.stringify para eliminar todas las funciones, pero luego me di cuenta de que también tenía un comportamiento extraño:
var error = new Error('simple error message');
JSON.stringify(error, function(key, value) {
console.log(key === ''); // true (?)
console.log(value === error); // true (?)
});
No parece recorrer el objeto como lo haría normalmente, y por lo tanto no puedo verificar si la tecla es una función e ignorarla.
La pregunta
¿Hay alguna forma de stringificar mensajes de error nativos con JSON.stringify
? Si no, ¿por qué ocurre este comportamiento?
Métodos para evitar esto
- Quédese con mensajes de error simples basados en cadenas, o cree objetos de error personales y no confíe en el objeto de error nativo.
- Propiedades de extracción:
JSON.stringify({ message: error.message, stack: error.stack })
Actualizaciones
@ Ray Toal Sugerido en un comentario que eche un vistazo a los descriptores de propiedades . Ahora está claro por qué no 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);
}
Salida:
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 }
Clave: enumerable: false
.
La respuesta aceptada proporciona una solución para este problema.
fuente
Respuestas:
Puede definir un
Error.prototype.toJSON
para recuperar un plano queObject
representa elError
:Usar
Object.defineProperty()
agregatoJSON
sin que sea unaenumerable
propiedad en sí misma.Con respecto a la modificación
Error.prototype
, aunquetoJSON()
puede no estar definidoError
específicamente para s, el método todavía está estandarizado para los objetos en general (ref: paso 3). Por lo tanto, el riesgo de colisiones o conflictos es mínimo.Sin embargo, todavía a evitarlo por completo,
JSON.stringify()
'sreplacer
parámetro se puede utilizar en su lugar:fuente
.getOwnPropertyNames()
lugar de.keys()
, obtendrá las propiedades no enumerables sin tener que definirlas manualmente.key
defunction replaceErrors(key, value)
evitar conflicto de nombres con.forEach(function (key) { .. })
; elreplaceErrors
key
parámetro no se usa en esta respuesta.key
en este ejemplo, aunque está permitido, es potencialmente confuso ya que deja dudas sobre si el autor pretendía referirse a la variable externa o no.propName
sería una opción más expresiva para el bucle interno. (Por cierto, creo que @ 404NotFound significa "linter" (herramienta de análisis estático) no "enlazador" ) En cualquier caso, el uso de unareplacer
función personalizada es una solución excelente para esto, ya que resuelve el problema en un lugar apropiado y no altera el nativo / comportamiento global.parece funcionar
[ de un comentario de / u / ub3rgeek en / r / javascript ] y el comentario de felixfbecker a continuación
fuente
JSON.stringify(err, Object.getOwnPropertyNames(err))
ValidationError
tipos. Esto no stringificará elerrors
objeto anidado en un objeto de error Mongoose de tipoValidationError
.var spam = { a: 1, b: { b: 2, b2: 3} };
y corresObject.getOwnPropertyNames(spam)
, serás["a", "b"]
engañoso aquí, porque elb
objeto tiene lo suyob
. Obtendría ambos en su llamada de stringify, pero se perderíaspam.b.b2
. Eso es malo.message
ystack
están incluidos en el JSON.Como nadie está hablando de la parte del porqué , la responderé.
¿Por qué esto
JSON.stringify
devuelve un objeto vacío?Responder
Del documento de JSON.stringify () ,
y el
Error
objeto no tiene sus propiedades enumerables, es por eso que imprime un objeto vacío.fuente
JSON.stringify
usando sureplacer
parámetro.Modificando la gran respuesta de Jonathan para evitar parches de monos:
fuente
monkey patching
:)toJSON
, directamente alError
prototipo 's , que a menudo no es una gran idea. Tal vez alguien ya tiene, que este cheques, pero luego no saben qué esa otra versión sí. O si alguien inesperadamente consigue el tuyo, o asume que el prototipo de Error tiene propiedades específicas, las cosas podrían funcionar.)Hay un gran paquete Node.js para que:
serialize-error
.Maneja bien incluso los objetos de error anidados, lo que realmente necesitaba mucho en mi proyecto.
https://www.npmjs.com/package/serialize-error
fuente
También puede redefinir esas propiedades no enumerables para que sean enumerables.
y quizás
stack
propiedad también.fuente
Necesitábamos serializar una jerarquía de objetos arbitraria, donde la raíz o cualquiera de las propiedades anidadas en la jerarquía podrían ser instancias de Error.
Nuestra solución fue usar el
replacer
parámetro deJSON.stringify()
, por ejemplo:fuente
Ninguna de las respuestas anteriores parece serializar correctamente las propiedades que están en el prototipo de Error (porque
getOwnPropertyNames()
no incluye las propiedades heredadas). Tampoco pude redefinir las propiedades como una de las respuestas sugeridas.Esta es la solución que se me ocurrió: utiliza lodash, pero podría reemplazarlo con versiones genéricas de esas funciones.
Aquí está la prueba que hice en Chrome:
fuente
Estaba trabajando en un formato JSON para agregadores de registro y terminé aquí tratando de resolver un problema similar. Después de un tiempo, me di cuenta de que podía hacer que Node hiciera el trabajo:
fuente
instanceof
y noinstanceOf
.