Jak wysłać niestandardowy komunikat o stanie HTTP w Node / Express?

89

Moja aplikacja node.js jest modelowana jak aplikacja express / examples / mvc .

W akcji kontrolera chcę wypluć status HTTP 400 za pomocą niestandardowego komunikatu http. Domyślnie komunikat o stanie http to „Złe żądanie”:

HTTP/1.1 400 Bad Request

Ale chcę wysłać

HTTP/1.1 400 Current password does not match

Próbowałem różnych sposobów, ale żaden z nich nie ustawił komunikatu o stanie http na moją niestandardową wiadomość.

Moja obecna funkcja kontrolera rozwiązania wygląda następująco:

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

Wszystko działa dobrze, ale ... wydaje się, że nie jest to właściwy sposób.

Czy jest lepszy sposób na ustawienie komunikatu o stanie HTTP przy użyciu ekspresu?

lgersman
źródło
4
Cóż, wydaje się, że jest to jedyne obejście. Ale nie radziłbym czegoś takiego, specyfikacja HTTP 1.1 ma ustandaryzowany opis błędu z dobrych powodów. Myślę, że wysyłanie dobrze znanych kodów statusu z niestandardowymi opisami jest złą praktyką, ale to zależy od Ciebie.
schaermu
Hmmm - może to prawda. Z drugiej strony założyłbym, że przeglądarki sprawdzają tylko kod stanu, a nie czytelny dla człowieka komunikat o stanie http. Pomyślałem, że dobrym pomysłem jest użycie komunikatu o stanie http do przesłania konkretnego (tj. Innego niż domyślny) komunikatu o błędzie, jeśli jest dostępny. Plus, że łatwo to złapać za pomocą skryptu java po stronie klienta (używając jQuery możesz zrobić "jqXHR.statusText", aby uzyskać błąd do celów wyświetlania)
lgersman
4
Nie chodzi o kompatybilność czy potencjalne problemy z przeglądarką, to po prostu zła praktyka;) jeśli chcesz otrzymać komunikat o błędzie do wyświetlenia, wyślij go jako treść, taki jest cel.
schaermu
6
Specyficzne opisy błędów nie są częścią specyfikacji. RCF-2616 konkretnie stwierdza: „Poszczególne wartości numerycznych kodów statusu zdefiniowanych dla HTTP / 1.1 i przykładowy zestaw odpowiadających im wyrażeń powodujących są przedstawione poniżej. Podane tutaj wyrażenia powodu są jedynie zaleceniami - MOGĄ zostać zastąpione przez lokalne odpowiedniki bez wpływu na protokół. "
Ted Bigham
Niestandardowe wyrażenia powodujące są świetne, ale (ponieważ Twoja wiadomość brzmi „Bieżące hasło nie pasuje”) wygląda na to, że faktycznie potrzebujesz kodu 401, w takim przypadku prawdopodobnie nie musisz zmieniać wiadomości.
Codebling

Odpowiedzi:

59

Możesz sprawdzić tę dokumentacjęres.send(400, 'Current password does not match') Look express 3.x, aby uzyskać szczegółowe informacje

UPDATE dla Expressjs 4.x

Skorzystaj z tego sposobu (zobacz dokumentację 4.x ):

res.status(400).send('Current password does not match');
// or
res.status(400);
res.send('Current password does not match');
Peter Gerasimenko
źródło
41
niestety nie spowoduje to ustawienia komunikatu o stanie http, ale wyśle ​​wiadomość „Bieżące hasło nie pasuje” jako treść…
lgersman
To ustawia stan HTTP, ale generuje ostrzeżenie, ponieważ ta sygnatura metody jest przestarzała.
nieważność
1
res.status(400).send('Current password does not match');Przykład działa dla mnie Express, 4.
Tyler Collier
Działa wExpress ^4.16.2
Ajay
103

Żadna z istniejących odpowiedzi nie spełnia tego, o co pierwotnie prosił PO, czyli zastąpienia domyślnego wyrażenia powodu (tekst pojawiający się bezpośrednio po kodzie statusu) wysłanego przez Express.

To czego chcesz res.statusMessage. To nie jest część Express, jest to właściwość bazowego obiektu http.Response w Node.js 0.11+.

Możesz go używać w ten sposób (testowane w Express 4.x):

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

Następnie użyj, curlaby sprawdzić, czy działa:

