-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexample.js
More file actions
430 lines (322 loc) · 18.1 KB
/
Copy pathexample.js
File metadata and controls
430 lines (322 loc) · 18.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
/*************************
* Setting Up the Router *
*************************/ console.log("Setting Up the Router\n---------------------");
// require the router module
var orouter = require('./origin-router.js');
// instantiate a new router
var router = new orouter.Router();
/*********************
* Routing URL Paths *
*********************/ console.log("\nRouting URL Paths\n-----------------");
// add routes to the router with corresponding callbacks ...
router.add('/dog', function() { console.log('I have a dog'); });
router.add('/cat', function() { console.log('I have a cat'); });
// route a couple of URL paths ...
router.route('/cat'); // outputs 'I have a cat'
router.route('/dog'); // outputs 'I have a dog'
// attempt to route URL paths that don't match either of the routes ...
router.route('/bulldog'); // outputs nothing
router.route('/dog/bulldog'); // outputs nothing
/**************************
* Routes with Parameters *
**************************/ console.log("\nRoutes with Parameters\n----------------------");
// add a few routes that use ':' to denote parameters ...
router.add('/dog/:color', function(event) {
console.log('I have a ' + event.arguments.color + ' dog'); });
router.add('/cat/:color', function(event) {
console.log('I have a ' + event.arguments.color + ' cat'); });
router.add('/:pet/homework', function(event) {
console.log('My ' + event.arguments.pet + ' ate my homework'); })
// route some URL paths that match the added routes with parameters ...
router.route('/dog/brown'); // outputs 'I have a brown dog'
router.route('cat/white'); // outputs 'I have a white cat'
router.route('/fish/homework'); // outputs 'My fish ate my homework'
router.route('/dog/homework'); // outputs 'I have a homework dog'
// this is routed by the 'dog color' route and not
// the 'homework' route because the 'dog color'
// route was added before the 'homework' route
/***********************************
* Routes with Wildcard Parameters *
***********************************/ console.log("\nRoutes with Wildcard Parameters\n-------------------------------");
// add a route with a wildcard parameter denoted by a '*' at the end ...
router.add('/calico/:pet/:colors*', function(event) {
console.log('I have a ' +
event.arguments.colors.join(',') + ' ' + event.arguments.pet);
});
// the wildcard parameter matches anything at the end of the URL path
// and translates the argument to an array of subpaths ...
router.route('/calico/cat/white/orange/gray'); // outputs
// 'I have a white,orange,gray cat'
/********************************************
* Applying Constraints to Route Parameters *
********************************************/ console.log("\nApplying Constraints to Route Parameters\n----------------------------------------");
// add a route with parameters and corresponding inline regex constraints ...
router.add('/fish/:count<^[0-9]+$>/:colors*<^[a-z]+$>', // total must be numeric
function(event) { // colors must be lower case
console.log('I have ' + event.arguments.count +
' ' + event.arguments.colors.join(' and ') + ' fish');
});
router.route('/fish/12/blue/red'); // outputs 'I have 12 blue and red fish'
router.route('/fish/twelve/blue/RED'); // outputs nothing because
// the count is not numeric and
// one of the colors is upper cased
// add a route with parameters and a corresponding constraints function ...
router.add('/dogs/:count/:breed', // count must be more than 0 to match
{'constraints': function(args) { return parseInt(args.count) > 0; }},
function(event) {
console.log('I have ' +
event.arguments.count + ' ' + event.arguments.breed + 's');
});
router.route('/dogs/0/poodle'); // outputs nothing because the count is invalid
// according to the route's parameter constraints
router.route('/dogs/2/poodle'); // outputs 'I have 2 poodles'
// a route's parameter constraints may be defined per parameter
// as either a function, regular expression or an array of valid strings ...
router.add('cats/:count/:breed',
{'constraints': {'count': /(two|three)/, 'breed': ['persian', 'siamese']}},
function(event) {
console.log('I have ' +
event.arguments.count + ' ' + event.arguments.breed + ' cats');
});
router.route('/cats/four/siamese'); // outputs nothing because the count is invalid
router.route('/cats/two/maltese'); // outputs nothing because the breed is invalid
router.route('/cats/two/persian'); // outputs 'I have two persian cats'
/*****************************
* Handling Trailing Slashes *
*****************************/ console.log("\nHandling Trailing Slashes\n-------------------------");
// add a route to the router that won't match URL paths with a trailing '/' ...
router.add('/lizard/:color', function(event) {
console.log('I have a ' + event.arguments.color + ' lizard'); });
router.route('/lizard/green'); // outputs 'I have a green lizard';
router.route('/lizard/green/'); // outputs nothing
// add a route to the router that will only match URL paths with a trailing '/' ...
router.add('/snake/:colors*/', function(event) {
console.log('I have a ' + event.arguments.colors.join(', ') + ' snake'); });
router.route('/snake/yellow/green'); // outputs nothing
router.route('/snake/yellow/green/'); // outputs 'I have a yellow, green snake';
// add a route that will match URL paths with or without a trailing '/' ...
router.add('/iguana/?', function(event) {
console.log('I have an iguana'); });
router.route('/iguana'); // outputs 'I have an iguana';
router.route('/iguana/'); // outputs 'I have an iguana';
/********************************
* HTTP Method-Specific Routing *
********************************/ console.log("\nHTTP Method-Specific Routing\n----------------------------");
// add a couple of routes that apply to only certain HTTP methods ...
router.add('/fish', {'method': 'GET'},
function() { console.log('I have a fish'); });
router.add('/bird', {'method': ['GET', 'POST']},
function() { console.log('I have a bird'); });
// alternatively routes can be applied for an HTTP method like so ...
router.addGet('/turtle', function() { console.log('I have a turtle'); });
router.addPost('/rabbit', function() { console.log('I have a rabbit'); });
// route URL paths with a corresponding HTTP method specified ...
router.route('/fish', {'method': 'GET'}); // outputs 'I have a fish'
router.route('/fish', {'method': 'POST'}); // outputs nothing
router.route('/bird', {'method': 'GET'}); // outputs 'I have a bird'
router.route('/bird', {'method': 'POST'}); // outputs 'I have a bird'
router.route('/bird', {'method': 'DELETE'}); // outputs nothing
// alternatively a URL path may be routed for an HTTP method like so ...
router.routeGet('/fish'); // outputs 'I have a fish'
router.routePost('/bird'); // outputs 'I have a bird'
// HTTP method-specific routes are still applicable when no method is specified ...
router.route('/fish'); // outputs 'I have a fish'
router.route('/bird'); // outputs 'I have a bird'
/*************************************
* Generating URL Paths using Routes *
*************************************/ console.log("\nGenerating URL Paths using Routes\n---------------------------------");
// add a route and give it a name for future reference ...
router.add('/:pet/mixed/:breeds*', {'name': 'my mix breed'}, function(event) {
console.log('I have a mixed breed ' + event.arguments.pet +
' that is a ' + event.arguments.breeds.join(','));
});
// alternatively the route's name can pe passed as the first argument like so...
router.add('my pure breed', '/:pet/pure/:breed', function(event) {
console.log('I have a pure breed ' + event.arguments.pet +
' that is a ' + event.arguments.breed);
});
// generate a URL path using the route named 'my mix breed' ...
var pathname = router.path('my mix breed', // use the route named 'my mix breed'
{'pet': 'dog', 'breeds': ['beagle', 'pug', 'terrier']}); // parameter arguments
console.log(pathname); // outputs '/dog/mixed/beagle/pug/terrier'
/*******************************************
* Generating URL Paths on the Client-Side *
*******************************************/ console.log("\nGenerating URL Paths on the Client-Side\n---------------------------------------");
// add a route and give it a name for future reference ...
router.add('/:pet/age/:years', {'name': "my pet's age"}, function(event) {
console.log('I have a ' + event.arguments.years + ' year old ' +
event.arguments.pet);
});
// get the source code for the function to generate a URL path using
// the route named "my pet's age" ...
var pathjs = router.pathjs("my pet's age");
// compile the source code into a function using eval for the sake of example,
// typically the source code would not be eval'd but rather included into a
// script or <script> tag that is then sent to and compiled by the client ...
var pathFunction;
eval('pathFunction = ' + pathjs);
// generate a URL by running the compiled function and passing any
// route parameter arguments ...
console.log(pathFunction({'pet': 'cat', 'years': 2})); // outputs /cat/age/2
/******************************
* Working with Route Objects *
******************************/ console.log("\nWorking with Route Objects\n--------------------------");
// a route can be instantiated directly ...
var route = new orouter.Route('/:pet/trick/:tricks*',
{'name': 'tricks', 'method': 'GET'});
// the route instance can then be used to generate a URL path
// without being added to a router ...
var pathname = route.path({'pet': 'dog', 'tricks': ['sit', 'roll']});
console.log(pathname); // outputs '/dog/trick/sit/roll'
// the route can also be added to any router(s) ...
router.add(route, function(event) {
console.log('My ' + event.arguments.pet + "'s best " + event.route.name +
' are ' + event.arguments.tricks.join(' and '));
});
router.routeGet(pathname); // outputs "My dog's best tricks are sit and roll"
/************************************
* Router and Route Events and Data *
************************************/ console.log("\nRouter and Route Events and Data\n--------------------------------");
// know when a route routes a URL path by listening to
// the route's 'route' event ...
var route = router.add('/hamster/:color', {'name': 'hamster'});
route.on('route', function(event) {
console.log('I have a ' + event.arguments.color + ' ' + this.name); });
router.route('/hamster/brown'); // outputs 'I have a brown hamster'
// know when the router is unable to find a matching route to route a URL path
// by listening to the router's 'fail' event ...
router.once('fail', function(event) {
console.log('What is a ' + event.pathname.replace('/', '-') + '?'); });
router.route('guinea/pig'); // outputs 'What is a guinea-pig?'
// alternatively, know when the router successfully routes any URL path by
// listening to the router's 'success' event ...
router.once('success', function(event) {
console.log('My ' + event.route.name + ' is ' + event.arguments.color); });
router.route('/hamster/yellow'); // outputs 'I have a yellow hamster'
// outputs 'My hamster is yellow'
// additionally when routing a URL path, arbitrary data can be attached
// by setting the data object which then will be accessible by any of the
// triggered listeners ...
router.add('mouse', '/mouse/:color', function(event) {
console.log(event.data + ' has a ' + event.arguments.color + ' mouse'); });
router.route('/mouse/white', {'data': 'John'}); // outputs 'John has a white mouse'
/**********************
* About URL Encoding *
**********************/ console.log("\nAbout URL Encoding\n------------------");
// by default, routes should be defined without any URL encodings ...
router.add('/pet name/:name', {'constraints': {'name': ['Pete', 'Mary Jo', 'Al']}},
function(event) { console.log("My pet's name is " + event.arguments.name); });
// when routing a URL path, the path should be in its original URL encoded form ...
router.route('/pet%20name/Pete'); // outputs "My pet's name is Pete"
// route parameter arguments are URL decoded upon matching ...
router.route('/pet%20name/Mary%20Jo'); // outputs "My pet's name is Mary Jo"
// in some cases, a route may need to be defined with URL encoding ...
router.add('/%3adogs%2fcats/:actions*', // 1st subpath is ':dogs/cats' URL encoded
{'encoded': true}, // indicate that the route is URL encoded
function(event) {
console.log('Dogs and cats ' +
event.arguments.actions.join(' and '));
});
router.route('/%3Adogs%2Fcats/run/jump'); // outputs 'Dogs and cats run and jump'
// when generating a URL path from a route, any passed route parameter arguments
// shouldn't contain URL encoding ...
router.add('/pet toys/:pet/:toys*', {'name': 'toys'});
pathname = router.path('toys',
{'pet': 'bengal cat', 'toys': ['ball of yarn', 'catnip']});
// the generated URL path is URL encoded ...
console.log(pathname); // ouputs '/pet%20toys/bengal%20cat/ball%20of%20yarn/catnip'
/**********************************
* Mapping URL Paths to Filepaths *
**********************************/ console.log("\nMapping URL Paths to Filepaths\n------------------------------");
var basejoin = orouter.basejoin; // 'basejoin' utility function
// add a route that will map a portion of the URL path into a filepath referring
// to a file on the filesystem ...
router.add('/pics/:pet/:breed/:dir*', function(event) {
// join the route parameter arguments with a base filepath using the
// 'basejoin' utility function to safely form a filepath restricted to
// be within the base filepath on the file system ...
var filepath = basejoin('../images', // base filepath
event.arguments.pet,
'breeds/small',
event.arguments.breed,
event.arguments.dir);
console.log(filepath);
});
router.route('pics/dog/pug/brown/image.gif');
// outputs '../images/dog/breeds/small/pug/brown/image.gif'
router.route('/pics/dog/malicious/../../../../../../../../../etc/private.conf');
// outputs '../images/etc/private.conf'
/*****************************
* Using with an HTTP Server *
*****************************/ console.log("\nUsing with an HTTP Server\n-------------------------");
var http = require('http');
// instantiate a new router ...
var router = new orouter.Router();
// create the server on port 3000 ...
var server = http.createServer(function(request, response) {
// pass all HTTP requests and corresponding response objects to the router ...
router.route(request, response);
}).listen(3000);
// add a route to show all users ...
router.add('all users', '/users/all', function(event) {
var response = event.response; // passed HTTP response object
// build the HTTP response ...
var entry1 = {'username': 'John Doe', 'pet': 'cat', 'color': 'brown'};
var entry2 = {'username': 'Jane Doe', 'pet': 'dog', 'color': 'white'};
var entry3 = {'username': 'Joe No Show'};
response.writeHead(200, {'Content-Type': 'text/html'});
// list users with links to each user's information on an HTML page ...
response.write(['<html><head></head><body>',
'<h3>Users:</h3>',
// generate URL path to link to the 'user' route for entry1 ...
'<a href="' + router.path('user', entry1) + '">',
entry1.username,
'</a><br />',
// generate URL path to link to the 'user' route for entry2 ...
'<a href="' + router.path('user', entry2) + '">',
entry2.username,
'</a><br />',
// no matching route for the following link ...
'<a href="/user/inactive">',
'<strike>' + entry3.username + '<strike>',
'</a><br />',
'</body></html>'].join('\n'));
response.end();
});
// add another route to show information about a user's pet ...
router.add('user', '/user/:username/:pet/:color', function(event) {
var response = event.response; // passed HTTP response object
// create the response ...
response.writeHead(200, {'Content-Type': 'text/html'});
// show a user's information on an HTML page ...
response.write(['<html><head></head><body>',
// use a few of the route parameter arguments to show as info ...
'<h4>User: ' + event.arguments.username + '</h4>',
event.arguments.username + ' has a ' +
event.arguments.color + ' ' + event.arguments.pet,
'</body></html>'].join('\n'));
response.end();
});
// add a homepage route that will redirect to the show all users page ...
router.add('/?', function(event) {
var response = event.response; // passed HTTP response object
// create the response ...
// generate URL path to redirect to the 'all users' route ...
response.writeHead(302, {'Location': router.path('all users')});
response.end();
});
// catch any requests that do not match a route and show a 404 message ...
router.on('fail', function(event) {
var request = event.request, // passed HTTP request object
response = event.response; // passed HTTP response object
// create the response ...
response.writeHead(404, {'Content-Type': 'text/html'});
// show a not found message on an HTML page ...
response.write(['<html><head></head><body>',
'<h4 style="color: red">',
'Sorry, a page for ' + request.url + " wasn't found",
'</h4>',
'</body></html>'].join('\n'));
response.end();
});
console.log('Browse to http://localhost:3000');