1 | ![Logo](https://github.com/flatiron/director/raw/master/img/director.png)
|
2 |
|
3 | # Synopsis
|
4 |
|
5 | Director is a router. Routing is the process of determining what code to run
|
6 | when a URL is requested.
|
7 |
|
8 | # Motivation
|
9 |
|
10 | A routing library that works in both the browser and node.js environments with
|
11 | as few differences as possible. Simplifies the development of Single Page Apps
|
12 | and Node.js applications. Dependency free (doesn't require jQuery or Express,
|
13 | etc).
|
14 |
|
15 | # Status
|
16 | [![Build Status](https://secure.travis-ci.org/flatiron/director.png?branch=master)](http://travis-ci.org/flatiron/director)
|
17 |
|
18 | # Features
|
19 |
|
20 | * [Client-Side Routing](#client-side-routing)
|
21 | * [Server-Side HTTP Routing](#server-side-http-routing)
|
22 | * [Server-Side CLI Routing](#server-side-cli-routing)
|
23 |
|
24 | # Usage
|
25 |
|
26 | * [API Documentation](#api-documentation)
|
27 | * [Frequently Asked Questions](#faq)
|
28 |
|
29 | ## Building client-side script
|
30 |
|
31 | Run the provided CLI script.
|
32 |
|
33 | ```bash
|
34 | ./bin/build
|
35 | ```
|
36 |
|
37 | ## Client-side Routing
|
38 |
|
39 | It simply watches the hash of the URL to determine what to do, for example:
|
40 |
|
41 | ```
|
42 | http://foo.com/#/bar
|
43 | ```
|
44 |
|
45 | Client-side routing (aka hash-routing) allows you to specify some information
|
46 | about the state of the application using the URL. So that when the user visits
|
47 | a specific URL, the application can be transformed accordingly.
|
48 |
|
49 | ![Hash route](https://github.com/flatiron/director/raw/master/img/hashRoute.png)
|
50 |
|
51 | Here is a simple example:
|
52 |
|
53 | ```html
|
54 | <!DOCTYPE html>
|
55 | <html>
|
56 | <head>
|
57 | <meta charset="utf-8">
|
58 | <title>A Gentle Introduction</title>
|
59 |
|
60 | <script
|
61 | src="https://rawgit.com/flatiron/director/master/build/director.min.js">
|
62 | </script>
|
63 |
|
64 | <script>
|
65 | var author = function () { console.log("author"); };
|
66 | var books = function () { console.log("books"); };
|
67 | var viewBook = function (bookId) {
|
68 | console.log("viewBook: bookId is populated: " + bookId);
|
69 | };
|
70 |
|
71 | var routes = {
|
72 | '/author': author,
|
73 | '/books': [books, function() {
|
74 | console.log("An inline route handler.");
|
75 | }],
|
76 | '/books/view/:bookId': viewBook
|
77 | };
|
78 |
|
79 | var router = Router(routes);
|
80 |
|
81 | router.init();
|
82 | </script>
|
83 | </head>
|
84 |
|
85 | <body>
|
86 | <ul>
|
87 | <li><a href="#/author">#/author</a></li>
|
88 | <li><a href="#/books">#/books</a></li>
|
89 | <li><a href="#/books/view/1">#/books/view/1</a></li>
|
90 | </ul>
|
91 | </body>
|
92 | </html>
|
93 | ```
|
94 |
|
95 | Director works great with your favorite DOM library, such as jQuery.
|
96 |
|
97 | ```html
|
98 | <!DOCTYPE html>
|
99 | <html>
|
100 | <head>
|
101 | <meta charset="utf-8">
|
102 | <title>A Gentle Introduction 2</title>
|
103 |
|
104 | <script
|
105 | src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js">
|
106 | </script>
|
107 |
|
108 | <script
|
109 | src="https://rawgit.com/flatiron/director/master/build/director.min.js">
|
110 | </script>
|
111 |
|
112 | <script>
|
113 | $('document').ready(function() {
|
114 | //
|
115 | // create some functions to be executed when
|
116 | // the correct route is issued by the user.
|
117 | //
|
118 | var showAuthorInfo = function () { console.log("showAuthorInfo"); };
|
119 | var listBooks = function () { console.log("listBooks"); };
|
120 |
|
121 | var allroutes = function() {
|
122 | var route = window.location.hash.slice(2);
|
123 | var sections = $('section');
|
124 | var section;
|
125 |
|
126 | section = sections.filter('[data-route=' + route + ']');
|
127 |
|
128 | if (section.length) {
|
129 | sections.hide(250);
|
130 | section.show(250);
|
131 | }
|
132 | };
|
133 |
|
134 | //
|
135 | // define the routing table.
|
136 | //
|
137 | var routes = {
|
138 | '/author': showAuthorInfo,
|
139 | '/books': listBooks
|
140 | };
|
141 |
|
142 | //
|
143 | // instantiate the router.
|
144 | //
|
145 | var router = Router(routes);
|
146 |
|
147 | //
|
148 | // a global configuration setting.
|
149 | //
|
150 | router.configure({
|
151 | on: allroutes
|
152 | });
|
153 |
|
154 | router.init();
|
155 | });
|
156 | </script>
|
157 | </head>
|
158 |
|
159 | <body>
|
160 | <section data-route="author">Author Name</section>
|
161 | <section data-route="books">Book1, Book2, Book3</section>
|
162 | <ul>
|
163 | <li><a href="#/author">#/author</a></li>
|
164 | <li><a href="#/books">#/books</a></li>
|
165 | </ul>
|
166 | </body>
|
167 | </html>
|
168 | ```
|
169 |
|
170 | You can find a browser-specific build of `director` [here][1] which has all of
|
171 | the server code stripped away.
|
172 |
|
173 | ## Server-Side HTTP Routing
|
174 |
|
175 | Director handles routing for HTTP requests similar to `journey` or `express`:
|
176 |
|
177 | ```js
|
178 | //
|
179 | // require the native http module, as well as director.
|
180 | //
|
181 | var http = require('http'),
|
182 | director = require('director');
|
183 |
|
184 | //
|
185 | // create some logic to be routed to.
|
186 | //
|
187 | function helloWorld() {
|
188 | this.res.writeHead(200, { 'Content-Type': 'text/plain' })
|
189 | this.res.end('hello world');
|
190 | }
|
191 |
|
192 | //
|
193 | // define a routing table.
|
194 | //
|
195 | var router = new director.http.Router({
|
196 | '/hello': {
|
197 | get: helloWorld
|
198 | }
|
199 | });
|
200 |
|
201 | //
|
202 | // setup a server and when there is a request, dispatch the
|
203 | // route that was requested in the request object.
|
204 | //
|
205 | var server = http.createServer(function (req, res) {
|
206 | router.dispatch(req, res, function (err) {
|
207 | if (err) {
|
208 | res.writeHead(404);
|
209 | res.end();
|
210 | }
|
211 | });
|
212 | });
|
213 |
|
214 | //
|
215 | // You can also do ad-hoc routing, similar to `journey` or `express`.
|
216 | // This can be done with a string or a regexp.
|
217 | //
|
218 | router.get('/bonjour', helloWorld);
|
219 | router.get(/hola/, helloWorld);
|
220 |
|
221 | //
|
222 | // set the server to listen on port `8080`.
|
223 | //
|
224 | server.listen(8080);
|
225 | ```
|
226 |
|
227 | ### See Also:
|
228 |
|
229 | - Auto-generated Node.js API Clients for routers using
|
230 | [Director-Reflector](http://github.com/flatiron/director-reflector)
|
231 | - RESTful Resource routing using [restful](http://github.com/flatiron/restful)
|
232 | - HTML / Plain Text views of routers using
|
233 | [Director-Explorer](http://github.com/flatiron/director-explorer)
|
234 |
|
235 | ## Server-Side CLI Routing
|
236 |
|
237 | Director supports Command Line Interface routing. Routes for cli options are
|
238 | based on command line input (i.e. `process.argv`) instead of a URL.
|
239 |
|
240 | ``` js
|
241 | var director = require('director');
|
242 |
|
243 | var router = new director.cli.Router();
|
244 |
|
245 | router.on('create', function () {
|
246 | console.log('create something');
|
247 | });
|
248 |
|
249 | router.on(/destroy/, function () {
|
250 | console.log('destroy something');
|
251 | });
|
252 |
|
253 | // You will need to dispatch the cli arguments yourself
|
254 | router.dispatch('on', process.argv.slice(2).join(' '));
|
255 | ```
|
256 |
|
257 | Using the cli router, you can dispatch commands by passing them as a string.
|
258 | For example, if this example is in a file called `foo.js`:
|
259 |
|
260 | ```bash
|
261 | $ node foo.js create
|
262 | create something
|
263 | $ node foo.js destroy
|
264 | destroy something
|
265 | ```
|
266 |
|
267 | # API Documentation
|
268 |
|
269 | * [Constructor](#constructor)
|
270 | * [Routing Table](#routing-table)
|
271 | * [Adhoc Routing](#adhoc-routing)
|
272 | * [Scoped Routing](#scoped-routing)
|
273 | * [Routing Events](#routing-events)
|
274 | * [Configuration](#configuration)
|
275 | * [URL Matching](#url-matching)
|
276 | * [URL Parameters](#url-parameters)
|
277 | * [Route Recursion](#route-recursion)
|
278 | * [Async Routing](#async-routing)
|
279 | * [Resources](#resources)
|
280 | * [History API](#history-api)
|
281 | * [Instance Methods](#instance-methods)
|
282 | * [Attach Properties to `this`](#attach-to-this)
|
283 | * [HTTP Streaming and Body Parsing](#http-streaming-body-parsing)
|
284 |
|
285 | ## Constructor
|
286 |
|
287 | ``` js
|
288 | var router = Router(routes);
|
289 | ```
|
290 |
|
291 | ## Routing Table
|
292 |
|
293 | An object literal that contains nested route definitions. A potentially nested
|
294 | set of key/value pairs. The keys in the object literal represent each potential
|
295 | part of the URL. The values in the object literal contain references to the
|
296 | functions that should be associated with them. *bark* and *meow* are two
|
297 | functions that you have defined in your code.
|
298 |
|
299 | ``` js
|
300 | //
|
301 | // Assign routes to an object literal.
|
302 | //
|
303 | var routes = {
|
304 | //
|
305 | // a route which assigns the function `bark`.
|
306 | //
|
307 | '/dog': bark,
|
308 | //
|
309 | // a route which assigns the functions `meow` and `scratch`.
|
310 | //
|
311 | '/cat': [meow, scratch]
|
312 | };
|
313 |
|
314 | //
|
315 | // Instantiate the router.
|
316 | //
|
317 | var router = Router(routes);
|
318 | ```
|
319 |
|
320 | ## Adhoc Routing
|
321 |
|
322 | When developing large client-side or server-side applications it is not always
|
323 | possible to define routes in one location. Usually individual decoupled
|
324 | components register their own routes with the application router. We refer to
|
325 | this as _Adhoc Routing._ Lets take a look at the API `director` exposes for
|
326 | adhoc routing:
|
327 |
|
328 | **Client-side Routing**
|
329 |
|
330 | ``` js
|
331 | var router = new Router().init();
|
332 |
|
333 | router.on('/some/resource', function () {
|
334 | //
|
335 | // Do something on `/#/some/resource`
|
336 | //
|
337 | });
|
338 | ```
|
339 |
|
340 | **HTTP Routing**
|
341 |
|
342 | ``` js
|
343 | var router = new director.http.Router();
|
344 |
|
345 | router.get(/\/some\/resource/, function () {
|
346 | //
|
347 | // Do something on an GET to `/some/resource`
|
348 | //
|
349 | });
|
350 | ```
|
351 |
|
352 | ## Scoped Routing
|
353 |
|
354 | In large web appliations, both [Client-side](#client-side) and
|
355 | [Server-side](#http-routing), routes are often scoped within a few individual
|
356 | resources. Director exposes a simple way to do this for [Adhoc
|
357 | Routing](#adhoc-routing) scenarios:
|
358 |
|
359 | ``` js
|
360 | var router = new director.http.Router();
|
361 |
|
362 | //
|
363 | // Create routes inside the `/users` scope.
|
364 | //
|
365 | router.path(/\/users\/(\w+)/, function () {
|
366 | //
|
367 | // The `this` context of the function passed to `.path()`
|
368 | // is the Router itself.
|
369 | //
|
370 |
|
371 | this.post(function (id) {
|
372 | //
|
373 | // Create the user with the specified `id`.
|
374 | //
|
375 | });
|
376 |
|
377 | this.get(function (id) {
|
378 | //
|
379 | // Retreive the user with the specified `id`.
|
380 | //
|
381 | });
|
382 |
|
383 | this.get(/\/friends/, function (id) {
|
384 | //
|
385 | // Get the friends for the user with the specified `id`.
|
386 | //
|
387 | });
|
388 | });
|
389 | ```
|
390 |
|
391 | ## Routing Events
|
392 |
|
393 | In `director`, a "routing event" is a named property in the
|
394 | [Routing Table](#routing-table) which can be assigned to a function or an Array
|
395 | of functions to be called when a route is matched in a call to
|
396 | `router.dispatch()`.
|
397 |
|
398 | * **on:** A function or Array of functions to execute when the route is matched.
|
399 | * **before:** A function or Array of functions to execute before calling the
|
400 | `on` method(s).
|
401 |
|
402 | **Client-side only**
|
403 |
|
404 | * **after:** A function or Array of functions to execute when leaving a
|
405 | particular route.
|
406 | * **once:** A function or Array of functions to execute only once for a
|
407 | particular route.
|
408 |
|
409 | ## Configuration
|
410 |
|
411 | Given the flexible nature of `director` there are several options available for
|
412 | both the [Client-side](#client-side) and [Server-side](#http-routing). These
|
413 | options can be set using the `.configure()` method:
|
414 |
|
415 | ``` js
|
416 | var router = new director.Router(routes).configure(options);
|
417 | ```
|
418 |
|
419 | The `options` are:
|
420 |
|
421 | * **recurse:** Controls [route recursion](#route-recursion). Use `forward`,
|
422 | `backward`, or `false`. Default is `false` Client-side, and `backward`
|
423 | Server-side.
|
424 | * **strict:** If set to `false`, then trailing slashes (or other delimiters)
|
425 | are allowed in routes. Default is `true`.
|
426 | * **async:** Controls [async routing](#async-routing). Use `true` or `false`.
|
427 | Default is `false`.
|
428 | * **delimiter:** Character separator between route fragments. Default is `/`.
|
429 | * **notfound:** A function to call if no route is found on a call to
|
430 | `router.dispatch()`.
|
431 | * **on:** A function (or list of functions) to call on every call to
|
432 | `router.dispatch()` when a route is found.
|
433 | * **before:** A function (or list of functions) to call before every call to
|
434 | `router.dispatch()` when a route is found.
|
435 |
|
436 | **Client-side only**
|
437 |
|
438 | * **resource:** An object to which string-based routes will be bound. This can
|
439 | be especially useful for late-binding to route functions (such as async
|
440 | client-side requires).
|
441 | * **after:** A function (or list of functions) to call when a given route is no
|
442 | longer the active route.
|
443 | * **html5history:** If set to `true` and client supports `pushState()`, then
|
444 | uses HTML5 History API instead of hash fragments. See
|
445 | [History API](#history-api) for more information.
|
446 | * **run_handler_in_init:** If `html5history` is enabled, the route handler by
|
447 | default is executed upon `Router.init()` since with real URIs the router can
|
448 | not know if it should call a route handler or not. Setting this to `false`
|
449 | disables the route handler initial execution.
|
450 | * **convert_hash_in_init:** If `html5history` is enabled, the window.location hash by default is converted to a route upon `Router.init()` since with canonical URIs the router can not know if it should convert the hash to a route or not. Setting this to `false` disables the hash conversion on router initialisation.
|
451 |
|
452 | ## URL Matching
|
453 |
|
454 | ``` js
|
455 | var router = Router({
|
456 | //
|
457 | // given the route '/dog/yella'.
|
458 | //
|
459 | '/dog': {
|
460 | '/:color': {
|
461 | //
|
462 | // this function will return the value 'yella'.
|
463 | //
|
464 | on: function (color) { console.log(color) }
|
465 | }
|
466 | }
|
467 | });
|
468 | ```
|
469 |
|
470 | Routes can sometimes become very complex, `simple/:tokens` don't always
|
471 | suffice. Director supports regular expressions inside the route names. The
|
472 | values captured from the regular expressions are passed to your listener
|
473 | function.
|
474 |
|
475 | ``` js
|
476 | var router = Router({
|
477 | //
|
478 | // given the route '/hello/world'.
|
479 | //
|
480 | '/hello': {
|
481 | '/(\\w+)': {
|
482 | //
|
483 | // this function will return the value 'world'.
|
484 | //
|
485 | on: function (who) { console.log(who) }
|
486 | }
|
487 | }
|
488 | });
|
489 | ```
|
490 |
|
491 | ``` js
|
492 | var router = Router({
|
493 | //
|
494 | // given the route '/hello/world/johny/appleseed'.
|
495 | //
|
496 | '/hello': {
|
497 | '/world/?([^\/]*)\/([^\/]*)/?': function (a, b) {
|
498 | console.log(a, b);
|
499 | }
|
500 | }
|
501 | });
|
502 | ```
|
503 |
|
504 | ## URL Parameters
|
505 |
|
506 | When you are using the same route fragments it is more descriptive to define
|
507 | these fragments by name and then use them in your
|
508 | [Routing Table](#routing-table) or [Adhoc Routes](#adhoc-routing). Consider a
|
509 | simple example where a `userId` is used repeatedly.
|
510 |
|
511 | ``` js
|
512 | //
|
513 | // Create a router. This could also be director.cli.Router() or
|
514 | // director.http.Router().
|
515 | //
|
516 | var router = new director.Router();
|
517 |
|
518 | //
|
519 | // A route could be defined using the `userId` explicitly.
|
520 | //
|
521 | router.on(/([\w-_]+)/, function (userId) { });
|
522 |
|
523 | //
|
524 | // Define a shorthand for this fragment called `userId`.
|
525 | //
|
526 | router.param('userId', /([\\w\\-]+)/);
|
527 |
|
528 | //
|
529 | // Now multiple routes can be defined with the same
|
530 | // regular expression.
|
531 | //
|
532 | router.on('/anything/:userId', function (userId) { });
|
533 | router.on('/something-else/:userId', function (userId) { });
|
534 | ```
|
535 |
|
536 | ## Route Recursion
|
537 |
|
538 | Can be assigned the value of `forward` or `backward`. The recurse option will
|
539 | determine the order in which to fire the listeners that are associated with
|
540 | your routes. If this option is NOT specified or set to null, then only the
|
541 | listeners associated with an exact match will be fired.
|
542 |
|
543 | ### No recursion, with the URL /dog/angry
|
544 |
|
545 | ``` js
|
546 | var routes = {
|
547 | '/dog': {
|
548 | '/angry': {
|
549 | //
|
550 | // Only this method will be fired.
|
551 | //
|
552 | on: growl
|
553 | },
|
554 | on: bark
|
555 | }
|
556 | };
|
557 |
|
558 | var router = Router(routes);
|
559 | ```
|
560 |
|
561 | ### Recursion set to `backward`, with the URL /dog/angry
|
562 |
|
563 | ``` js
|
564 | var routes = {
|
565 | '/dog': {
|
566 | '/angry': {
|
567 | //
|
568 | // This method will be fired first.
|
569 | //
|
570 | on: growl
|
571 | },
|
572 | //
|
573 | // This method will be fired second.
|
574 | //
|
575 | on: bark
|
576 | }
|
577 | };
|
578 |
|
579 | var router = Router(routes).configure({ recurse: 'backward' });
|
580 | ```
|
581 |
|
582 | ### Recursion set to `forward`, with the URL /dog/angry
|
583 |
|
584 | ``` js
|
585 | var routes = {
|
586 | '/dog': {
|
587 | '/angry': {
|
588 | //
|
589 | // This method will be fired second.
|
590 | //
|
591 | on: growl
|
592 | },
|
593 | //
|
594 | // This method will be fired first.
|
595 | //
|
596 | on: bark
|
597 | }
|
598 | };
|
599 |
|
600 | var router = Router(routes).configure({ recurse: 'forward' });
|
601 | ```
|
602 |
|
603 | ### Breaking out of recursion, with the URL /dog/angry
|
604 |
|
605 | ``` js
|
606 | var routes = {
|
607 | '/dog': {
|
608 | '/angry': {
|
609 | //
|
610 | // This method will be fired first.
|
611 | //
|
612 | on: function() { return false; }
|
613 | },
|
614 | //
|
615 | // This method will not be fired.
|
616 | //
|
617 | on: bark
|
618 | }
|
619 | };
|
620 |
|
621 | //
|
622 | // This feature works in reverse with recursion set to true.
|
623 | //
|
624 | var router = Router(routes).configure({ recurse: 'backward' });
|
625 | ```
|
626 |
|
627 | ## Async Routing
|
628 |
|
629 | Before diving into how Director exposes async routing, you should understand
|
630 | [Route Recursion](#route-recursion). At it's core route recursion is about
|
631 | evaluating a series of functions gathered when traversing the [Routing
|
632 | Table](#routing-table).
|
633 |
|
634 | Normally this series of functions is evaluated synchronously. In async routing,
|
635 | these functions are evaluated asynchronously. Async routing can be extremely
|
636 | useful both on the client-side and the server-side:
|
637 |
|
638 | * **Client-side:** To ensure an animation or other async operations (such as
|
639 | HTTP requests for authentication) have completed before continuing evaluation
|
640 | of a route.
|
641 | * **Server-side:** To ensure arbitrary async operations (such as performing
|
642 | authentication) have completed before continuing the evaluation of a route.
|
643 |
|
644 | The method signatures for route functions in synchronous and asynchronous
|
645 | evaluation are different: async route functions take an additional `next()`
|
646 | callback.
|
647 |
|
648 | ### Synchronous route functions
|
649 |
|
650 | ``` js
|
651 | var router = new director.Router();
|
652 |
|
653 | router.on('/:foo/:bar/:bazz', function (foo, bar, bazz) {
|
654 | //
|
655 | // Do something asynchronous with `foo`, `bar`, and `bazz`.
|
656 | //
|
657 | });
|
658 | ```
|
659 |
|
660 | ### Asynchronous route functions
|
661 |
|
662 | ``` js
|
663 | var router = new director.http.Router().configure({ async: true });
|
664 |
|
665 | router.on('/:foo/:bar/:bazz', function (foo, bar, bazz, next) {
|
666 | //
|
667 | // Go do something async, and determine that routing should stop
|
668 | //
|
669 | next(false);
|
670 | });
|
671 | ```
|
672 |
|
673 | ## Resources
|
674 |
|
675 | **Available on the Client-side only.** An object literal containing functions.
|
676 | If a host object is specified, your route definitions can provide string
|
677 | literals that represent the function names inside the host object. A host
|
678 | object can provide the means for better encapsulation and design.
|
679 |
|
680 | ``` js
|
681 |
|
682 | var router = Router({
|
683 |
|
684 | '/hello': {
|
685 | '/usa': 'americas',
|
686 | '/china': 'asia'
|
687 | }
|
688 |
|
689 | }).configure({ resource: container }).init();
|
690 |
|
691 | var container = {
|
692 | americas: function() { return true; },
|
693 | china: function() { return true; }
|
694 | };
|
695 |
|
696 | ```
|
697 |
|
698 | ## History API
|
699 |
|
700 | **Available on the Client-side only.** Director supports using HTML5 History
|
701 | API instead of hash fragments for navigation. To use the API, pass
|
702 | `{html5history: true}` to `configure()`. Use of the API is enabled only if the
|
703 | client supports `pushState()`.
|
704 |
|
705 | Using the API gives you cleaner URIs but they come with a cost. Unlike with
|
706 | hash fragments your route URIs must exist. When the client enters a page, say
|
707 | http://foo.com/bar/baz, the web server must respond with something meaningful.
|
708 | Usually this means that your web server checks the URI points to something
|
709 | that, in a sense, exists, and then serves the client the JavaScript
|
710 | application.
|
711 |
|
712 | If you're after a single-page application you can not use plain old `<a
|
713 | href="/bar/baz">` tags for navigation anymore. When such link is clicked, web
|
714 | browsers try to ask for the resource from server which is not of course desired
|
715 | for a single-page application. Instead you need to use e.g. click handlers and
|
716 | call the `setRoute()` method yourself.
|
717 |
|
718 | ## Attach Properties To `this`
|
719 |
|
720 | **Available in the http router only.** Generally, the `this` object bound to
|
721 | route handlers, will contain the request in `this.req` and the response in
|
722 | `this.res`. One may attach additional properties to `this` with the
|
723 | `router.attach` method:
|
724 |
|
725 | ```js
|
726 | var director = require('director');
|
727 |
|
728 | var router = new director.http.Router().configure(options);
|
729 |
|
730 | //
|
731 | // Attach properties to `this`
|
732 | //
|
733 | router.attach(function () {
|
734 | this.data = [1,2,3];
|
735 | });
|
736 |
|
737 | //
|
738 | // Access properties attached to `this` in your routes!
|
739 | //
|
740 | router.get('/hello', function () {
|
741 | this.res.writeHead(200, { 'content-type': 'text/plain' });
|
742 |
|
743 | //
|
744 | // Response will be `[1,2,3]`!
|
745 | //
|
746 | this.res.end(this.data);
|
747 | });
|
748 | ```
|
749 |
|
750 | This API may be used to attach convenience methods to the `this` context of
|
751 | route handlers.
|
752 |
|
753 | ## HTTP Streaming and Body Parsing
|
754 |
|
755 | When you are performing HTTP routing there are two common scenarios:
|
756 |
|
757 | * Buffer the request body and parse it according to the `Content-Type` header
|
758 | (usually `application/json` or `application/x-www-form-urlencoded`).
|
759 | * Stream the request body by manually calling `.pipe` or listening to the
|
760 | `data` and `end` events.
|
761 |
|
762 | By default `director.http.Router()` will attempt to parse either the `.chunks`
|
763 | or `.body` properties set on the request parameter passed to
|
764 | `router.dispatch(request, response, callback)`. The router instance will also
|
765 | wait for the `end` event before firing any routes.
|
766 |
|
767 | **Default Behavior**
|
768 |
|
769 | ``` js
|
770 | var director = require('director');
|
771 |
|
772 | var router = new director.http.Router();
|
773 |
|
774 | router.get('/', function () {
|
775 | //
|
776 | // This will not work, because all of the data
|
777 | // events and the end event have already fired.
|
778 | //
|
779 | this.req.on('data', function (chunk) {
|
780 | console.log(chunk)
|
781 | });
|
782 | });
|
783 | ```
|
784 |
|
785 | In [flatiron][2], `director` is used in conjunction with [union][3] which uses
|
786 | a `BufferedStream` proxy to the raw `http.Request` instance. [union][3] will
|
787 | set the `req.chunks` property for you and director will automatically parse the
|
788 | body. If you wish to perform this buffering yourself directly with `director`
|
789 | you can use a simple request handler in your http server:
|
790 |
|
791 | ``` js
|
792 | var http = require('http'),
|
793 | director = require('director');
|
794 |
|
795 | var router = new director.http.Router();
|
796 |
|
797 | var server = http.createServer(function (req, res) {
|
798 | req.chunks = [];
|
799 | req.on('data', function (chunk) {
|
800 | req.chunks.push(chunk.toString());
|
801 | });
|
802 |
|
803 | router.dispatch(req, res, function (err) {
|
804 | if (err) {
|
805 | res.writeHead(404);
|
806 | res.end();
|
807 | }
|
808 |
|
809 | console.log('Served ' + req.url);
|
810 | });
|
811 | });
|
812 |
|
813 | router.post('/', function () {
|
814 | this.res.writeHead(200, { 'Content-Type': 'application/json' })
|
815 | this.res.end(JSON.stringify(this.req.body));
|
816 | });
|
817 | ```
|
818 |
|
819 | **Streaming Support**
|
820 |
|
821 | If you wish to get access to the request stream before the `end` event is
|
822 | fired, you can pass the `{ stream: true }` options to the route.
|
823 |
|
824 | ``` js
|
825 | var director = require('director');
|
826 |
|
827 | var router = new director.http.Router();
|
828 |
|
829 | router.get('/', { stream: true }, function () {
|
830 | //
|
831 | // This will work because the route handler is invoked
|
832 | // immediately without waiting for the `end` event.
|
833 | //
|
834 | this.req.on('data', function (chunk) {
|
835 | console.log(chunk);
|
836 | });
|
837 | });
|
838 | ```
|
839 |
|
840 | ## Instance methods
|
841 |
|
842 | ### configure(options)
|
843 |
|
844 | * `options` {Object}: Options to configure this instance with.
|
845 |
|
846 | Configures the Router instance with the specified `options`. See
|
847 | [Configuration](#configuration) for more documentation.
|
848 |
|
849 | ### param(token, matcher)
|
850 |
|
851 | * token {string}: Named parameter token to set to the specified `matcher`
|
852 | * matcher {string|Regexp}: Matcher for the specified `token`.
|
853 |
|
854 | Adds a route fragment for the given string `token` to the specified regex
|
855 | `matcher` to this Router instance. See [URL Parameters](#url-parameters) for more
|
856 | documentation.
|
857 |
|
858 | ### on(method, path, route)
|
859 |
|
860 | * `method` {string}: Method to insert within the Routing Table (e.g. `on`,
|
861 | `get`, etc.).
|
862 | * `path` {string}: Path within the Routing Table to set the `route` to.
|
863 | * `route` {function|Array}: Route handler to invoke for the `method` and `path`.
|
864 |
|
865 | Adds the `route` handler for the specified `method` and `path` within the
|
866 | [Routing Table](#routing-table).
|
867 |
|
868 | ### path(path, routesFn)
|
869 |
|
870 | * `path` {string|Regexp}: Scope within the Routing Table to invoke the
|
871 | `routesFn` within.
|
872 | * `routesFn` {function}: Adhoc Routing function with calls to `this.on()`,
|
873 | `this.get()` etc.
|
874 |
|
875 | Invokes the `routesFn` within the scope of the specified `path` for this Router
|
876 | instance.
|
877 |
|
878 | ### dispatch(method, path[, callback])
|
879 |
|
880 | * method {string}: Method to invoke handlers for within the Routing Table
|
881 | * path {string}: Path within the Routing Table to match
|
882 | * callback {function}: Invoked once all route handlers have been called.
|
883 |
|
884 | Dispatches the route handlers matched within the [Routing Table](#routing-table)
|
885 | for this instance for the specified `method` and `path`.
|
886 |
|
887 | ### mount(routes, path)
|
888 |
|
889 | * routes {object}: Partial routing table to insert into this instance.
|
890 | * path {string|Regexp}: Path within the Routing Table to insert the `routes`
|
891 | into.
|
892 |
|
893 | Inserts the partial [Routing Table](#routing-table), `routes`, into the Routing
|
894 | Table for this Router instance at the specified `path`.
|
895 |
|
896 | ## Instance methods (Client-side only)
|
897 |
|
898 | ### init([redirect])
|
899 |
|
900 | * `redirect` {String}: This value will be used if '/#/' is not found in the
|
901 | URL. (e.g., init('/') will resolve to '/#/', init('foo') will resolve to
|
902 | '/#foo').
|
903 |
|
904 | Initialize the router, start listening for changes to the URL.
|
905 |
|
906 | ### getRoute([index])
|
907 |
|
908 | * `index` {Number}: The hash value is divided by forward slashes, each section
|
909 | then has an index, if this is provided, only that section of the route will
|
910 | be returned.
|
911 |
|
912 | Returns the entire route or just a section of it.
|
913 |
|
914 | ### setRoute(route)
|
915 |
|
916 | * `route` {String}: Supply a route value, such as `home/stats`.
|
917 |
|
918 | Set the current route.
|
919 |
|
920 | ### setRoute(start, length)
|
921 |
|
922 | * `start` {Number} - The position at which to start removing items.
|
923 | * `length` {Number} - The number of items to remove from the route.
|
924 |
|
925 | Remove a segment from the current route.
|
926 |
|
927 | ### setRoute(index, value)
|
928 |
|
929 | * `index` {Number} - The hash value is divided by forward slashes, each section
|
930 | then has an index.
|
931 | * `value` {String} - The new value to assign the the position indicated by the
|
932 | first parameter.
|
933 |
|
934 | Set a segment of the current route.
|
935 |
|
936 | # Frequently Asked Questions
|
937 |
|
938 | ## What About SEO?
|
939 |
|
940 | Is using a Client-side router a problem for SEO? Yes. If advertising is a
|
941 | requirement, you are probably building a "Web Page" and not a "Web
|
942 | Application". Director on the client is meant for script-heavy Web
|
943 | Applications.
|
944 |
|
945 | ##### LICENSE: MIT
|
946 | ##### Author: [Charlie Robbins](https://github.com/indexzero)
|
947 | ##### Contributors: [Paolo Fragomeni](https://github.com/hij1nx)
|
948 |
|
949 | [0]: http://github.com/flatiron/director
|
950 | [1]: https://github.com/flatiron/director/blob/master/build/director.min.js
|
951 | [2]: http://github.com/flatiron/flatiron
|
952 | [3]: http://github.com/flatiron/union
|