Come inviare un messaggio di stato http personalizzato in node / express?


90

La mia app node.js è modellata come l'app express / examples / mvc .

In un'azione del controller voglio sputare uno stato HTTP 400 con un messaggio http personalizzato. Per impostazione predefinita, il messaggio di stato http è "Richiesta non valida":

HTTP/1.1 400 Bad Request

Ma voglio inviare

HTTP/1.1 400 Current password does not match

Ho provato vari modi ma nessuno di loro ha impostato il messaggio di stato http sul mio messaggio personalizzato.

La mia attuale funzione di controller della soluzione ha questo aspetto:

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

Funziona tutto bene ma ... non sembra il modo giusto per farlo.

C'è un modo migliore per impostare il messaggio di stato http utilizzando Express?


4
Bene, questa sembra essere l'unica soluzione alternativa. Ma non consiglierei qualcosa del genere, la specifica HTTP 1.1 ha la sua descrizione dell'errore standardizzata per alcuni buoni motivi. Penso che sia una cattiva pratica inviare codici di stato noti con descrizioni personalizzate, ma dipende da te.
schaermu

Hmmm - forse è vero. D'altra parte, presumo che i browser controllino solo il codice di stato e non il messaggio di stato http leggibile dall'uomo. Ho pensato che fosse una buona idea usare il messaggio di stato http per trasportare un messaggio di errore concreto (cioè non predefinito) se disponibile. Inoltre è facile capirlo utilizzando lo script java lato client (usando jQuery puoi fare "jqXHR.statusText" per ottenere l'errore a scopo di visualizzazione)
lgersman

4
Non si tratta di compatibilità o potenziali problemi del browser, è solo una cattiva pratica;) se vuoi visualizzare un messaggio di errore, invialo come corpo, questo è lo scopo previsto.
schaermu

6
Le descrizioni degli errori specifici non fanno parte delle specifiche. RCF-2616 afferma in particolare: "Di seguito vengono presentati i singoli valori dei codici di stato numerici definiti per HTTP / 1.1 e un insieme di esempi di frasi di motivo corrispondenti. Le frasi di motivazione elencate qui sono solo raccomandazioni: POSSONO essere sostituite da equivalenti locali senza influire sul protocollo. "
Ted Bigham

Le frasi motivo personalizzate sono ottime, ma (dato che il tuo messaggio è "La password corrente non corrisponde a '") sembra che tu voglia effettivamente il codice 401 qui, nel qual caso probabilmente non avrai bisogno di cambiare il messaggio.
Codebling

Risposte:


60

Puoi controllare questa documentazione dires.send(400, 'Current password does not match') Look express 3.x. per i dettagli

AGGIORNAMENTO per Expressjs 4.x

Usa in questo modo (guarda i documenti express 4.x ):

res.status(400).send('Current password does not match');
// or
res.status(400);
res.send('Current password does not match');

41
sfortunatamente questo non imposterà il messaggio di stato http ma invierà "La password corrente non corrisponde" come contenuto del corpo ...
lgersman

Questo imposta lo stato HTTP, ma genera un avviso perché la firma di questo metodo è deprecata.
nullability

1
L' res.status(400).send('Current password does not match');esempio per me funziona per Express 4.
Tyler Collier

Lavora aExpress ^4.16.2
Ajay

105

Nessuna delle risposte esistenti soddisfa ciò che l'OP aveva originariamente richiesto, ovvero sovrascrivere la frase-motivo predefinita (il testo che appare immediatamente dopo il codice di stato) inviata da Express.

Quello che vuoi è res.statusMessage . Non fa parte di Express, è una proprietà dell'oggetto http.Response sottostante in Node.js 0.11+.

Puoi usarlo in questo modo (testato in Express 4.x):

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

Quindi utilizzare curlper verificare che funzioni:

$ 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

6
Questo è il modo corretto per impostare statusMessagesu qualcosa di diverso dal messaggio standard mappato allo StatusCode
peteb

4
È possibile ottenere la proprietà nell'oggetto sottostante conres.nativeResponse.statusMessage
sebilasse

