From 888d637785ef3e04be0b55162ba62485958310b3 Mon Sep 17 00:00:00 2001 From: Jim Cook Date: Tue, 16 Aug 2011 12:45:40 -0400 Subject: [PATCH] Added support for mounting paths using the typical REST style syntax with no trailing slash. --- lib/middleware/mount.js | 10 +++++++-- test/stick_test.js | 48 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/lib/middleware/mount.js b/lib/middleware/mount.js index 0daece7..1069c0e 100644 --- a/lib/middleware/mount.js +++ b/lib/middleware/mount.js @@ -9,6 +9,11 @@ * that will be matched against the URI path and `Host` header of incoming requests. * _Note that virtual host based mounting has not been tested so far._ * + * The `mount` method accepts an optional third boolean `noRedirect` argument. + * If set to `true` it will disable redirecting GET requests to the mount base + * URL without a trailing slash to the same URL with trailing slash. By default, + * mount middleware will send a redirect to the mount URL with trailing slash. + * * Mounting one application within another causes the `scriptName` and `pathInfo` * properties in the request object to be adjusted so that the mounted application * receives the same pathInfo as if it was the main application. This means @@ -39,7 +44,7 @@ exports.middleware = function Mount(next, app) { var mounts = []; // define mount() method on application object - app.mount = function(spec, target) { + app.mount = function(spec, target, noRedirect) { if (typeof spec === "string") { spec = {path: spec}; } else if (!spec) { @@ -75,6 +80,7 @@ exports.middleware = function Mount(next, app) { }, path: spec.path, canonicalPath: spec.canonicalPath, + redirect: !noRedirect, app: resolved }); mounts.sort(mostSpecificPathFirst); @@ -104,7 +110,7 @@ exports.middleware = function Mount(next, app) { if (mount.match(req)) { // if trailing slash is missing redirect to canonical path - if (req.pathInfo === mount.path && req.method === "GET") { + if (mount.redirect && req.pathInfo === mount.path && req.method === "GET") { var location = req.scriptName + mount.canonicalPath; if (req.queryString) location += "?" + req.queryString; return { diff --git a/test/stick_test.js b/test/stick_test.js index 8ee02d7..422beea 100644 --- a/test/stick_test.js +++ b/test/stick_test.js @@ -67,6 +67,54 @@ exports.testMount = function() { testMount(app); }; +/** +* The default behavior of mount.js middleware will issue a 303 redirect if the +* user enters a mount path which does not end with a slash on a GET request. +* The redirect returns the browser to the same path, but with a trailing slash. +* Not very nice for performance or style when using REST urls. +*/ +exports.testMountRedirect = function() { + var response; + + var app = new Application(); + app.configure(mount); + + app.mount("/", function() { return "root" }); + app.mount("/foo", function() { return "foo" }); + app.mount("/foo/bar", function() { return "foo/bar" }); + + // These URLs should return a 303 response using the default URL treatment + response = app({headers: {}, method: "GET", env: {}, scriptName: "", pathInfo: ""}); + assert.strictEqual(response.status, 303); + assert.strictEqual(response.headers.Location, "/"); + response = app({headers: {}, method: "GET", env: {}, scriptName: "", pathInfo: "/foo"}); + assert.strictEqual(response.status, 303); + assert.strictEqual(response.headers.Location, "/foo/"); + response = app({headers: {}, method: "GET", env: {}, scriptName: "", pathInfo: "/foo/bar"}); + assert.strictEqual(response.status, 303); + assert.strictEqual(response.headers.Location, "/foo/bar/"); +}; + +/** +* When using the mount command, the developer can choose to use REST-style URLs +* without a redirect and without a trailing slash on the end of the URL. +*/ +exports.testMountNoRedirect = function() { + var response; + + var app = new Application(); + app.configure(mount); + + app.mount("/", function() { return "root" }, true); + app.mount("/foo", function() { return "foo" }, true); + app.mount("/foo/bar", function() { return "foo/bar" }, true); + + // Using REST urls, these requests should return the expected content + assert.equal(app({headers: {}, env: {}, method: "GET", pathInfo: ""}), "root"); + assert.equal(app({headers: {}, env: {}, method: "GET", pathInfo: "/foo"}), "foo"); + assert.equal(app({headers: {}, env: {}, method: "GET", pathInfo: "/foo/bar"}), "foo/bar"); +}; + exports.testMountSort = function() { var app = new Application(); app.configure(mount);