125

My node.js app is modeled like the express/examples/mvc app.

In a controller action I want to spit out a HTTP 400 status with a custom http message. By default the http status message is "Bad Request":

HTTP/1.1 400 Bad Request

But I want to send

HTTP/1.1 400 Current password does not match

I tried various ways but none of them set the http status message to my custom message.

My current solution controller function looks like that:

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

Everything works fine but ... it seems not the the right way to do it.

Is there any better way to set the http status message using express ?

6
  • 5
    Well, this seems to be the only workaround. But i wouldn't advice something like that, the HTTP 1.1 spec has it's error description standardized for some good reasons. I think it's bad practice to send well-known status-codes with custom descriptions, but that's up to you. Commented Jan 4, 2013 at 9:54
  • 1
    Hmmm - maybe thats true. On the other hand, I would assume browsers just check the status code and not the human readable http status message. I thought it is a good idea the use the http status message to transport a concrete (i.e. non default) error message if available. Plus that its easy to grab that using client side java script (using jQuery you can do "jqXHR.statusText" to get the error for display purposes) Commented Jan 4, 2013 at 10:03
  • 4
    It's not about compatibility or potential browser issues, it's just bad practice ;) if you want an error message for display, send it as the body, that's the intended purpose. Commented Jan 4, 2013 at 10:37
  • 7
    Specific error descriptions are not part of the spec. RCF-2616 specifically states: "The individual values of the numeric status codes defined for HTTP/1.1, and an example set of corresponding Reason-Phrase's, are presented below. The reason phrases listed here are only recommendations -- they MAY be replaced by local equivalents without affecting the protocol." Commented May 29, 2015 at 23:10
  • 1
    Custom reason phrases are great, but (since your message is "Current password does not match'") it sounds like you actually want code 401 here, in which case you probably don't need to change the message. Commented Nov 3, 2016 at 6:17

8 Answers 8

196

None of the existing answers accomplish what the OP originally asked for, which is to override the default Reason-Phrase (the text appearing immediately after the status code) sent by Express.

What you want is res.statusMessage. This is not part of Express, it's a property of the underlying http.Response object in Node.js 0.11+.

You can use it like this (tested in Express 4.x):

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

Then use curl to verify that it works:

$ 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
Sign up to request clarification or add additional context in comments.

6 Comments

This is the correct way to set the statusMessage to something other than the standard message mapped to the StatusCode
You can get the property in the underlying object with res.nativeResponse.statusMessage
@RobertMoskal Tested using a minimal Express server (Express 4.16.1 and Node 12.9.0), and it still works for me. Check your application code: maybe something else is wrong.
Not sure why this isn't the accepted answer because it definitely is the solution, at least at the time of me writing this.
This should work for HTTP1.1, not HTTP2: nodejs.org/dist/latest-v15.x/docs/api/…
|
80

You can check this res.send(400, 'Current password does not match') Look express 3.x docs for details

UPDATE for Expressjs 4.x

Use this way (look express 4.x docs):

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

6 Comments

unfortunately this will not set the http status message but will send 'Current password does not match' as body content ...
This does set the HTTP status, but throws a warning because this method signature is deprecated.
The res.status(400).send('Current password does not match'); example works for me for Express 4.
Works in Express ^4.16.2
It works, but it's sent in the body, not as "Reason-Phrase" ... see @mamacdon's answer below, the only one who got it right
|
14

You can use it like this

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

1 Comment

That's not what OP asked for; @mamacdon is the only one who got it right
12

One elegant way to handle custom errors like this in express is:

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

(you can also use express' built-in express.errorHandler for this)

Then in your middleware, before your routes:

app.use(errorHandler);

Then where you want to create the error 'Current password does not match':

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

1 Comment

err.status = 400; is more common I belive.
10

At server side(Express middleware):

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

Handle at Client side

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

1 Comment

Again, that's not what OP asked for; @mamacdon is the only one who got it right
6

When using Axios you can retrieve the custom response message with:

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

...after setting it in Express as:

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

2 Comments

so simple, why do the people above you have to make things so complicated.
Because that's not what OP asked for, @iqbal125.... mamacdon is the only one who got it right
3

My use-case is sending a custom JSON error message, since I'm using express to power my REST API. I think this is a fairly common scenario, so will focus on that in my answer.

Short Version:

Express Error Handling

Define error-handling middleware like other middleware, except with four arguments instead of three, specifically with the signature (err, req, res, next). ... You define error-handling middleware last, after other app.use() and routes calls

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

Raise errors from any point in the code by doing:

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

Long Version

The canonical way of throwing errors is:

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

By default, Express handles this by neatly packaging it as a HTTP Response with code 404, and body consisting of the message string appended with a stack trace.

This doesn't work for me when I'm using Express as a REST server, for example. I'll want the error to be sent back as JSON, not as HTML. I'll also definitely not want my stack trace moving out to my client.

I can send JSON as a response using req.json(), eg. something like req.json({ status: 404, message: 'Uh oh! Can't find something'}). Optionally, I can set the status code using req.status(). Combining the two:

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

This works like a charm. That said, I find it quite unwieldy to type every time I have an error, and the code is no longer self-documenting like our next(err) was. It looks far too similar to how a normal (i.e, valid) response JSON is sent. Further, any errors thrown by the canonical approach still result in HTML output.

This is where Express' error handling middleware comes in. As part of my routes, I define:

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

I also subclass Error into a custom JSONError class:

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;

Now, when I want to throw an Error in the code, I do:

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

Going back to the custom error handling middleware, I modify it to:

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

Subclassing Error into JSONError is important, as I suspect Express does an instanceof Error check on the first parameter passed to a next() to determine if a normal handler or an error handler must be invoked. I can remove the instanceof JSONError check and make minor modifications to ensure unexpected errors (such as a crash) also return a JSON response.

1 Comment

Top marks for effort, but it's not what OP asked for. @mamacdon is the only one who got it right
-3

Well in the case of Restify we should use sendRaw() method

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.