Comment envoyer un message d'état http personnalisé dans node / express?

89

Mon application node.js est modélisée comme l'application express / examples / mvc .

Dans une action de contrôleur, je veux cracher un état HTTP 400 avec un message http personnalisé. Par défaut, le message d'état http est "Mauvaise demande":

HTTP/1.1 400 Bad Request

Mais je veux envoyer

HTTP/1.1 400 Current password does not match

J'ai essayé différentes méthodes, mais aucune n'a défini le message d'état http sur mon message personnalisé.

Ma fonction actuelle de contrôleur de solution ressemble à ceci:

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;
  } 
  // ...
}

Tout fonctionne bien mais ... cela ne semble pas la bonne façon de le faire.

Existe-t-il un meilleur moyen de définir le message d'état http en utilisant express?

lgersman
la source
4
Eh bien, cela semble être la seule solution de contournement. Mais je ne conseillerais pas quelque chose comme ça, la spécification HTTP 1.1 a sa description d'erreur normalisée pour de bonnes raisons. Je pense que c'est une mauvaise pratique d'envoyer des codes de statut bien connus avec des descriptions personnalisées, mais cela dépend de vous.
schaermu
Hmmm - c'est peut-être vrai. D'un autre côté, je suppose que les navigateurs vérifient simplement le code d'état et non le message d'état http lisible par l'homme. J'ai pensé que c'était une bonne idée d'utiliser le message d'état http pour transporter un message d'erreur concret (c'est-à-dire non par défaut) s'il est disponible. De plus, il est facile de saisir cela en utilisant un script java côté client (en utilisant jQuery, vous pouvez faire "jqXHR.statusText" pour obtenir l'erreur à des fins d'affichage)
lgersman
4
Il ne s'agit pas de compatibilité ou de problèmes potentiels de navigateur, c'est juste une mauvaise pratique;) si vous voulez un message d'erreur à afficher, envoyez-le en tant que corps, c'est le but visé.
schaermu
6
Les descriptions d'erreur spécifiques ne font pas partie de la spécification. Le RCF-2616 indique spécifiquement: "Les valeurs individuelles des codes d'état numériques définis pour HTTP / 1.1, et un exemple d'ensemble de phrases-raisons correspondantes, sont présentées ci-dessous. Les phrases-raisons répertoriées ici ne sont que des recommandations - elles PEUVENT être remplacées par équivalents locaux sans affecter le protocole. "
Ted Bigham
Les phrases de raison personnalisées sont excellentes, mais (puisque votre message est "Le mot de passe actuel ne correspond pas à '"), il semble que vous vouliez réellement le code 401 ici, auquel cas vous n'avez probablement pas besoin de changer le message.
Codebling

Réponses:

59

Vous pouvez consulter cette documentation res.send(400, 'Current password does not match') Look express 3.x pour plus de détails

MISE À JOUR pour Expressjs 4.x

Utilisez cette méthode (regardez les documents 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
la source
41
Malheureusement, cela ne définira pas le message d'état http mais enverra 'Le mot de passe actuel ne correspond pas' comme contenu du corps ...
lgersman
Cela définit l'état HTTP, mais émet un avertissement car cette signature de méthode est obsolète.
nullability
1
L' res.status(400).send('Current password does not match');exemple fonctionne pour moi pour Express 4.
Tyler Collier
Works inExpress ^4.16.2
Ajay
103

Aucune des réponses existantes n'accomplit ce que l'OP demandait à l'origine, c'est-à-dire remplacer la phrase-raison par défaut (le texte apparaissant immédiatement après le code d'état) envoyée par Express.

Ce que tu veux c'est res.statusMessage. Cela ne fait pas partie d'Express, c'est une propriété de l'objet http.Response sous-jacent dans Node.js 0.11+.

Vous pouvez l'utiliser comme ceci (testé dans Express 4.x):

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

Ensuite, utilisez curlpour vérifier que cela fonctionne:

$ 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
la source
6
C'est la bonne façon de définir le statusMessagesur autre chose que le message standard mappé sur le StatusCode
peteb
4
Vous pouvez obtenir la propriété dans l'objet sous-jacent avecres.nativeResponse.statusMessage
sebilasse
@RobertMoskal Testé en utilisant un serveur Express minimal (Express 4.16.1 et Node 12.9.0), et cela fonctionne toujours pour moi. Vérifiez le code de votre application: peut-être que quelque chose ne va pas.
mamacdon
Je ne sais pas pourquoi ce n'est pas la réponse acceptée, car c'est définitivement la solution, du moins au moment où j'écris ceci.
Aaron Summers
12

Côté serveur (middleware Express):

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

Poignée côté client

Angulaire:-

$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);
    }
});
vigne
la source
11

