diff --git a/API.md b/API.md index 3b94410c4..e18f5efc8 100755 --- a/API.md +++ b/API.md @@ -1,4 +1,4 @@ -# 14.0.x API Reference +# 14.1.x API Reference - [Server](#server) - [`new Server([options])`](#new-serveroptions) @@ -2134,7 +2134,9 @@ The route configuration object supports the following options: an object with a single key using the name of a registered handler type and value with the options passed to the registered handler. -- `config` - additional [route options](#route-options). +- `config` - additional [route options](#route-options). The `config` value can be an object + or a function that returns an object using the signature `function(server)` where `server` is + the server the route is being added to and `this` is bound to the current realm's `bind` option. Note that the `options` object is deeply cloned (with the exception of `bind` which is shallowly copied) and cannot contain any values that are unsafe to perform deep copy on. diff --git a/lib/route.js b/lib/route.js index 899a5aebe..a53e80cb4 100755 --- a/lib/route.js +++ b/lib/route.js @@ -39,13 +39,19 @@ exports = module.exports = internals.Route = function (route, connection, plugin Hoek.assert(route.path, 'Route missing path'); const routeDisplay = route.method + ' ' + route.path; - Hoek.assert(route.handler || (route.config && route.config.handler), 'Missing or undefined handler:', routeDisplay); - Hoek.assert(!!route.handler ^ !!(route.config && route.config.handler), 'Handler must only appear once:', routeDisplay); // XOR + + let config = route.config; + if (typeof config === 'function') { + config = config.call(realm.settings.bind, connection.server); + } + + Hoek.assert(route.handler || (config && config.handler), 'Missing or undefined handler:', routeDisplay); + Hoek.assert(!!route.handler ^ !!(config && config.handler), 'Handler must only appear once:', routeDisplay); // XOR Hoek.assert(route.path === '/' || route.path[route.path.length - 1] !== '/' || !connection.settings.router.stripTrailingSlash, 'Path cannot end with a trailing slash when connection configured to strip:', routeDisplay); route = Schema.apply('route', route, routeDisplay); - const handler = route.handler || route.config.handler; + const handler = route.handler || config.handler; const method = route.method.toLowerCase(); Hoek.assert(method !== 'head', 'Method name not allowed:', routeDisplay); @@ -54,7 +60,7 @@ exports = module.exports = internals.Route = function (route, connection, plugin const handlerDefaults = Handler.defaults(method, handler, connection.server); let base = Hoek.applyToDefaultsWithShallow(connection.settings.routes, handlerDefaults, ['bind']); base = Hoek.applyToDefaultsWithShallow(base, realm.settings, ['bind']); - this.settings = Hoek.applyToDefaultsWithShallow(base, route.config || {}, ['bind']); + this.settings = Hoek.applyToDefaultsWithShallow(base, config || {}, ['bind']); this.settings.handler = handler; this.settings = Schema.apply('routeConfig', this.settings, routeDisplay); @@ -95,8 +101,8 @@ exports = module.exports = internals.Route = function (route, connection, plugin // Assert on config, not on merged settings - Hoek.assert(!route.config || !route.config.payload, 'Cannot set payload settings on HEAD or GET request:', routeDisplay); - Hoek.assert(!route.config || !route.config.validate || !route.config.validate.payload, 'Cannot validate HEAD or GET requests:', routeDisplay); + Hoek.assert(!config || !config.payload, 'Cannot set payload settings on HEAD or GET request:', routeDisplay); + Hoek.assert(!config || !config.validate || !config.validate.payload, 'Cannot validate HEAD or GET requests:', routeDisplay); validation.payload = null; } diff --git a/lib/schema.js b/lib/schema.js index 25a5f601f..b7d53ca2e 100755 --- a/lib/schema.js +++ b/lib/schema.js @@ -230,7 +230,10 @@ internals.route = Joi.object({ path: Joi.string().required(), vhost: internals.vhost, handler: Joi.any(), // Validated in routeConfig - config: Joi.object().allow(null) + config: Joi.alternatives([ + Joi.object(), + Joi.func() + ]).allow(null) }); diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 8f71125f2..800787ce2 100755 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "hapi", - "version": "14.0.0", + "version": "14.1.0", "dependencies": { "accept": { "version": "2.1.2" diff --git a/package.json b/package.json index df29a13d2..7c921ecc2 100755 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "hapi", "description": "HTTP Server framework", "homepage": "http://hapijs.com", - "version": "14.0.0", + "version": "14.1.0", "repository": { "type": "git", "url": "git://github.com/hapijs/hapi" diff --git a/test/route.js b/test/route.js index fa81b7623..78734bb71 100755 --- a/test/route.js +++ b/test/route.js @@ -26,6 +26,36 @@ const expect = Code.expect; describe('Route', () => { + it('registers with config function', (done) => { + + const server = new Hapi.Server(); + server.connection(); + server.bind({ a: 1 }); + server.app.b = 2; + server.route({ + method: 'GET', + path: '/', + config: function (srv) { + + const a = this.a; + + return { + handler: function (request, reply) { + + return reply(a + srv.app.b); + } + }; + } + }); + + server.inject('/', (res) => { + + expect(res.statusCode).to.equal(200); + expect(res.result).to.equal(3); + done(); + }); + }); + it('throws an error when a route is missing a path', (done) => { expect(() => {