¿Cómo enviar un mensaje de estado http personalizado en node / express?

89

Mi aplicación node.js está modelada como la aplicación express / examples / mvc .

En una acción de controlador, quiero escupir un estado HTTP 400 con un mensaje http personalizado. De forma predeterminada, el mensaje de estado http es "Solicitud incorrecta":

HTTP/1.1 400 Bad Request

Pero quiero enviar

HTTP/1.1 400 Current password does not match

Intenté varias formas, pero ninguna de ellas configuró el mensaje de estado http en mi mensaje personalizado.

Mi función de controlador de solución actual se ve así:

exports.check = function( req, res) {
  if( req.param( 'val')!=='testme') {
    res.writeHead( 400, 'Current password does not match', {'content-type' : 'text/plain'});
    res.end( 'Current value does not match');

    return;
  } 
  // ...
}

Todo funciona bien pero ... parece que no es la forma correcta de hacerlo.

¿Hay alguna forma mejor de configurar el mensaje de estado http usando express?

lgersman
fuente
4
Bueno, esta parece ser la única solución. Pero no recomendaría algo así, la especificación HTTP 1.1 tiene su descripción de error estandarizada por algunas buenas razones. Creo que es una mala práctica enviar códigos de estado conocidos con descripciones personalizadas, pero eso depende de usted.
schaermu
Hmmm, tal vez eso sea cierto. Por otro lado, supongo que los navegadores solo verifican el código de estado y no el mensaje de estado http legible por humanos. Pensé que era una buena idea usar el mensaje de estado http para transportar un mensaje de error concreto (es decir, no predeterminado) si está disponible. Además, es fácil de captar eso usando el script de Java del lado del cliente (usando jQuery puede hacer "jqXHR.statusText" para obtener el error con fines de visualización)
lgersman
4
No se trata de compatibilidad o problemas potenciales del navegador, es solo una mala práctica;) si desea que se muestre un mensaje de error, envíelo como el cuerpo, ese es el propósito previsto.
schaermu
6
Las descripciones de errores específicas no forman parte de la especificación. RCF-2616 establece específicamente: "Los valores individuales de los códigos de estado numéricos definidos para HTTP / 1.1, y un conjunto de ejemplo de las frases de motivo correspondientes, se presentan a continuación. Las frases de motivo enumeradas aquí son solo recomendaciones; PUEDEN ser reemplazadas por equivalentes locales sin afectar el protocolo ".
Ted Bigham
Las frases de motivo personalizadas son excelentes, pero (dado que su mensaje es "La contraseña actual no coincide") parece que realmente desea el código 401 aquí, en cuyo caso probablemente no necesite cambiar el mensaje.
Codificación

Respuestas:

59

Puede consultar esta documentación deres.send(400, 'Current password does not match') Look express 3.x para obtener más detalles

ACTUALIZAR para Expressjs 4.x

Úselo de esta manera (vea los documentos express 4.x ):

res.status(400).send('Current password does not match');
// or
res.status(400);
res.send('Current password does not match');
Peter Gerasimenko
fuente
41
desafortunadamente, esto no configurará el mensaje de estado http, pero enviará 'La contraseña actual no coincide' como contenido del cuerpo ...
lgersman
Esto establece el estado HTTP, pero genera una advertencia porque la firma de este método está obsoleta.
nulabilidad
1
El res.status(400).send('Current password does not match');ejemplo me funciona para Express 4.
Tyler Collier
Funciona enExpress ^4.16.2
Ajay
103

Ninguna de las respuestas existentes logra lo que pidió originalmente el OP, que es anular la Frase de motivo predeterminada (el texto que aparece inmediatamente después del código de estado) enviado por Express.

Lo que quieres es res.statusMessage. Esto no es parte de Express, es una propiedad del objeto http.Response subyacente en Node.js 0.11+.

Puede usarlo así (probado en Express 4.x):

function(req, res) {
    res.statusMessage = "Current password does not match";
    res.status(400).end();
}

Luego use curlpara verificar que funciona:

$ curl -i -s http://localhost:3100/
HTTP/1.1 400 Current password does not match
X-Powered-By: Express
Date: Fri, 08 Apr 2016 19:04:35 GMT
Connection: keep-alive
Content-Length: 0
mamacdon
fuente
6
Esta es la forma correcta de configurar el statusMessageen algo diferente al mensaje estándar asignado al StatusCode
peteb
4
Puede obtener la propiedad en el objeto subyacente conres.nativeResponse.statusMessage
sebilasse
@RobertMoskal Probado usando un servidor Express mínimo (Express 4.16.1 y Node 12.9.0), y todavía me funciona. Verifique el código de su aplicación: quizás algo más esté mal.
mamacdon
No estoy seguro de por qué esta no es la respuesta aceptada porque definitivamente es la solución, al menos en el momento en que escribo esto.
Aaron Summers
12

En el lado del servidor (middleware Express):

if(err) return res.status(500).end('User already exists.');

Manejar en el lado del cliente

Angular:-

$http().....
.error(function(data, status) {
  console.error('Repos error', status, data);//"Repos error" 500 "User already exists."
});

