Wie sende ich eine benutzerdefinierte http-Statusnachricht in Node / Express?

85

Meine node.js-App ist wie die express / examples / mvc- App modelliert .

In einer Controller-Aktion möchte ich einen HTTP 400-Status mit einer benutzerdefinierten http-Nachricht ausspucken. Standardmäßig lautet die http-Statusmeldung "Bad Request": Bad Request:

HTTP/1.1 400 Bad Request

Aber ich möchte senden

HTTP/1.1 400 Current password does not match

Ich habe verschiedene Methoden ausprobiert, aber keine davon hat die http-Statusnachricht auf meine benutzerdefinierte Nachricht gesetzt.

Meine aktuelle Solution Controller-Funktion sieht folgendermaßen aus:

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

Alles funktioniert gut, aber ... es scheint nicht der richtige Weg zu sein.

Gibt es eine bessere Möglichkeit, die http-Statusmeldung mit Express festzulegen?

lgersman
quelle
4
Nun, dies scheint die einzige Problemumgehung zu sein. Aber ich würde so etwas nicht empfehlen, die Fehlerbeschreibung der HTTP 1.1-Spezifikation ist aus guten Gründen standardisiert. Ich denke, es ist eine schlechte Praxis, bekannte Statuscodes mit benutzerdefinierten Beschreibungen zu senden, aber das liegt bei Ihnen.
schaermu
Hmmm - vielleicht stimmt das ja. Andererseits würde ich davon ausgehen, dass Browser nur den Statuscode und nicht die vom Menschen lesbare http-Statusmeldung überprüfen. Ich hielt es für eine gute Idee, die http-Statusmeldung zu verwenden, um eine konkrete (dh nicht standardmäßige) Fehlermeldung zu transportieren, falls verfügbar. Außerdem ist es einfach, das mit einem clientseitigen Java-Skript zu erfassen (mit jQuery können Sie "jqXHR.statusText" ausführen, um den Fehler für Anzeigezwecke zu erhalten)
lgersman
4
Es geht nicht um Kompatibilität oder mögliche Browserprobleme, es ist nur eine schlechte Praxis;) Wenn Sie eine Fehlermeldung zur Anzeige wünschen, senden Sie sie als Text, das ist der beabsichtigte Zweck.
schaermu
6
Spezifische Fehlerbeschreibungen sind nicht Teil der Spezifikation. In RCF-2616 heißt es ausdrücklich: "Die einzelnen Werte der für HTTP / 1.1 definierten numerischen Statuscodes und ein Beispielsatz entsprechender Grundphrasen sind nachstehend aufgeführt. Die hier aufgeführten Grundphrasen sind nur Empfehlungen - sie können durch ersetzt werden lokale Äquivalente, ohne das Protokoll zu beeinflussen. "
Ted Bigham
Benutzerdefinierte Grundphrasen sind großartig, aber (da Ihre Nachricht "Aktuelles Passwort stimmt nicht überein" lautet) scheint es, als ob Sie hier tatsächlich Code 401 möchten. In diesem Fall müssen Sie die Nachricht wahrscheinlich nicht ändern.
Codebling

Antworten:

57

Sie können diese res.send(400, 'Current password does not match') Look Express 3.x-Dokumente auf Details überprüfen

UPDATE für Expressjs 4.x.

Verwenden Sie diese Methode (siehe Express 4.x-Dokumente ):

res.status(400).send('Current password does not match');
// or
res.status(400);
res.send('Current password does not match');
Peter Gerasimenko
quelle
40
Leider wird dadurch nicht die http-Statusmeldung gesetzt, sondern "Aktuelles Passwort stimmt nicht überein" als Textinhalt
gesendet
Dadurch wird zwar der HTTP-Status festgelegt, es wird jedoch eine Warnung ausgegeben, da diese Methodensignatur veraltet ist.
Nichtigkeit
1
Das res.status(400).send('Current password does not match');Beispiel funktioniert für mich für Express 4.
Tyler Collier
Arbeitet inExpress ^4.16.2
Ajay
99

Keine der vorhandenen Antworten erfüllt das, was das OP ursprünglich verlangt hatte, nämlich die von Express gesendete Standard -Grundphrase (der Text, der unmittelbar nach dem Statuscode erscheint) zu überschreiben .

Was du willst ist res.statusMessage. Dies ist nicht Teil von Express, sondern eine Eigenschaft des zugrunde liegenden http.Response-Objekts in Node.js 0.11+.

Sie können es folgendermaßen verwenden (getestet in Express 4.x):

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

Verwenden Sie dann, um curlzu überprüfen, ob es funktioniert:

$ 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
quelle
6
Dies ist der richtige Weg, um die statusMessageNachricht auf etwas anderes als die Standardnachricht zu setzen, die dem StatusCode zugeordnet ist
peteb
4
Sie können die Eigenschaft im zugrunde liegenden Objekt mitres.nativeResponse.statusMessage
sebilasse
@RobertMoskal Getestet mit einem minimalen Express-Server (Express 4.16.1 und Node 12.9.0), und es funktioniert immer noch für mich. Überprüfen Sie Ihren Anwendungscode: Möglicherweise stimmt etwas anderes nicht.
Mamacdon
Ich bin mir nicht sicher, warum dies nicht die akzeptierte Antwort ist, da es definitiv die Lösung ist, zumindest zu dem Zeitpunkt, als ich dies schreibe.
Aaron Summers
12

Auf der Serverseite (Express Middleware):

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

Auf Kundenseite handhaben

Winkel: -

$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);
    }
});
Weinstock
quelle
11

Eine elegante Möglichkeit, solche benutzerdefinierten Fehler in Express zu behandeln, ist:

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

(Sie können hierfür auch den in express integrierten Express.errorHandler verwenden. )

Dann in Ihrer Middleware vor Ihren Routen:

app.use(errorHandler);

Wo möchten Sie dann den Fehler "Aktuelles Passwort stimmt nicht überein" erstellen:

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
quelle
err.status = 400; ist häufiger ich glaube.
Mkmelin
11

Sie können es so verwenden

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

Mein Anwendungsfall ist das Senden einer benutzerdefinierten JSON-Fehlermeldung, da ich Express verwende, um meine REST-API mit Strom zu versorgen. Ich denke, dies ist ein ziemlich häufiges Szenario, daher werde ich mich in meiner Antwort darauf konzentrieren.

Kurzfassung:

Express-Fehlerbehandlung

Definieren Sie Middleware zur Fehlerbehandlung wie andere Middleware, außer mit vier statt drei Argumenten, insbesondere mit der Signatur (err, req, res, next). ... Sie definieren die Middleware zur Fehlerbehandlung zuletzt nach anderen app.use () - und leiten Anrufe weiter

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

Erhöhen Sie Fehler an einer beliebigen Stelle im Code, indem Sie Folgendes tun:

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

Lange Version

Die kanonische Art, Fehler zu werfen, ist:

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

Standardmäßig behandelt Express dies, indem es es ordentlich als HTTP-Antwort mit Code 404 verpackt und einen Text enthält, der aus der Nachrichtenzeichenfolge besteht, an die ein Stack-Trace angehängt ist.

Dies funktioniert bei mir nicht, wenn ich beispielsweise Express als REST-Server verwende. Ich möchte, dass der Fehler als JSON und nicht als HTML zurückgesendet wird. Ich möchte auch definitiv nicht, dass mein Stack-Trace auf meinen Client übertragen wird.

Ich kann JSON als Antwort senden req.json(), z. so etwas wie req.json({ status: 404, message: 'Uh oh! Can't find something'}). Optional kann ich den Statuscode mit einstellen req.status(). Kombination der beiden:

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

Das funktioniert wie ein Zauber. Trotzdem finde ich es ziemlich unhandlich, jedes Mal zu tippen, wenn ich einen Fehler habe, und der Code ist nicht mehr so ​​selbstdokumentierend wie unser Code next(err). Es sieht viel zu ähnlich aus, wie eine normale (dh gültige) Antwort JSON gesendet wird. Darüber hinaus führen alle Fehler, die durch den kanonischen Ansatz ausgelöst werden, immer noch zu einer HTML-Ausgabe.

Hier kommt die Fehlerbehandlungs-Middleware von Express ins Spiel. Als Teil meiner Routen definiere ich:

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

Ich habe auch Fehler in eine benutzerdefinierte JSONError-Klasse unterteilt:

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;

Wenn ich jetzt einen Fehler in den Code werfen möchte, mache ich:

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

Zurück zur benutzerdefinierten Middleware zur Fehlerbehandlung ändere ich sie wie folgt:

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

Das Unterklassifizieren von Fehlern in JSONError ist wichtig, da Express vermutlich instanceof Errorden ersten an a übergebenen Parameter überprüft, next()um festzustellen, ob ein normaler Handler oder ein Fehlerhandler aufgerufen werden muss. Ich kann die instanceof JSONErrorPrüfung entfernen und geringfügige Änderungen vornehmen, um sicherzustellen, dass unerwartete Fehler (z. B. ein Absturz) auch eine JSON-Antwort zurückgeben.

Sharadh
quelle
1

Bei Verwendung von Axios können Sie die benutzerdefinierte Antwortnachricht abrufen mit:

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

... nach dem Einstellen in Express als:

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

Wenn Ihr Ziel nur darin besteht, es auf eine einzelne / einfache Zeile zu reduzieren, können Sie sich ein wenig auf die Standardeinstellungen verlassen ...

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

Nun, im Fall von Restify sollten wir die sendRaw()Methode verwenden

Die Syntax lautet: res.sendRaw(200, 'Operation was Successful', <some Header Data> or null)

KNDheeraj
quelle