Une manière élégante de gérer les erreurs personnalisées comme celle-ci dans express est:

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);
}

(vous pouvez également utiliser express 'built-in express.errorHandler pour cela)

Puis dans votre middleware, avant vos routes:

app.use(errorHandler);

Ensuite, où vous voulez créer l'erreur «Le mot de passe actuel ne correspond pas»:

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
la source
err.status = 400; est plus commun, je crois.
mkmelin
11

Vous pouvez l'utiliser comme ça

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

Mon cas d'utilisation envoie un message d'erreur JSON personnalisé, car j'utilise express pour alimenter mon API REST. Je pense que c'est un scénario assez courant, alors je vais me concentrer sur cela dans ma réponse.

Version courte:

Traitement des erreurs express

Définissez le middleware de gestion des erreurs comme tout autre middleware, sauf avec quatre arguments au lieu de trois, spécifiquement avec la signature (err, req, res, next). ... Vous définissez le middleware de gestion des erreurs en dernier, après les autres app.use () et achemine les appels

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);
    }
  });

Soulevez des erreurs à partir de n'importe quel point du code en faisant:

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

Version longue

La manière canonique de lancer des erreurs est:

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

Par défaut, Express gère cela en l'empaquetant proprement en tant que réponse HTTP avec le code 404 et un corps composé de la chaîne de message ajoutée avec une trace de pile.

Cela ne fonctionne pas pour moi lorsque j'utilise Express comme serveur REST, par exemple. Je veux que l'erreur soit renvoyée en JSON, pas en HTML. Je ne veux certainement pas non plus que ma trace de pile soit transférée vers mon client.

Je peux envoyer JSON comme réponse en utilisant req.json(), par exemple. quelque chose comme req.json({ status: 404, message: 'Uh oh! Can't find something'}). En option, je peux définir le code d'état à l'aide de req.status(). Combiner les deux:

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

Cela fonctionne comme un charme. Cela dit, je trouve qu'il est assez difficile de taper à chaque fois que j'ai une erreur, et le code n'est plus auto-documenté comme le nôtre next(err). Cela ressemble beaucoup trop à la façon dont une réponse JSON normale (c'est-à-dire valide) est envoyée. En outre, toutes les erreurs générées par l'approche canonique entraînent toujours une sortie HTML.

C'est là qu'intervient le middleware de gestion des erreurs d'Express. Dans le cadre de mes itinéraires, je définis:

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

Je sous-classe également Error dans une classe JSONError personnalisée:

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;

Maintenant, quand je veux lancer une erreur dans le code, je fais:

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

Pour revenir au middleware de gestion des erreurs personnalisé, je le modifie en:

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);
  }
}

L'erreur de sous-classification dans JSONError est importante, car je suppose qu'Express instanceof Errorvérifie le premier paramètre passé à a next()pour déterminer si un gestionnaire normal ou un gestionnaire d'erreurs doit être appelé. Je peux supprimer la instanceof JSONErrorvérification et apporter des modifications mineures pour m'assurer que des erreurs inattendues (comme un crash) renvoient également une réponse JSON.

Sharadh
la source
2

Lorsque vous utilisez Axios, vous pouvez récupérer le message de réponse personnalisé avec:

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

... après l'avoir défini dans Express comme:

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

Si votre objectif est simplement de le réduire à une seule / simple ligne, vous pouvez vous fier un peu aux valeurs par défaut ...

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

Eh bien, dans le cas de Restify, nous devrions utiliser la sendRaw()méthode

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

KNDheeraj
la source