senchalabs/connect — pull request #954 by bajtos (2013-11-06 15:50:26+00:00)
Title: Use standard error handler for 404 Not Found.

Remove the dummy 404 handler called when no middleware handled the request
and call registered error handlers or *the one* default error
handler instead.

This change preserves backward compatibility. Applications that installed
custom 404 handler as the last middleware in the stack will continue to handle
404 using that middleware.

### Reasoning

#### Principle of Least Astonishment

In the current implementation, users have to install a special 404 middleware as the last one in the stack to get standard error pages for 404. This violates the Principle of Least Astonishment. Consider the following example:

```js
// server
var app = connect();
app.use('/foo', function(req, res, next) {
next(new Error('foo failed'));
});
app.use(connect.errorHandler());
app.listen(3000);
```

```sh
# client
$ curl -H "Accept: application/json" http://localhost:3000/foo
{"error":{"message":"foo failed","stack":"Error..."}}
$curl -H "Accept: application/json" http://localhost:3000/bar
Cannot GET /bar
```

Surprise! Server does not return valid JSON for unknown URLs.

#### No generic solution (middleware)

To get a consistent error reporting (e.g. because we want to build a reliable REST+JSON server), one has to keep in mind to add a special 404 middleware as the last one in the stack. There is no off-the-shelf solution that will allow usage like `app.use(connect.handle404())` and IMO it is not even possible to write it. Such middleware would have to detect what error handlers were already installed by the user, which requires knowledge of connect's internals.

In other words, to handle 404 in the same way as we handle other errors, we must install a new middleware that is tightly coupled with the configured error handlers. Every change of error handling configuration (e.g. replacing `connect.errorHandler()` with something else) requires a change of the 404 handler. It's easy to forget about the second place to change and get inconsistent error responses as the result.

There is another option though. One can use a generic handler that must be added *after* all regular middleware but *before* all error handlers
```js
app.use(function(req, res, next) {
var err = new Error('Cannot ' + req.method + ' ' + req.url);
err.code = 404;
next(err);
});
app.use(connect.errorHandler());
```
This solution does not care about error-handling strategy, but depends on very particular order of middleware handlers. Not to mention that it is in conflict with the usual advice "add a middleware at the very bottom below all the others" (e.g. [Express.js FAQ](http://expressjs.com/faq.html#404-handling)). The new advice would be "add the 404 middleware exactly after all of your regular 3-param middleware but before all of your 4-param error handling middleware". I hope I am not the only one who think that's a silly way?