@RobertMoskal Testato utilizzando un server Express minimo (Express 4.16.1 e Nodo 12.9.0) e funziona ancora per me. Controlla il codice dell'applicazione: forse qualcos'altro non va.
mamacdon

Non sono sicuro del motivo per cui questa non è la risposta accettata perché è sicuramente la soluzione, almeno nel momento in cui scrivo questo.
Aaron Summers,

1
Questo dovrebbe funzionare per HTTP1.1, non per HTTP2: nodejs.org/dist/latest-v15.x/docs/api/…
Jokesterfr

12

Sul lato server (middleware Express):

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

Maniglia a lato del cliente

Angolare:-

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

11

Un modo elegante per gestire errori personalizzati come questo in Express è:

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

(puoi anche usare express ' express.errorHandler incorporato per questo)

Quindi nel middleware, prima dei percorsi:

app.use(errorHandler);

Quindi dove si desidera creare l'errore "La password corrente non corrisponde":

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

err.status = 400; è più comune credo.
mkmelin

11

Puoi usarlo in questo modo

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

3

Il mio caso d'uso è l'invio di un messaggio di errore JSON personalizzato, poiché utilizzo Express per alimentare la mia API REST. Penso che questo sia uno scenario abbastanza comune, quindi mi concentrerò su quello nella mia risposta.

Versione breve:

Gestione rapida degli errori

Definisci il middleware di gestione degli errori come un altro middleware, tranne con quattro argomenti invece di tre, in particolare con la firma (err, req, res, next). ... Definisci il middleware di gestione degli errori per ultimo, dopo altre chiamate app.use () e instradati

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

Genera errori da qualsiasi punto del codice eseguendo:

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

Versione lunga

Il modo canonico di lanciare errori è:

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

Per impostazione predefinita, Express gestisce questo impacchettandolo ordinatamente come una risposta HTTP con codice 404 e il corpo costituito dalla stringa del messaggio aggiunta con un'analisi dello stack.

Questo non funziona per me quando utilizzo Express come server REST, ad esempio. Voglio che l'errore venga restituito come JSON, non come HTML. Inoltre, sicuramente non voglio che la mia traccia dello stack venga trasferita al mio client.

Posso inviare JSON come risposta utilizzando req.json(), ad es. qualcosa di simile req.json({ status: 404, message: 'Uh oh! Can't find something'}). Facoltativamente, posso impostare il codice di stato utilizzando req.status(). Combinando i due:

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

Funziona come un fascino. Detto questo, trovo piuttosto ingombrante digitare ogni volta che ho un errore e il codice non si documenta più come il nostro next(err). Sembra troppo simile al modo in cui viene inviato un JSON di risposta normale (cioè valido). Inoltre, qualsiasi errore generato dall'approccio canonico risulta comunque in output HTML.

È qui che entra in gioco il middleware di gestione degli errori di Express. Come parte dei miei percorsi, definisco:

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

Ho anche sottoclasse Error in una classe JSONError personalizzata:

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;

Ora, quando voglio lanciare un errore nel codice, faccio:

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

Tornando al middleware di gestione degli errori personalizzato, lo modifico in:

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

La creazione di sottoclassi di errore in JSONError è importante, poiché sospetto che Express esegua un instanceof Errorcontrollo sul primo parametro passato a a next()per determinare se è necessario richiamare un gestore normale o un gestore di errori. Posso rimuovere il instanceof JSONErrorcontrollo e apportare piccole modifiche per garantire che anche errori imprevisti (come un arresto anomalo) restituiscano una risposta JSON.


3

Quando si utilizza Axios è possibile recuperare il messaggio di risposta personalizzato con:

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

... dopo averlo impostato in Express come:

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

così semplice, perché le persone sopra di te devono rendere le cose così complicate.
iqbal125

0

Se il tuo obiettivo è solo ridurlo a una singola / semplice linea, potresti fare affidamento un po 'sui valori predefiniti ...

return res.end(res.writeHead(400, 'Current password does not match'));

-2

Bene, nel caso di Restify dovremmo usare sendRaw() metodo

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.