├── .gitignore ├── .npmignore ├── History.md ├── Makefile ├── Readme.md ├── index.js ├── lib └── express-params.js ├── package.json └── test └── test.params.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | *.sock 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | support 2 | test 3 | examples 4 | *.sock 5 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 0.0.3 / 2012-06-26 3 | ================== 4 | 5 | * remove engines field. Closes #2 6 | * update to work with express 3x 7 | 8 | 0.0.2 / 2011-12-16 9 | ================== 10 | 11 | * Added support for node 0.6.x 12 | 13 | 0.0.1 / 2011-05-23 14 | ================== 15 | 16 | * Initial release 17 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | test: 3 | @NODE_ENV=test ./node_modules/expresso/bin/expresso \ 4 | --require should 5 | 6 | .PHONY: test -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | 2 | # express-params 3 | 4 | Express param pre-condition functions. 5 | 6 | ## Installation 7 | 8 | Works with Express 2.5.x 9 | 10 | $ npm install express-params 11 | 12 | ## Usage 13 | 14 | Simply invoke the `extend()` method on an express `HTTPServer` to add this functionality. 15 | 16 | ```javascript 17 | var express = require('express') 18 | , params = require('express-params') 19 | , app = express.createServer(); 20 | 21 | params.extend(app); 22 | ``` 23 | 24 | ## RegExp 25 | 26 | Regular expressions can be used to extract data from pathname 27 | segments as shown below. When matched `req.params.range` contains 28 | the capture groups of the `regexp.exec()` call. 29 | 30 | ```javascript 31 | app.param('range', /^(\w+)\.\.(\w+)?$/); 32 | 33 | app.get('/range/:range', function(req, res, next){ 34 | var range = req.params.range; 35 | res.send('from ' + range[1] + ' to ' + range[2]); 36 | }); 37 | ``` 38 | 39 | Another use-case for regular expression parameters is to validate input, 40 | for example here we may want to route via numeric id, followed by a route 41 | which will accept other values. 42 | 43 | ```javascript 44 | app.param('uid', /^[0-9]+$/); 45 | 46 | app.get('/user/:uid', function(req, res, next){ 47 | var uid = req.params.uid; 48 | res.send('user ' + uid); 49 | }); 50 | 51 | app.get('/user/:name', function(req, res, next){ 52 | var name = req.params.name; 53 | res.send('user ' + name); 54 | }); 55 | ``` 56 | 57 | ## Return Value 58 | 59 | Functions with arity < 3 (less than three parameters) are not 60 | considered to be middleware-style, and are useful for type coercion. 61 | For example below we pass `Number`, a function which accepts a string coercing to a number, alternatively we could use `parseInt` here. The result of `req.params.id` will then be a number, however if we were to issue `GET /user/tj` the result would be `NaN`, which is considered invalid by `exports.invalidParamReturnValue(val)` so `next('route')` is called, ignoring the route. 62 | 63 | ```javascript 64 | app.param('id', Number); 65 | 66 | app.get('/user/:id', function(req, res, next){ 67 | var id = req.params.id; 68 | res.send('typeof ' + typeof id + ' ' + id); 69 | }); 70 | ``` 71 | 72 | The following default logic is applied to test if a return value is invalid: 73 | 74 | ```javascript 75 | return null == val 76 | || false === val 77 | || ('number' == typeof val && isNaN(val)); 78 | ``` 79 | 80 | It's safe to throw in these functions, as connect's router wraps them in a try/catch block, and they are not asynchronous. 81 | 82 | ## Running Tests 83 | 84 | First install dependencies: 85 | 86 | $ npm install -g 87 | 88 | Then run the tests: 89 | 90 | $ make test 91 | 92 | ## License 93 | 94 | (The MIT License) 95 | 96 | Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca> 97 | 98 | Permission is hereby granted, free of charge, to any person obtaining 99 | a copy of this software and associated documentation files (the 100 | 'Software'), to deal in the Software without restriction, including 101 | without limitation the rights to use, copy, modify, merge, publish, 102 | distribute, sublicense, and/or sell copies of the Software, and to 103 | permit persons to whom the Software is furnished to do so, subject to 104 | the following conditions: 105 | 106 | The above copyright notice and this permission notice shall be 107 | included in all copies or substantial portions of the Software. 108 | 109 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 110 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 111 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 112 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 113 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 114 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 115 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = require('./lib/express-params'); -------------------------------------------------------------------------------- /lib/express-params.js: -------------------------------------------------------------------------------- 1 | 2 | /*! 3 | * express-params 4 | * Copyright(c) 2011 TJ Holowaychuk 5 | * MIT Licensed 6 | */ 7 | 8 | /** 9 | * Library version. 10 | */ 11 | 12 | exports.version = '0.0.3'; 13 | 14 | /** 15 | * Add all param functions to `app`. 16 | * 17 | * @param {express.HTTPServer} app 18 | * @api public 19 | */ 20 | 21 | exports.extend = function(app){ 22 | exports.extend.returnValue(app); 23 | exports.extend.regexp(app); 24 | }; 25 | 26 | /** 27 | * Adds `RegExp` support. 28 | * 29 | * @param {express.HTTPServer} app 30 | * @api public 31 | */ 32 | 33 | exports.extend.regexp = function(app){ 34 | app.param(function(name, fn){ 35 | if (fn instanceof RegExp) { 36 | return function(req, res, next, val){ 37 | var captures; 38 | if (captures = fn.exec(String(val))) { 39 | req.params[name] = captures; 40 | next(); 41 | } else { 42 | next('route'); 43 | } 44 | } 45 | } 46 | }); 47 | }; 48 | 49 | /** 50 | * Treat functions with arity < 3 to 51 | * return a value, replacing the raw 52 | * param string. 53 | * 54 | * @param {express.HTTPServer} app 55 | * @api public 56 | */ 57 | 58 | exports.extend.returnValue = function(app){ 59 | app.param(function(name, fn){ 60 | if (fn.length < 3) { 61 | return function(req, res, next, val){ 62 | val = req.params[name] = fn(val); 63 | if (exports.invalidParamReturnValue(val)) { 64 | next('route'); 65 | } else { 66 | next(); 67 | } 68 | }; 69 | } 70 | }); 71 | }; 72 | 73 | /** 74 | * Check if `val` is an invalid return value, aka: 75 | * 76 | * - is `null` or `undefined` 77 | * - is `false` 78 | * - is `NaN` 79 | * 80 | * @param {Mixed} val 81 | * @return {Boolean} 82 | * @api private 83 | */ 84 | 85 | exports.invalidParamReturnValue = function(val){ 86 | return null == val 87 | || false === val 88 | || ('number' == typeof val && isNaN(val)); 89 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "express-params", 3 | "version": "0.0.3", 4 | "description": "Express param functions", 5 | "keywords": [ 6 | "express" 7 | ], 8 | "author": "TJ Holowaychuk ", 9 | "dependencies": {}, 10 | "devDependencies": { 11 | "express": "2.5.x", 12 | "should": "0.3.x", 13 | "expresso": "0.9.x" 14 | }, 15 | "main": "index", 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/visionmedia/express-params.git" 19 | } 20 | } -------------------------------------------------------------------------------- /test/test.params.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var express = require('express') 7 | , should = require('should') 8 | , assert = require('assert') 9 | , params = require('../'); 10 | 11 | module.exports = { 12 | '.version': function(){ 13 | params.version.should.match(/^\d+\.\d+\.\d+$/); 14 | }, 15 | 16 | 'arity < 3': function(){ 17 | var app = express.createServer(); 18 | params.extend(app); 19 | 20 | app.param('id', Number); 21 | app.param('error', function(){ throw new Error('fail') }); 22 | app.param('lib', function(val){ return false; }); 23 | 24 | app.get('/project/:lib', function(req, res, next){ 25 | res.send('project ' + req.params.lib); 26 | }); 27 | 28 | app.get('/user/:id', function(req, res, next){ 29 | var id = req.params.id; 30 | res.send('typeof ' + typeof id + ' ' + id); 31 | }); 32 | 33 | app.get('/error/:error', function(req, res, next){ 34 | 35 | }); 36 | 37 | assert.response(app, 38 | { url: '/project/express' }, 39 | { status: 404 }); 40 | 41 | assert.response(app, 42 | { url: '/user/2' }, 43 | { body: 'typeof number 2' }); 44 | 45 | assert.response(app, 46 | { url: '/user/tj' }, 47 | { status: 404 }); 48 | 49 | assert.response(app, 50 | { url: '/error/hey' }, 51 | { status: 500 }); 52 | }, 53 | 54 | 'regexp': function(){ 55 | var app = express.createServer(); 56 | params.extend(app); 57 | 58 | app.param('range', /^(\w+)\.\.(\w+)?$/); 59 | 60 | app.get('/range/:range', function(req, res, next){ 61 | var range = req.params.range; 62 | res.send('from ' + range[1] + ' to ' + range[2]); 63 | }); 64 | 65 | app.param('username', /^[a-z_]+$/); 66 | 67 | app.get('/user/:username', function(req, res, next){ 68 | var username = req.params.username; 69 | res.send('user ' + username); 70 | }); 71 | 72 | assert.response(app, 73 | { url: '/user/tj' }, 74 | { body: 'user tj' }); 75 | 76 | assert.response(app, 77 | { url: '/user/23' }, 78 | { status: 404 }); 79 | 80 | assert.response(app, 81 | { url: '/range/1..5' }, 82 | { body: 'from 1 to 5' }); 83 | } 84 | } --------------------------------------------------------------------------------