Como enviar uma mensagem de status http personalizada em node / express?

90

Meu aplicativo node.js é modelado como o aplicativo express / examples / mvc .

Em uma ação do controlador, quero exibir um status HTTP 400 com uma mensagem http personalizada. Por padrão, a mensagem de status http é "Bad Request":

HTTP/1.1 400 Bad Request

Mas eu quero enviar

HTTP/1.1 400 Current password does not match

Tentei várias maneiras, mas nenhuma delas definiu a mensagem de status http para minha mensagem personalizada.

Minha função de controlador de solução atual é assim:

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

Tudo funciona bem, mas ... parece não ser a maneira certa de fazer isso.

Existe alguma maneira melhor de definir a mensagem de status http usando o expresso?

Lgersman
fonte
4
Bem, esta parece ser a única solução alternativa. Mas eu não aconselharia algo assim, a especificação HTTP 1.1 tem sua descrição de erro padronizada por alguns bons motivos. Eu acho que é uma má prática enviar códigos de status conhecidos com descrições personalizadas, mas isso é com você.
schaermu
Hmmm - talvez seja verdade. Por outro lado, presumo que os navegadores apenas verifiquem o código de status e não a mensagem de status http legível por humanos. Achei uma boa ideia usar a mensagem de status http para transportar uma mensagem de erro concreta (ou seja, não padrão), se disponível. Além disso, é fácil entender isso usando o script java do lado do cliente (usando jQuery, você pode fazer "jqXHR.statusText" para obter o erro para fins de exibição)
lgersman
4
Não se trata de compatibilidade ou problemas potenciais do navegador, é apenas uma prática ruim;) se você quiser uma mensagem de erro para exibição, envie-a como o corpo, esse é o propósito pretendido.
schaermu
6
Descrições de erro específicas não fazem parte das especificações. RCF-2616 declara especificamente: "Os valores individuais dos códigos de status numéricos definidos para HTTP / 1.1, e um conjunto de exemplo de frases-motivo correspondentes, são apresentados abaixo. As frases de razão listadas aqui são apenas recomendações - PODEM ser substituídas por equivalentes locais sem afetar o protocolo. "
Ted Bigham
Frases de motivo personalizadas são ótimas, mas (como sua mensagem é "A senha atual não corresponde a '") parece que você realmente deseja o código 401 aqui, caso em que provavelmente não será necessário alterar a mensagem.
Codebling

Respostas:

60

Você pode verificar esta documentação dores.send(400, 'Current password does not match') Look express 3.x para obter detalhes

ATUALIZAÇÃO para Expressjs 4.x

Use desta forma (veja os 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
fonte
41
infelizmente isso não definirá a mensagem de status http, mas enviará 'A senha atual não corresponde' como conteúdo do corpo ...
lgersman
Isso define o status do HTTP, mas emite um aviso porque essa assinatura de método está obsoleta.
nulidade
1
O res.status(400).send('Current password does not match');exemplo funciona para mim no Express 4.
Tyler Collier
Trabalha emExpress ^4.16.2
Ajay
105

Nenhuma das respostas existentes cumpre o que o OP pediu originalmente, que é substituir o Reason-Phrase padrão (o texto que aparece imediatamente após o código de status) enviado pelo Express.

O que você quer é res.statusMessage. Isso não faz parte do Express, é uma propriedade do objeto http.Response subjacente no Node.js 0.11+.

Você pode usá-lo assim (testado no Express 4.x):

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

Em seguida, use curlpara verificar se 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
fonte
6
Esta é a maneira correta de definir o statusMessagepara algo diferente da mensagem padrão mapeada para o StatusCode
peteb
4
Você pode obter a propriedade no objeto subjacente comres.nativeResponse.statusMessage
sebilasse
@RobertMoskal Testado usando um servidor Express mínimo (Express 4.16.1 e Node 12.9.0), e ainda funciona para mim. Verifique o código do seu aplicativo: talvez algo esteja errado.
mamacdon
Não sei por que essa não é a resposta aceita, porque definitivamente é a solução, pelo menos no momento em que estou escrevendo isso.
Aaron Summers
1
Isso deve funcionar para HTTP1.1, não HTTP2: nodejs.org/dist/latest-v15.x/docs/api/…
Jokesterfr
12

No lado do servidor (middleware Express):

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

Lidar com o 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);
    }
});
vinha
fonte
11