$ 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
źródło
6
To jest poprawny sposób ustawienia statusMessageczegoś innego niż standardowa wiadomość mapowana na StatusCode
peteb
4
Możesz uzyskać nieruchomość w podstawowym obiekcie za pomocąres.nativeResponse.statusMessage
sebilasse
@RobertMoskal Przetestowano przy użyciu minimalnego serwera Express (Express 4.16.1 i Node 12.9.0) i nadal działa. Sprawdź kod aplikacji: może coś innego jest nie tak.
mamacdon
Nie jestem pewien, dlaczego nie jest to akceptowana odpowiedź, ponieważ zdecydowanie jest to rozwiązanie, przynajmniej w momencie, gdy to piszę.
Aaron Summers
12

Po stronie serwera (oprogramowanie pośredniczące Express):

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

Uchwyt po stronie klienta

Kątowy:-

$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);
    }
});
winorośl
źródło
11

Jednym eleganckim sposobem obsługi niestandardowych błędów, takich jak ten w ekspresowym, jest:

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

(możesz również użyć do tego wbudowanego express.errorHandler )

Następnie w oprogramowaniu pośrednim, przed trasami:

app.use(errorHandler);

Następnie, gdzie chcesz utworzyć błąd „Bieżące hasło nie pasuje”:

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
źródło
err.status = 400; jest bardziej powszechne, wierzę.
mkmelin
11

Możesz tego używać w ten sposób

return res.status(400).json({'error':'User already exists.'});
Manoj Ojha
źródło
3

Mój przypadek użycia wysyła niestandardowy komunikat o błędzie JSON, ponieważ używam ekspresu do zasilania mojego interfejsu API REST. Myślę, że jest to dość powszechny scenariusz, więc skupię się na tym w mojej odpowiedzi.

Krótka wersja:

Ekspresowa obsługa błędów

Zdefiniuj oprogramowanie pośredniczące do obsługi błędów, podobnie jak inne oprogramowanie pośredniczące, z wyjątkiem czterech argumentów zamiast trzech, w szczególności z sygnaturą (err, req, res, next). ... Oprogramowanie pośredniczące do obsługi błędów definiuje się jako ostatnie, po innych app.use () i kieruje wywołania

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

Podnieś błędy z dowolnego miejsca w kodzie, wykonując:

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

Długa wersja

Kanoniczny sposób zgłaszania błędów to:

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

Domyślnie Express obsługuje to, starannie pakując je jako odpowiedź HTTP z kodem 404 i treścią składającą się z ciągu komunikatu dołączonego do śladu stosu.

Na przykład to nie działa, gdy używam Express jako serwera REST. Chcę, aby błąd został odesłany w formacie JSON, a nie HTML. Zdecydowanie nie chcę też, aby mój ślad stosu został przeniesiony do mojego klienta.

Mogę wysłać JSON jako odpowiedź używając req.json()np. coś w stylu req.json({ status: 404, message: 'Uh oh! Can't find something'}). Opcjonalnie mogę ustawić kod statusu za pomocą req.status(). Połączenie tych dwóch:

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

To działa jak urok. To powiedziawszy, uważam, że pisanie za każdym razem, gdy mam błąd, jest dość nieporęczne, a kod nie dokumentuje się już samoczynnie, jak nasz next(err). Wygląda to zbyt podobnie do wysyłania normalnej (tj. Prawidłowej) odpowiedzi w formacie JSON. Co więcej, wszelkie błędy zgłaszane przez podejście kanoniczne nadal skutkują wynikiem w postaci HTML.

W tym miejscu pojawia się oprogramowanie pośredniczące do obsługi błędów Express. W ramach moich tras definiuję:

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

Podklasuję również Error do niestandardowej klasy JSONError:

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;

Teraz, gdy chcę zgłosić błąd w kodzie, robię:

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

Wracając do niestandardowego oprogramowania pośredniczącego do obsługi błędów, modyfikuję je, aby:

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

Podklasowanie błędu do JSONError jest ważne, ponieważ podejrzewam, że Express instanceof Errorsprawdza pierwszy parametr przekazany do a, next()aby określić, czy należy wywołać normalną procedurę obsługi lub procedurę obsługi błędów. Mogę usunąć instanceof JSONErrorczek i wprowadzić drobne modyfikacje, aby zapewnić, że nieoczekiwane błędy (takie jak awaria) również zwrócą odpowiedź JSON.

Sharadh
źródło
2

Korzystając z Axios, możesz pobrać niestandardową wiadomość z odpowiedzią za pomocą:

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

... po ustawieniu go w Express jako:

res.status(400).send(“your custom message”)
Brawo
źródło
0

Jeśli Twoim celem jest po prostu zredukowanie go do jednej / prostej linii, możesz trochę polegać na domyślnych ...

return res.end(res.writeHead(400, 'Current password does not match'));
Ted Bigham
źródło
-2

W przypadku Restify powinniśmy użyć sendRaw()metody

Składnia to: res.sendRaw(200, 'Operation was Successful', <some Header Data> or null)

KNDheeraj
źródło