Skip to content

Commit

Permalink
Merge pull request hapijs#417 from wpreul/feature/unknown
Browse files Browse the repository at this point in the history
Adding notFound handler and server setting
  • Loading branch information
Eran Hammer committed Jan 26, 2013
2 parents bd476ed + ac26631 commit 71fbc06
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 136 deletions.
85 changes: 39 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ Current version: **0.11.3**
- [Router](#router)
- [Payload](#payload)
- [Extensions](#extensions)
- [Unknown Route](#unknown-route)
- [Format](#format)
- [Error Format](#error-format)
- [Payload Format](#payload-format)
Expand All @@ -40,6 +39,8 @@ Current version: **0.11.3**
- [Timeout](#timeout)
<p></p>
- [**Server Events**](#server-events)
<p></p>
- [**Server Route Not Found](#not-found)
<p></p>
- [**Route Configuration**](#route-configuration)
- [Configuration options](#configuration-options)
Expand All @@ -54,6 +55,7 @@ Current version: **0.11.3**
- [View](#view)
- [Docs](#documentation)
- [Request Logging](#request-logging)
- [Not Found](#not-found)
- [Query Validation](#query-validation)
- [Payload Validation](#payload-validation)
- [Path Validation](#path-validation)
Expand Down Expand Up @@ -230,51 +232,6 @@ function onRequest(request, next) {
```


#### Unknown Route

**hapi** provides a default handler for unknown routes (HTTP 404). If the application needs to override the default handler, it can use the
`ext.onUnknownRoute` server option. The extension function signature is _function (request)_ where:
- _'request'_ is the **hapi** request object.

When the extension handler is called, the _'request'_ object is decorated as described in [Route Handler](#route-handler) with the following additional method:
- _'reply.close()'_ - returns control over to the server after the application has taken care of responding to the request via the _request.raw.res_ object directly.

The method **must** return control over to the route using the _reply_ interface described in [Route Handler](#route-handler) or the _'reply.close()'_ method but not both.

For example, using the _'reply.close()'_ method:
```javascript
var Hapi = require('hapi');

var options = {
ext: {
onUnknownRoute: onUnknownRoute
}
};

// Create server
var http = new Hapi.Server('localhost', 8000, options);

// Start server
http.start();

// 404 handler
function onUnknownRoute(request) {

request.raw.res.writeHead(404);
request.raw.res.end();
request.reply.close();
}
```

Or using the _'reply(result)'_ method:
```javascript
function onUnknownRoute(request) {

request.reply({ roads: 'ocean' });
}
```


### Format

The `format` option provides an extension point for use of custom methods to format error responses or payloads before they are sent back to the client.
Expand Down Expand Up @@ -505,6 +462,34 @@ The server object emits the following events:
- _'tail'_ - emitted when a request finished processing, including any registered tails as described in [Request Tails](#request-tails).


## Not Found

**hapi** provides a default handler for unknown routes (HTTP 404). If the application needs to override the default handler, it can use the
`setNotFound` server method. The method takes a route configuration object with a handler property.

Below is an example creating a server and then setting the _'notFound'_ route configuration.

For example, the default notFound route configuration can be set as shown below:
```javascript
var Hapi = require('hapi');

// Create server
var http = new Hapi.Server(8000);

// Set the notFound route configuration
http.setNotFound({ handler: notFoundHandler });

// Start server
http.start();

// 404 handler
function notFoundHandler(request) {

request.reply(Hapi.Error.notFound('The page was not found'));
}
```


## Route Configuration

**hapi** was designed to move as much logic as possible from the route handler to the route configuration. The goal is to provide a simple
Expand Down Expand Up @@ -1180,6 +1165,14 @@ http.start();
The 'request.log' method is always available.


### Not Found

Whenever a route needs to respond with a simple 404 message use the _'notFound'_ handler. This can be done by simply setting the route _'handler'_ property to the string 'notFound'. Below is an example of a route that responds with a 404.
```javascript
{ method: 'GET', path: '/hideme', handler: 'notFound' }
```


### Query Validation

When a request URI includes a query component (the key-value part of the URI between _?_ and _#_), the query is parsed into its individual
Expand Down
11 changes: 0 additions & 11 deletions examples/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,6 @@ internals.onPostRoute = function (request, next) {
};


internals.onUnknownRoute = function (request) {

Hapi.Log.event('onUnknownRoute');
request.reply(Hapi.Error.notFound());
};


internals.main = function () {

var config = {
Expand All @@ -65,10 +58,6 @@ internals.main = function () {
internals.onPreHandler2], // After validation and body parsing, before route handler
onPostHandler: internals.onPostHandler, // After route handler returns, before setting response
onPostRoute: internals.onPostRoute, // After response sent

// Overrides hapi's default handler for unknown route. Cannot be an array!

onUnknownRoute: internals.onUnknownRoute
}
};

Expand Down
5 changes: 1 addition & 4 deletions lib/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,7 @@ exports.server = {
onRequest: null, // New request, before handing over to the router (allows changes to the request method, url, etc.)
onPreHandler: null, // After validation and body parsing, before route handler
onPostHandler: null, // After route handler returns, before sending response
onPostRoute: null, // After response sent

// function (request) { request.reply(result); OR request.reply.close(); }
onUnknownRoute: null // Overrides hapi's default handler for unknown route. Cannot be an array!
onPostRoute: null // After response sent
},

// Response formatter
Expand Down
16 changes: 16 additions & 0 deletions lib/notFound.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Load modules

var Err = require('./error');

// Declare internals

var internals = {};


exports.handler = function (route) {

return function (request) {

return request.reply(Err.notFound('No such path or method ' + request.path));
};
};
52 changes: 0 additions & 52 deletions lib/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,6 @@ internals.Request.prototype._execute = function (route) {

var self = this;

if (!route) {
// 404
return this._unknown();
}

this._route = route;

// Handler wrappers
Expand Down Expand Up @@ -398,53 +393,6 @@ internals.Request.prototype._undecorate = function () {
};


internals.Request.prototype._unknown = function () {

var self = this;

if (!this.server.settings.ext.onUnknownRoute) {

// No extension handler

this._response = Err.notFound('No such path or method ' + this.path);
this._respond();
this._wagTail();
return;
}

// Decorate request with extension helpers

this._decorate(function (response) {

// Called when reply...send() or reply() is called

self._response = response;
self._respond();
self._wagTail();
});

// Unknown-specific chain finalizer

this.reply.close = function () {

self._undecorate();

self.log(['http', 'response', 'ext']);

if (!self._isResponded) {
self.server.emit('response', self);
self._isResponded = true;
}

self._wagTail();
};

// Extension handler

this.server.settings.ext.onUnknownRoute(this);
};


internals.Request.prototype._prerequisites = function (next) {

var self = this;
Expand Down
14 changes: 9 additions & 5 deletions lib/route.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Load modules

var Auth = require('./auth');
var Catbox = require('catbox');
var Utils = require('./utils');
var Proxy = require('./proxy');
var Files = require('./files');
var Auth = require('./auth');
var Docs = require('./docs');
var Files = require('./files');
var NotFound = require('./notFound');
var Proxy = require('./proxy');
var Utils = require('./utils');
var Views = require('./views');


Expand Down Expand Up @@ -37,7 +38,7 @@ exports = module.exports = internals.Route = function (options, server) {
Utils.assert(!!settings.handler ^ !!this.config.handler, 'Handler must appear once and only once'); // XOR
this.config.handler = this.config.handler || settings.handler;

Utils.assert((typeof this.config.handler === 'function') ^ !!this.config.handler.proxy ^ !!this.config.handler.file ^ !!this.config.handler.directory ^ !!this.config.handler.docs ^ !!this.config.handler.view, 'Handler must be a function or an object with a proxy, docs, file, directory or view');
Utils.assert((typeof this.config.handler === 'function') ^ !!this.config.handler.proxy ^ !!this.config.handler.file ^ !!this.config.handler.directory ^ !!this.config.handler.docs ^ !!this.config.handler.view ^ (this.config.handler === 'notFound'), 'Handler must be a function or equal notFound or be an object with a proxy, docs, file, directory, or view');

// Payload configuration ('stream', 'raw', 'parse')
// Default is 'parse' for POST and PUT otherwise 'stream'
Expand Down Expand Up @@ -142,6 +143,9 @@ exports = module.exports = internals.Route = function (options, server) {
this.config.handler = Views.handler(this, this.config.handler.view);
}
}
else if (this.config.handler === 'notFound') {
this.config.handler = NotFound.handler(this);
}

return this;
};
Expand Down
24 changes: 17 additions & 7 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,6 @@ module.exports = internals.Server = function (/* host, port, options */) {
this.settings.debug = Utils.applyToDefaults(Defaults.debug, this.settings.debug);
this.settings.docs = Utils.applyToDefaults(Defaults.docs, this.settings.docs);

// Validate configuration

Utils.assert(!this.settings.ext.onUnknownRoute || this.settings.ext.onUnknownRoute instanceof Array === false, 'ext.onUnknownRoute cannot be an array');

// Initialize process monitoring

Expand All @@ -85,6 +82,7 @@ module.exports = internals.Server = function (/* host, port, options */) {

this._routes = {};
this.routeDefaults = null;
this.setNotFound({ handler: 'notFound' });

// Initialize Views

Expand Down Expand Up @@ -200,7 +198,7 @@ internals.Server.prototype._dispatch = function (options) {
}
}

request._execute();
request._execute(self._routes['*']);
});
};
};
Expand Down Expand Up @@ -240,10 +238,12 @@ internals.Server.prototype._routeTable = function () {

Object.keys(this._routes).forEach(function (method) {

self._routes[method].forEach(function (route) {
if (method !== '*') {
self._routes[method].forEach(function (route) {

flattenedRoutes.push(route);
});
flattenedRoutes.push(route);
});
}
});

return flattenedRoutes;
Expand Down Expand Up @@ -300,6 +300,16 @@ internals.Server.prototype.setRoutesDefaults = function (config) {
};


// Set notFound route for the server

internals.Server.prototype.setNotFound = function (routeConfig) {

Utils.assert(routeConfig && routeConfig.handler, 'routeConfig must exist and provide a handler');

this._routes['*'] = new Route({ method: '*', path: '/{p*}', config: routeConfig }, this);
};


// Add server route

internals.Server.prototype.addRoute = function (options) {
Expand Down
Loading

0 comments on commit 71fbc06

Please sign in to comment.