Uma maneira elegante de lidar com erros personalizados como este no expresso é:

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

(você também pode usar express ' express.errorHandler embutido para isso)

Então, em seu middleware, antes de suas rotas:

app.use(errorHandler);

Então, onde você deseja criar o erro 'A senha atual não corresponde':

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
fonte
err.status = 400; é mais comum que eu acredito.
mkmelin
11

Você pode usar assim

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

Meu caso de uso está enviando uma mensagem de erro JSON customizada, já que estou usando o expresso para alimentar minha API REST. Acho que esse é um cenário bastante comum, então vou me concentrar nele em minha resposta.

Versão curta:

Tratamento expresso de erros

Defina o middleware de tratamento de erros como outro middleware, exceto com quatro argumentos em vez de três, especificamente com a assinatura (err, req, res, next). ... Você define o middleware de tratamento de erros por último, depois de outro app.use () e encaminha chamadas

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

Eleve os erros de qualquer ponto do código, fazendo:

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

Versão longa

A maneira canônica de lançar erros é:

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

Por padrão, o Express lida com isso empacotando-o ordenadamente como uma resposta HTTP com o código 404 e o corpo consistindo na string de mensagem anexada a um rastreamento de pilha.

Isso não funciona para mim quando estou usando o Express como um servidor REST, por exemplo. Desejo que o erro seja enviado de volta como JSON, não como HTML. Eu também definitivamente não quero que meu rastreamento de pilha seja transferido para meu cliente.

Posso enviar JSON como resposta usando req.json(), por exemplo. algo parecido req.json({ status: 404, message: 'Uh oh! Can't find something'}). Opcionalmente, posso definir o código de status usando req.status(). Combinando os dois:

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

Isso funciona como um encanto. Dito isso, acho bastante difícil digitar sempre que ocorre um erro, e o código não é mais autodocumentado como o nosso next(err). Parece muito semelhante a como uma resposta JSON normal (ou seja, válida) é enviada. Além disso, quaisquer erros lançados pela abordagem canônica ainda resultam em saída HTML.

É aqui que entra o middleware de tratamento de erros do Express. Como parte de minhas rotas, eu defino:

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

Eu também faço a subclasse de Error em uma classe 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;

Agora, quando quero lançar um erro no código, eu faço:

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

Voltando ao middleware de tratamento de erros personalizado, eu o modifico para:

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

Subclassificar o erro em JSONError é importante, pois suspeito que o Express faz uma instanceof Errorverificação no primeiro parâmetro passado para a next()para determinar se um manipulador normal ou um manipulador de erro deve ser invocado. Posso remover a instanceof JSONErrorverificação e fazer pequenas modificações para garantir que erros inesperados (como uma falha) também retornem uma resposta JSON.

Sharadh
fonte
3

Ao usar o Axios, você pode recuperar a mensagem de resposta personalizada com:

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

... depois de defini-lo no Express como:

res.status(400).send(“your custom message”)
Bravo
fonte
tão simples, por que as pessoas acima de você têm que tornar as coisas tão complicadas.
iqbal125
0

Se o seu objetivo é apenas reduzi-lo a uma linha única / simples, você pode confiar um pouco nos padrões ...

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

Bem, no caso do Restify, devemos usar o sendRaw()método

A sintaxe é: res.sendRaw(200, 'Operation was Successful', <some Header Data> or null)

KNDheeraj
fonte