jQuery: -

$.ajax({
    type: "post",
    url: url,
    success: function (data, text) {
    },
    error: function (request, status, error) {
        alert(request.responseText);
    }
});
vineet
fuente
11

Una forma elegante de manejar errores personalizados como este en Express es:

function errorHandler(err, req, res, next) {
  var code = err.code;
  var message = err.message;
  res.writeHead(code, message, {'content-type' : 'text/plain'});
  res.end(message);
}

(también puede usar express 'incorporado express.errorHandler para esto)

Luego, en su middleware, antes de sus rutas:

app.use(errorHandler);

Luego, donde desea crear el error 'La contraseña actual no coincide':

function checkPassword(req, res, next) {
  // check password, fails:
  var err = new Error('Current password does not match');
  err.code = 400;
  // forward control on to the next registered error handler:
  return next(err);
}
hunterloftis
fuente
err.status = 400; es más común, creo.
mkmelin
11

Puedes usarlo así

return res.status(400).json({'error':'User already exists.'});
Manoj Ojha
fuente
3

Mi caso de uso es enviar un mensaje de error JSON personalizado, ya que estoy usando Express para alimentar mi API REST. Creo que este es un escenario bastante común, por lo que me enfocaré en eso en mi respuesta.

Version corta:

Manejo rápido de errores

Defina el middleware de manejo de errores como otro middleware, excepto con cuatro argumentos en lugar de tres, específicamente con la firma (err, req, res, next). ... Usted define el middleware de manejo de errores en último lugar, después de otras llamadas app.use () y rutas

app.use(function(err, req, res, next) {
    if (err instanceof JSONError) {
      res.status(err.status).json({
        status: err.status,
        message: err.message
      });
    } else {
      next(err);
    }
  });

Genere errores desde cualquier punto del código haciendo:

var JSONError = require('./JSONError');
var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);

Versión larga

La forma canónica de lanzar errores es:

var err = new Error("Uh oh! Can't find something");
err.status = 404;
next(err)

De forma predeterminada, Express maneja esto empaquetándolo cuidadosamente como una Respuesta HTTP con el código 404, y el cuerpo consiste en la cadena del mensaje adjunta con un seguimiento de la pila.

Esto no funciona para mí cuando uso Express como servidor REST, por ejemplo. Querré que el error se devuelva como JSON, no como HTML. Definitivamente tampoco querré que mi seguimiento de pila se mueva a mi cliente.

Puedo enviar JSON como respuesta usando req.json(), por ejemplo. algo como req.json({ status: 404, message: 'Uh oh! Can't find something'}). Opcionalmente, puedo configurar el código de estado usando req.status(). Combinando los dos:

req.status(404).json({ status: 404, message: 'Uh oh! Can't find something'});

Esto funciona a las mil maravillas. Dicho esto, me resulta bastante difícil de escribir cada vez que tengo un error, y el código ya no se auto-documenta como lo next(err)era el nuestro . Parece demasiado similar a cómo se envía una respuesta JSON normal (es decir, válida). Además, cualquier error arrojado por el enfoque canónico aún resulta en una salida HTML.

Aquí es donde entra en juego el middleware de manejo de errores de Express. Como parte de mis rutas, defino:

app.use(function(err, req, res, next) {
    console.log('Someone tried to throw an error response');
  });

También hago una subclase de Error en una clase JSONError personalizada:

JSONError = function (status, message) {
    Error.prototype.constructor.call(this, status + ': ' + message);
    this.status = status;
    this.message = message;
  };
JSONError.prototype = Object.create(Error);
JSONError.prototype.constructor = JSONError;

Ahora, cuando quiero lanzar un error en el código, lo hago:

var err = new JSONError(404, 'Uh oh! Can't find something');
next(err);

Volviendo al middleware de manejo de errores personalizado, lo modifico a:

app.use(function(err, req, res, next) {
  if (err instanceof JSONError) {
    res.status(err.status).json({
      status: err.status,
      message: err.message
    });
  } else {
    next(err);
  }
}

Subclasificar el error en JSONError es importante, ya que sospecho que Express instanceof Errorcomprueba el primer parámetro que se pasa a next()para determinar si se debe invocar un controlador normal o un controlador de errores. Puedo eliminar el instanceof JSONErrorcheque y hacer modificaciones menores para asegurarme de que los errores inesperados (como un bloqueo) también devuelvan una respuesta JSON.

Sharadh
fuente
2

Al usar Axios, puede recuperar el mensaje de respuesta personalizado con:

Axios.get(“your_url”)
.then(data => {
... do something
}.catch( err => {
console.log(err.response.data) // you want this
})

... después de configurarlo en Express como:

res.status(400).send(“your custom message”)
Bravo
fuente
0

Si su objetivo es simplemente reducirlo a una sola línea, puede confiar un poco en los valores predeterminados ...

return res.end(res.writeHead(400, 'Current password does not match'));
Ted Bigham
fuente
-2

Bueno, en el caso de Restify deberíamos usar el sendRaw()método

La sintaxis es: res.sendRaw(200, 'Operation was Successful', <some Header Data> or null)

KNDheeraj
fuente