mirror of
https://github.com/expressjs/express.git
synced 2026-02-26 18:25:02 +00:00
Compare commits
424 Commits
3.0.0alpha
...
3.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d40c65b7f | ||
|
|
58f2057ba7 | ||
|
|
37179109db | ||
|
|
579857cfaa | ||
|
|
0b4e2df480 | ||
|
|
49cc1a70b1 | ||
|
|
f8a33d137a | ||
|
|
2db135dfc7 | ||
|
|
99bc628ad1 | ||
|
|
5ba6c301d7 | ||
|
|
88273a59f8 | ||
|
|
2e53cb72ec | ||
|
|
3b1597d79e | ||
|
|
776ee26bc3 | ||
|
|
ed273448b9 | ||
|
|
4bb91b3f67 | ||
|
|
c5f866098e | ||
|
|
6cfd01be6b | ||
|
|
53b8e25731 | ||
|
|
9e684d45bc | ||
|
|
09d9201787 | ||
|
|
a732d6d471 | ||
|
|
ee9d50c128 | ||
|
|
d1bafa0685 | ||
|
|
2604be5491 | ||
|
|
c52d9cdfbe | ||
|
|
aab6b7e721 | ||
|
|
d13cea46d5 | ||
|
|
82731dae6e | ||
|
|
476fba3e8b | ||
|
|
a566624f2d | ||
|
|
c6d7352f5c | ||
|
|
771573be30 | ||
|
|
b7afa4f0f4 | ||
|
|
4a1fa58704 | ||
|
|
6654b7162c | ||
|
|
f26a3cc806 | ||
|
|
57e48c4767 | ||
|
|
66d9a4ad43 | ||
|
|
78d9c98187 | ||
|
|
b686ec1182 | ||
|
|
158f452b50 | ||
|
|
916acd1dd3 | ||
|
|
b4f612474b | ||
|
|
9a884aa9ee | ||
|
|
db5636199e | ||
|
|
8211562cf6 | ||
|
|
9df93d6dec | ||
|
|
1e251af8d3 | ||
|
|
eed0f598a0 | ||
|
|
ec4d4a792a | ||
|
|
84e745f67c | ||
|
|
97edb23dba | ||
|
|
856782c81c | ||
|
|
a7266392f9 | ||
|
|
04b0c44bdf | ||
|
|
99c9eecde5 | ||
|
|
4d65bbf612 | ||
|
|
956aa0cfff | ||
|
|
d874476f0b | ||
|
|
30f9805539 | ||
|
|
46536dee39 | ||
|
|
24087d94df | ||
|
|
d02df2ebd5 | ||
|
|
16ba1f62a3 | ||
|
|
684dd1a3c6 | ||
|
|
8bcdcfeedd | ||
|
|
3bc372aa33 | ||
|
|
fc1c024041 | ||
|
|
89427228d1 | ||
|
|
420225f370 | ||
|
|
44a3fa6359 | ||
|
|
d853c833f0 | ||
|
|
03a796c460 | ||
|
|
75e47f2883 | ||
|
|
a4b2e48dfe | ||
|
|
57cda1578d | ||
|
|
6b1d7a94ff | ||
|
|
49abd7bec1 | ||
|
|
0ebebd80fe | ||
|
|
d157d47c6e | ||
|
|
e3ac2c5b02 | ||
|
|
cd54faa4af | ||
|
|
5beb1c4e30 | ||
|
|
4031aaa591 | ||
|
|
ba00e23630 | ||
|
|
8beb1f21ef | ||
|
|
fa8eec449b | ||
|
|
ab75fa048e | ||
|
|
bb29da5980 | ||
|
|
a4d7b75129 | ||
|
|
0fdceb3de3 | ||
|
|
480d0064e1 | ||
|
|
17bf04d1ef | ||
|
|
3ab30210a2 | ||
|
|
14fcfdee7e | ||
|
|
d4e56c1fa2 | ||
|
|
39ee6f8e79 | ||
|
|
8d21f1e45c | ||
|
|
618484a4fe | ||
|
|
64a234958a | ||
|
|
e4907ce8e8 | ||
|
|
33eaa8329c | ||
|
|
3c4fd57e51 | ||
|
|
a1e42ac33f | ||
|
|
ce7d7bfd8d | ||
|
|
9bd86cdddc | ||
|
|
0117464ac2 | ||
|
|
6f6eec7d8d | ||
|
|
a4e93c0fb8 | ||
|
|
e2ad0d3d6e | ||
|
|
763be5e631 | ||
|
|
c8526932f3 | ||
|
|
5cf29a3d29 | ||
|
|
18a3cc03ee | ||
|
|
ea5e254c7d | ||
|
|
060653bd4c | ||
|
|
b709009bc9 | ||
|
|
b80d7ec257 | ||
|
|
ff0384e610 | ||
|
|
ab9c275bde | ||
|
|
c70db96b06 | ||
|
|
1c616e29e2 | ||
|
|
cb7518435f | ||
|
|
79f81c0a25 | ||
|
|
72b9e48331 | ||
|
|
eba6aa1767 | ||
|
|
534fbdb307 | ||
|
|
6b309a4457 | ||
|
|
af5e38c31a | ||
|
|
4283f38698 | ||
|
|
2d49c0d1f3 | ||
|
|
a7ca3817db | ||
|
|
1f044547ee | ||
|
|
612fc47044 | ||
|
|
8959ff155b | ||
|
|
dadf57cc8b | ||
|
|
764b6c61d9 | ||
|
|
582dadf787 | ||
|
|
12d97169d7 | ||
|
|
c29cfa823d | ||
|
|
e183a9694e | ||
|
|
d6cb449011 | ||
|
|
7bf4ad30fc | ||
|
|
b3936b96e5 | ||
|
|
44d0625e91 | ||
|
|
c492cde048 | ||
|
|
c2fa6cc94d | ||
|
|
464608025b | ||
|
|
d653d2308b | ||
|
|
a52b1f121c | ||
|
|
9edd8be520 | ||
|
|
aa1d47600a | ||
|
|
be7ec8b40e | ||
|
|
8e12dd9c17 | ||
|
|
fee0f0dce0 | ||
|
|
0dd80e7b50 | ||
|
|
78d5b72081 | ||
|
|
e0df865401 | ||
|
|
2abcbed67a | ||
|
|
02b9d0b2e6 | ||
|
|
927e181db7 | ||
|
|
dc411b3175 | ||
|
|
1e870f0f0d | ||
|
|
3cd01598a5 | ||
|
|
3b6d683b7f | ||
|
|
eeef763f6d | ||
|
|
76d3ec583d | ||
|
|
077d8b9247 | ||
|
|
92840b9f68 | ||
|
|
bf60d5a041 | ||
|
|
56ffab045e | ||
|
|
496ee401d7 | ||
|
|
f0d5b3b368 | ||
|
|
88072a56ea | ||
|
|
0d3a637389 | ||
|
|
dc44f3c226 | ||
|
|
1029b0b97f | ||
|
|
84f8228b8c | ||
|
|
968e8f0121 | ||
|
|
9eb1da4568 | ||
|
|
315e79e9f0 | ||
|
|
363d0d4f41 | ||
|
|
5ac631a270 | ||
|
|
75fc882001 | ||
|
|
d7cb213eeb | ||
|
|
568e0d297a | ||
|
|
8edf358739 | ||
|
|
40be3ed05d | ||
|
|
2c174d6b3b | ||
|
|
60ee465bf7 | ||
|
|
51210d6b95 | ||
|
|
a6caa267bd | ||
|
|
cdb3e9dc0d | ||
|
|
f2f09767fb | ||
|
|
eec31d7126 | ||
|
|
3c5ad753b6 | ||
|
|
dd338b5567 | ||
|
|
1b15af713c | ||
|
|
89c5affc3b | ||
|
|
2bba69f633 | ||
|
|
4403f136b7 | ||
|
|
f7c1c638bb | ||
|
|
f2d7bbe0e9 | ||
|
|
87468cffce | ||
|
|
3f7e3a714e | ||
|
|
38a9caf159 | ||
|
|
500c8ab4b3 | ||
|
|
c4ad97d555 | ||
|
|
b96401ac83 | ||
|
|
32a5c9cba5 | ||
|
|
0217e6ce96 | ||
|
|
6bebe0837f | ||
|
|
ff1c6f0cfa | ||
|
|
8ac3e80bf0 | ||
|
|
3923ec89f7 | ||
|
|
fd8ca32ab2 | ||
|
|
ba2f66d765 | ||
|
|
056c840a06 | ||
|
|
20e8f08cb2 | ||
|
|
bac0c64633 | ||
|
|
48923055eb | ||
|
|
0f20a5e06a | ||
|
|
56bfb9249f | ||
|
|
5ed1544cab | ||
|
|
e5c7be9364 | ||
|
|
73ce9d028c | ||
|
|
75debbe5bc | ||
|
|
5f33d89ea5 | ||
|
|
42fd29efe8 | ||
|
|
2d91eac811 | ||
|
|
a50f02e87d | ||
|
|
214f913b0c | ||
|
|
1021c86300 | ||
|
|
386516815a | ||
|
|
d5e5647bba | ||
|
|
a861ea7eaf | ||
|
|
cb844132e6 | ||
|
|
8050308706 | ||
|
|
e79f72bf88 | ||
|
|
07b6c9f563 | ||
|
|
8f4e61a474 | ||
|
|
54d37c60f5 | ||
|
|
a93d375acc | ||
|
|
d66f0e5eb9 | ||
|
|
e84db12783 | ||
|
|
1ad2ecefe8 | ||
|
|
08b68ec8cd | ||
|
|
48be9233d8 | ||
|
|
a512d9b47d | ||
|
|
62234cc106 | ||
|
|
ab61837885 | ||
|
|
7a5041bf9c | ||
|
|
4d219135b2 | ||
|
|
26eeb64640 | ||
|
|
5426eb0b62 | ||
|
|
b9e32ec2c4 | ||
|
|
32b8613708 | ||
|
|
90eddb3439 | ||
|
|
accd6180c1 | ||
|
|
e770b674ff | ||
|
|
4f7c4d1051 | ||
|
|
78845e7d23 | ||
|
|
93f1cecc92 | ||
|
|
8ccd89f6ad | ||
|
|
0a210cce7a | ||
|
|
f110248462 | ||
|
|
96e4014a70 | ||
|
|
2173d00829 | ||
|
|
3827f5ef8b | ||
|
|
73bed61afa | ||
|
|
ee89ff5026 | ||
|
|
8df9e745d5 | ||
|
|
c76e954504 | ||
|
|
136f054614 | ||
|
|
e9a4b4f8ff | ||
|
|
5415c98ff9 | ||
|
|
39efa452fc | ||
|
|
bddcdee3fe | ||
|
|
2e8d44b444 | ||
|
|
70d68419b0 | ||
|
|
26fab2a27d | ||
|
|
0f5560eebd | ||
|
|
170dcc2907 | ||
|
|
18d6c78ef4 | ||
|
|
1248f0338b | ||
|
|
b400814d00 | ||
|
|
a934929bb3 | ||
|
|
140efb574c | ||
|
|
3c0c96114f | ||
|
|
362c96c8c1 | ||
|
|
5db3d0f9fc | ||
|
|
80c814c393 | ||
|
|
bf66465937 | ||
|
|
72eea7e6cd | ||
|
|
8f4cd13c89 | ||
|
|
6556cefc71 | ||
|
|
7bfb58920a | ||
|
|
2e324ccf5f | ||
|
|
1a10ee76b3 | ||
|
|
619e6349f6 | ||
|
|
b1ff68548f | ||
|
|
376b6c3bad | ||
|
|
f4c8a59b17 | ||
|
|
e6129d8ba5 | ||
|
|
d2baf11b8a | ||
|
|
82b5b12ca7 | ||
|
|
c145ab9b81 | ||
|
|
c10223b803 | ||
|
|
1e09b54ad2 | ||
|
|
d073e0aeb5 | ||
|
|
ce7293de13 | ||
|
|
108e66c24b | ||
|
|
f90401b8c0 | ||
|
|
a32e705d49 | ||
|
|
640cf4ca21 | ||
|
|
442e782692 | ||
|
|
0a874ad8b3 | ||
|
|
18083f0c13 | ||
|
|
f25aaf11e9 | ||
|
|
9b09257b28 | ||
|
|
f895516a2c | ||
|
|
76aa718b75 | ||
|
|
0acee67339 | ||
|
|
4475e335ef | ||
|
|
895673141d | ||
|
|
e4cd99ae1c | ||
|
|
c39a398d83 | ||
|
|
2787bd5bf0 | ||
|
|
6aaa7dc26d | ||
|
|
ebf60d2340 | ||
|
|
02d43846f6 | ||
|
|
8930cd563c | ||
|
|
2b90cd7d51 | ||
|
|
d5fde6a4b9 | ||
|
|
99b2e0fa08 | ||
|
|
ebcb1ca90e | ||
|
|
1763b073f9 | ||
|
|
908e467548 | ||
|
|
3c6ad5350b | ||
|
|
0ff3aa4b20 | ||
|
|
fd42b5c42c | ||
|
|
1311f2ac25 | ||
|
|
82a9817061 | ||
|
|
685eec0149 | ||
|
|
910dae16ab | ||
|
|
fd53197b46 | ||
|
|
d84d0b69ef | ||
|
|
b694ba27be | ||
|
|
e3cbac2d77 | ||
|
|
bbaa295ee2 | ||
|
|
1150a88001 | ||
|
|
58cfd60000 | ||
|
|
fcf268742d | ||
|
|
30d71c8f8f | ||
|
|
c3f9398b12 | ||
|
|
f1ac6ab764 | ||
|
|
d6ca5f71bc | ||
|
|
cdca9cf88f | ||
|
|
5840b42f4a | ||
|
|
a5be68b5b2 | ||
|
|
9fda13bc25 | ||
|
|
125dd7a594 | ||
|
|
df2584cc3b | ||
|
|
4de95c0e7b | ||
|
|
9ed1f2a446 | ||
|
|
833a4873a4 | ||
|
|
6ca1807372 | ||
|
|
9da3e9ccc7 | ||
|
|
5f65c36171 | ||
|
|
d64bb2f886 | ||
|
|
fc179c8fc3 | ||
|
|
8235af47fe | ||
|
|
908f3da3da | ||
|
|
64aac199de | ||
|
|
503c45840f | ||
|
|
6f102ff40f | ||
|
|
6e26a8d366 | ||
|
|
d9aea70ccc | ||
|
|
dd33ef2eb6 | ||
|
|
f3a32f2e29 | ||
|
|
1a3e40d818 | ||
|
|
18cdb3d845 | ||
|
|
0fca62c037 | ||
|
|
e9cd82b72d | ||
|
|
4d9ad21047 | ||
|
|
1cc2dc7150 | ||
|
|
3dc88b2c0c | ||
|
|
53f1ffb4e7 | ||
|
|
579a1be7c8 | ||
|
|
c0a68fcd0d | ||
|
|
b79853e9bd | ||
|
|
f86838ceab | ||
|
|
809e0b8c92 | ||
|
|
a58e3deac2 | ||
|
|
430699c082 | ||
|
|
b04233981f | ||
|
|
de10194f33 | ||
|
|
7c2e1ad0ed | ||
|
|
67ddb429e3 | ||
|
|
a169cc7119 | ||
|
|
9719b58575 | ||
|
|
13c18fa363 | ||
|
|
c17ad6ef65 | ||
|
|
2c14d0c966 | ||
|
|
2cd1783613 | ||
|
|
06db11cd61 | ||
|
|
125421ec45 | ||
|
|
cc84f40d61 | ||
|
|
c130918135 | ||
|
|
babeb4633e | ||
|
|
30167356f2 | ||
|
|
441b309959 | ||
|
|
b7b032f8e0 | ||
|
|
50f43462ae | ||
|
|
bd2a972dba | ||
|
|
3c162ae030 | ||
|
|
3c12757d9e | ||
|
|
fa746cc027 | ||
|
|
1e85178c73 | ||
|
|
d0bc0ad2ca | ||
|
|
c33f1bac08 | ||
|
|
58d522d824 | ||
|
|
858e4dccdc |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -16,3 +16,4 @@ testing
|
||||
.coverage_data
|
||||
cover_html
|
||||
test.js
|
||||
.idea
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.6
|
||||
- "0.8"
|
||||
- "0.10"
|
||||
|
||||
362
History.md
362
History.md
@@ -1,7 +1,239 @@
|
||||
|
||||
3.0.0alpha2 / 2012-04-26
|
||||
3.2.0 / 2013-04-15
|
||||
==================
|
||||
|
||||
* add "view" constructor setting to override view behaviour
|
||||
* add req.acceptsEncoding(name)
|
||||
* add req.acceptedEncodings
|
||||
* revert cookie signature change causing session race conditions
|
||||
* fix sorting of Accept values of the same quality
|
||||
|
||||
3.1.2 / 2013-04-12
|
||||
==================
|
||||
|
||||
* add support for custom Accept parameters
|
||||
* update cookie-signature
|
||||
|
||||
3.1.1 / 2013-04-01
|
||||
==================
|
||||
|
||||
* add X-Forwarded-Host support to `req.host`
|
||||
* fix relative redirects
|
||||
* update mkdirp
|
||||
* update buffer-crc32
|
||||
* remove legacy app.configure() method from app template.
|
||||
|
||||
3.1.0 / 2013-01-25
|
||||
==================
|
||||
|
||||
* add support for leading "." in "view engine" setting
|
||||
* add array support to `res.set()`
|
||||
* add node 0.8.x to travis.yml
|
||||
* add "subdomain offset" setting for tweaking `req.subdomains`
|
||||
* add `res.location(url)` implementing `res.redirect()`-like setting of Location
|
||||
* use app.get() for x-powered-by setting for inheritance
|
||||
* fix colons in passwords for `req.auth`
|
||||
|
||||
3.0.6 / 2013-01-04
|
||||
==================
|
||||
|
||||
* add http verb methods to Router
|
||||
* update connect
|
||||
* fix mangling of the `res.cookie()` options object
|
||||
* fix jsonp whitespace escape. Closes #1132
|
||||
|
||||
3.0.5 / 2012-12-19
|
||||
==================
|
||||
|
||||
* add throwing when a non-function is passed to a route
|
||||
* fix: explicitly remove Transfer-Encoding header from 204 and 304 responses
|
||||
* revert "add 'etag' option"
|
||||
|
||||
3.0.4 / 2012-12-05
|
||||
==================
|
||||
|
||||
* add 'etag' option to disable `res.send()` Etags
|
||||
* add escaping of urls in text/plain in `res.redirect()`
|
||||
for old browsers interpreting as html
|
||||
* change crc32 module for a more liberal license
|
||||
* update connect
|
||||
|
||||
3.0.3 / 2012-11-13
|
||||
==================
|
||||
|
||||
* update connect
|
||||
* update cookie module
|
||||
* fix cookie max-age
|
||||
|
||||
3.0.2 / 2012-11-08
|
||||
==================
|
||||
|
||||
* add OPTIONS to cors example. Closes #1398
|
||||
* fix route chaining regression. Closes #1397
|
||||
|
||||
3.0.1 / 2012-11-01
|
||||
==================
|
||||
|
||||
* update connect
|
||||
|
||||
3.0.0 / 2012-10-23
|
||||
==================
|
||||
|
||||
* add `make clean`
|
||||
* add "Basic" check to req.auth
|
||||
* add `req.auth` test coverage
|
||||
* add cb && cb(payload) to `res.jsonp()`. Closes #1374
|
||||
* add backwards compat for `res.redirect()` status. Closes #1336
|
||||
* add support for `res.json()` to retain previously defined Content-Types. Closes #1349
|
||||
* update connect
|
||||
* change `res.redirect()` to utilize a pathname-relative Location again. Closes #1382
|
||||
* remove non-primitive string support for `res.send()`
|
||||
* fix view-locals example. Closes #1370
|
||||
* fix route-separation example
|
||||
|
||||
3.0.0rc5 / 2012-09-18
|
||||
==================
|
||||
|
||||
* update connect
|
||||
* add redis search example
|
||||
* add static-files example
|
||||
* add "x-powered-by" setting (`app.disable('x-powered-by')`)
|
||||
* add "application/octet-stream" redirect Accept test case. Closes #1317
|
||||
|
||||
3.0.0rc4 / 2012-08-30
|
||||
==================
|
||||
|
||||
* add `res.jsonp()`. Closes #1307
|
||||
* add "verbose errors" option to error-pages example
|
||||
* add another route example to express(1) so people are not so confused
|
||||
* add redis online user activity tracking example
|
||||
* update connect dep
|
||||
* fix etag quoting. Closes #1310
|
||||
* fix error-pages 404 status
|
||||
* fix jsonp callback char restrictions
|
||||
* remove old OPTIONS default response
|
||||
|
||||
3.0.0rc3 / 2012-08-13
|
||||
==================
|
||||
|
||||
* update connect dep
|
||||
* fix signed cookies to work with `connect.cookieParser()` ("s:" prefix was missing) [tnydwrds]
|
||||
* fix `res.render()` clobbering of "locals"
|
||||
|
||||
3.0.0rc2 / 2012-08-03
|
||||
==================
|
||||
|
||||
* add CORS example
|
||||
* update connect dep
|
||||
* deprecate `.createServer()` & remove old stale examples
|
||||
* fix: escape `res.redirect()` link
|
||||
* fix vhost example
|
||||
|
||||
3.0.0rc1 / 2012-07-24
|
||||
==================
|
||||
|
||||
* add more examples to view-locals
|
||||
* add scheme-relative redirects (`res.redirect("//foo.com")`) support
|
||||
* update cookie dep
|
||||
* update connect dep
|
||||
* update send dep
|
||||
* fix `express(1)` -h flag, use -H for hogan. Closes #1245
|
||||
* fix `res.sendfile()` socket error handling regression
|
||||
|
||||
3.0.0beta7 / 2012-07-16
|
||||
==================
|
||||
|
||||
* update connect dep for `send()` root normalization regression
|
||||
|
||||
3.0.0beta6 / 2012-07-13
|
||||
==================
|
||||
|
||||
* add `err.view` property for view errors. Closes #1226
|
||||
* add "jsonp callback name" setting
|
||||
* add support for "/foo/:bar*" non-greedy matches
|
||||
* change `res.sendfile()` to use `send()` module
|
||||
* change `res.send` to use "response-send" module
|
||||
* remove `app.locals.use` and `res.locals.use`, use regular middleware
|
||||
|
||||
3.0.0beta5 / 2012-07-03
|
||||
==================
|
||||
|
||||
* add "make check" support
|
||||
* add route-map example
|
||||
* add `res.json(obj, status)` support back for BC
|
||||
* add "methods" dep, remove internal methods module
|
||||
* update connect dep
|
||||
* update auth example to utilize cores pbkdf2
|
||||
* updated tests to use "supertest"
|
||||
|
||||
3.0.0beta4 / 2012-06-25
|
||||
==================
|
||||
|
||||
* Added `req.auth`
|
||||
* Added `req.range(size)`
|
||||
* Added `res.links(obj)`
|
||||
* Added `res.send(body, status)` support back for backwards compat
|
||||
* Added `.default()` support to `res.format()`
|
||||
* Added 2xx / 304 check to `req.fresh`
|
||||
* Revert "Added + support to the router"
|
||||
* Fixed `res.send()` freshness check, respect res.statusCode
|
||||
|
||||
3.0.0beta3 / 2012-06-15
|
||||
==================
|
||||
|
||||
* Added hogan `--hjs` to express(1) [nullfirm]
|
||||
* Added another example to content-negotiation
|
||||
* Added `fresh` dep
|
||||
* Changed: `res.send()` always checks freshness
|
||||
* Fixed: expose connects mime module. Cloases #1165
|
||||
|
||||
3.0.0beta2 / 2012-06-06
|
||||
==================
|
||||
|
||||
* Added `+` support to the router
|
||||
* Added `req.host`
|
||||
* Changed `req.param()` to check route first
|
||||
* Update connect dep
|
||||
|
||||
3.0.0beta1 / 2012-06-01
|
||||
==================
|
||||
|
||||
* Added `res.format()` callback to override default 406 behaviour
|
||||
* Fixed `res.redirect()` 406. Closes #1154
|
||||
|
||||
3.0.0alpha5 / 2012-05-30
|
||||
==================
|
||||
|
||||
* Added `req.ip`
|
||||
* Added `{ signed: true }` option to `res.cookie()`
|
||||
* Removed `res.signedCookie()`
|
||||
* Changed: dont reverse `req.ips`
|
||||
* Fixed "trust proxy" setting check for `req.ips`
|
||||
|
||||
3.0.0alpha4 / 2012-05-09
|
||||
==================
|
||||
|
||||
* Added: allow `[]` in jsonp callback. Closes #1128
|
||||
* Added `PORT` env var support in generated template. Closes #1118 [benatkin]
|
||||
* Updated: connect 2.2.2
|
||||
|
||||
3.0.0alpha3 / 2012-05-04
|
||||
==================
|
||||
|
||||
* Added public `app.routes`. Closes #887
|
||||
* Added _view-locals_ example
|
||||
* Added _mvc_ example
|
||||
* Added `res.locals.use()`. Closes #1120
|
||||
* Added conditional-GET support to `res.send()`
|
||||
* Added: coerce `res.set()` values to strings
|
||||
* Changed: moved `static()` in generated apps below router
|
||||
* Changed: `res.send()` only set ETag when not previously set
|
||||
* Changed connect 2.2.1 dep
|
||||
* Changed: `make test` now runs unit / acceptance tests
|
||||
* Fixed req/res proto inheritance
|
||||
|
||||
3.0.0alpha2 / 2012-04-26
|
||||
==================
|
||||
|
||||
* Added `make benchmark` back
|
||||
* Added `res.send()` support for `String` objects
|
||||
@@ -16,7 +248,7 @@
|
||||
* Fixed session example. Closes #1105
|
||||
* Fixed generated express dep. Closes #1078
|
||||
|
||||
3.0.0alpha1 / 2012-04-15
|
||||
3.0.0alpha1 / 2012-04-15
|
||||
==================
|
||||
|
||||
* Added `app.locals.use(callback)`
|
||||
@@ -71,54 +303,54 @@
|
||||
* Fixed `res.sendfile()` with non-GET. Closes #723
|
||||
* Fixed express(1) public dir for windows. Closes #866
|
||||
|
||||
2.5.9/ 2012-04-02
|
||||
2.5.9/ 2012-04-02
|
||||
==================
|
||||
|
||||
* Added support for PURGE request method [pbuyle]
|
||||
* Fixed `express(1)` generated app `app.address()` before `listening` [mmalecki]
|
||||
|
||||
2.5.8 / 2012-02-08
|
||||
2.5.8 / 2012-02-08
|
||||
==================
|
||||
|
||||
* Update mkdirp dep. Closes #991
|
||||
|
||||
2.5.7 / 2012-02-06
|
||||
2.5.7 / 2012-02-06
|
||||
==================
|
||||
|
||||
* Fixed `app.all` duplicate DELETE requests [mscdex]
|
||||
|
||||
2.5.6 / 2012-01-13
|
||||
2.5.6 / 2012-01-13
|
||||
==================
|
||||
|
||||
* Updated hamljs dev dep. Closes #953
|
||||
|
||||
2.5.5 / 2012-01-08
|
||||
2.5.5 / 2012-01-08
|
||||
==================
|
||||
|
||||
* Fixed: set `filename` on cached templates [matthewleon]
|
||||
|
||||
2.5.4 / 2012-01-02
|
||||
2.5.4 / 2012-01-02
|
||||
==================
|
||||
|
||||
* Fixed `express(1)` eol on 0.4.x. Closes #947
|
||||
|
||||
2.5.3 / 2011-12-30
|
||||
2.5.3 / 2011-12-30
|
||||
==================
|
||||
|
||||
* Fixed `req.is()` when a charset is present
|
||||
|
||||
2.5.2 / 2011-12-10
|
||||
2.5.2 / 2011-12-10
|
||||
==================
|
||||
|
||||
* Fixed: express(1) LF -> CRLF for windows
|
||||
|
||||
2.5.1 / 2011-11-17
|
||||
2.5.1 / 2011-11-17
|
||||
==================
|
||||
|
||||
* Changed: updated connect to 1.8.x
|
||||
* Removed sass.js support from express(1)
|
||||
|
||||
2.5.0 / 2011-10-24
|
||||
2.5.0 / 2011-10-24
|
||||
==================
|
||||
|
||||
* Added ./routes dir for generated app by default
|
||||
@@ -127,7 +359,7 @@
|
||||
* Removed `make test-cov` since it wont work with node 0.5.x
|
||||
* Fixed express(1) public dir for windows. Closes #866
|
||||
|
||||
2.4.7 / 2011-10-05
|
||||
2.4.7 / 2011-10-05
|
||||
==================
|
||||
|
||||
* Added mkdirp to express(1). Closes #795
|
||||
@@ -138,17 +370,17 @@
|
||||
* Fixed `req.flash()`, only escape args
|
||||
* Fixed absolute path checking on windows. Closes #829 [reported by andrewpmckenzie]
|
||||
|
||||
2.4.6 / 2011-08-22
|
||||
2.4.6 / 2011-08-22
|
||||
==================
|
||||
|
||||
* Fixed multiple param callback regression. Closes #824 [reported by TroyGoode]
|
||||
|
||||
2.4.5 / 2011-08-19
|
||||
2.4.5 / 2011-08-19
|
||||
==================
|
||||
|
||||
* Added support for routes to handle errors. Closes #809
|
||||
* Added `app.routes.all()`. Closes #803
|
||||
* Added "basepath" setting to work in conjunction with reverse proxies etc.
|
||||
* Added "basepath" setting to work in conjunction with reverse proxies etc.
|
||||
* Refactored `Route` to use a single array of callbacks
|
||||
* Added support for multiple callbacks for `app.param()`. Closes #801
|
||||
Closes #805
|
||||
@@ -156,25 +388,25 @@ Closes #805
|
||||
* Dependency: `qs >= 0.3.1`
|
||||
* Fixed `res.redirect()` on windows due to `join()` usage. Closes #808
|
||||
|
||||
2.4.4 / 2011-08-05
|
||||
2.4.4 / 2011-08-05
|
||||
==================
|
||||
|
||||
* Fixed `res.header()` intention of a set, even when `undefined`
|
||||
* Fixed `*`, value no longer required
|
||||
* Fixed `res.send(204)` support. Closes #771
|
||||
|
||||
2.4.3 / 2011-07-14
|
||||
2.4.3 / 2011-07-14
|
||||
==================
|
||||
|
||||
* Added docs for `status` option special-case. Closes #739
|
||||
* Fixed `options.filename`, exposing the view path to template engines
|
||||
|
||||
2.4.2. / 2011-07-06
|
||||
2.4.2. / 2011-07-06
|
||||
==================
|
||||
|
||||
* Revert "removed jsonp stripping" for XSS
|
||||
|
||||
2.4.1 / 2011-07-06
|
||||
2.4.1 / 2011-07-06
|
||||
==================
|
||||
|
||||
* Added `res.json()` JSONP support. Closes #737
|
||||
@@ -186,14 +418,14 @@ Closes #805
|
||||
* Changed; default cookie path to "home" setting. Closes #731
|
||||
* Removed _pids/logs_ creation from express(1)
|
||||
|
||||
2.4.0 / 2011-06-28
|
||||
2.4.0 / 2011-06-28
|
||||
==================
|
||||
|
||||
* Added chainable `res.status(code)`
|
||||
* Added `res.json()`, an explicit version of `res.send(obj)`
|
||||
* Added simple web-service example
|
||||
|
||||
2.3.12 / 2011-06-22
|
||||
2.3.12 / 2011-06-22
|
||||
==================
|
||||
|
||||
* \#express is now on freenode! come join!
|
||||
@@ -205,7 +437,7 @@ Closes #805
|
||||
* Fixed view layout bug. Closes #720
|
||||
* Fixed; ignore body on 304. Closes #701
|
||||
|
||||
2.3.11 / 2011-06-04
|
||||
2.3.11 / 2011-06-04
|
||||
==================
|
||||
|
||||
* Added `npm test`
|
||||
@@ -213,14 +445,14 @@ Closes #805
|
||||
* Fixed; `express(1)` adds express as a dep
|
||||
* Fixed; prune on `prepublish`
|
||||
|
||||
2.3.10 / 2011-05-27
|
||||
2.3.10 / 2011-05-27
|
||||
==================
|
||||
|
||||
* Added `req.route`, exposing the current route
|
||||
* Added _package.json_ generation support to `express(1)`
|
||||
* Fixed call to `app.param()` function for optional params. Closes #682
|
||||
|
||||
2.3.9 / 2011-05-25
|
||||
2.3.9 / 2011-05-25
|
||||
==================
|
||||
|
||||
* Fixed bug-ish with `../' in `res.partial()` calls
|
||||
@@ -239,7 +471,7 @@ Closes #805
|
||||
* Removed module.parent check from express(1) generated app. Closes #670
|
||||
* Refactored router. Closes #639
|
||||
|
||||
2.3.6 / 2011-05-20
|
||||
2.3.6 / 2011-05-20
|
||||
==================
|
||||
|
||||
* Changed; using devDependencies instead of git submodules
|
||||
@@ -247,30 +479,30 @@ Closes #805
|
||||
* Fixed markdown example
|
||||
* Fixed view caching, should not be enabled in development
|
||||
|
||||
2.3.5 / 2011-05-20
|
||||
2.3.5 / 2011-05-20
|
||||
==================
|
||||
|
||||
* Added export `.view` as alias for `.View`
|
||||
|
||||
2.3.4 / 2011-05-08
|
||||
2.3.4 / 2011-05-08
|
||||
==================
|
||||
|
||||
* Added `./examples/say`
|
||||
* Fixed `res.sendfile()` bug preventing the transfer of files with spaces
|
||||
|
||||
2.3.3 / 2011-05-03
|
||||
2.3.3 / 2011-05-03
|
||||
==================
|
||||
|
||||
* Added "case sensitive routes" option.
|
||||
* Changed; split methods supported per rfc [slaskis]
|
||||
* Fixed route-specific middleware when using the same callback function several times
|
||||
|
||||
2.3.2 / 2011-04-27
|
||||
2.3.2 / 2011-04-27
|
||||
==================
|
||||
|
||||
* Fixed view hints
|
||||
|
||||
2.3.1 / 2011-04-26
|
||||
2.3.1 / 2011-04-26
|
||||
==================
|
||||
|
||||
* Added `app.match()` as `app.match.all()`
|
||||
@@ -280,7 +512,7 @@ Closes #805
|
||||
* Fixed template caching collision issue. Closes #644
|
||||
* Moved router over from connect and started refactor
|
||||
|
||||
2.3.0 / 2011-04-25
|
||||
2.3.0 / 2011-04-25
|
||||
==================
|
||||
|
||||
* Added options support to `res.clearCookie()`
|
||||
@@ -289,18 +521,18 @@ Closes #805
|
||||
* Changed; auto set Content-Type in res.attachement [Aaron Heckmann]
|
||||
* Renamed "cache views" to "view cache". Closes #628
|
||||
* Fixed caching of views when using several apps. Closes #637
|
||||
* Fixed gotcha invoking `app.param()` callbacks once per route middleware.
|
||||
* Fixed gotcha invoking `app.param()` callbacks once per route middleware.
|
||||
Closes #638
|
||||
* Fixed partial lookup precedence. Closes #631
|
||||
Shaw]
|
||||
|
||||
2.2.2 / 2011-04-12
|
||||
2.2.2 / 2011-04-12
|
||||
==================
|
||||
|
||||
* Added second callback support for `res.download()` connection errors
|
||||
* Fixed `filename` option passing to template engine
|
||||
|
||||
2.2.1 / 2011-04-04
|
||||
2.2.1 / 2011-04-04
|
||||
==================
|
||||
|
||||
* Added `layout(path)` helper to change the layout within a view. Closes #610
|
||||
@@ -314,7 +546,7 @@ Shaw]
|
||||
* Removed `request` and `response` locals
|
||||
* Changed; errorHandler page title is now `Express` instead of `Connect`
|
||||
|
||||
2.2.0 / 2011-03-30
|
||||
2.2.0 / 2011-03-30
|
||||
==================
|
||||
|
||||
* Added `app.lookup.VERB()`, ex `app.lookup.put('/user/:id')`. Closes #606
|
||||
@@ -322,14 +554,14 @@ Shaw]
|
||||
* Added `app.VERB(path)` as alias of `app.lookup.VERB()`.
|
||||
* Dependency `connect >= 1.2.0`
|
||||
|
||||
2.1.1 / 2011-03-29
|
||||
2.1.1 / 2011-03-29
|
||||
==================
|
||||
|
||||
* Added; expose `err.view` object when failing to locate a view
|
||||
* Fixed `res.partial()` call `next(err)` when no callback is given [reported by aheckmann]
|
||||
* Fixed; `res.send(undefined)` responds with 204 [aheckmann]
|
||||
|
||||
2.1.0 / 2011-03-24
|
||||
2.1.0 / 2011-03-24
|
||||
==================
|
||||
|
||||
* Added `<root>/_?<name>` partial lookup support. Closes #447
|
||||
@@ -340,20 +572,20 @@ Shaw]
|
||||
* Fixed stylus example for latest version
|
||||
* Fixed; wrap try/catch around `res.render()`
|
||||
|
||||
2.0.0 / 2011-03-17
|
||||
2.0.0 / 2011-03-17
|
||||
==================
|
||||
|
||||
* Fixed up index view path alternative.
|
||||
* Changed; `res.locals()` without object returns the locals
|
||||
|
||||
2.0.0rc3 / 2011-03-17
|
||||
2.0.0rc3 / 2011-03-17
|
||||
==================
|
||||
|
||||
* Added `res.locals(obj)` to compliment `res.local(key, val)`
|
||||
* Added `res.partial()` callback support
|
||||
* Fixed recursive error reporting issue in `res.render()`
|
||||
|
||||
2.0.0rc2 / 2011-03-17
|
||||
2.0.0rc2 / 2011-03-17
|
||||
==================
|
||||
|
||||
* Changed; `partial()` "locals" are now optional
|
||||
@@ -362,14 +594,14 @@ Shaw]
|
||||
* Fixed blog example
|
||||
* Fixed `{req,res}.app` reference when mounting [Ben Weaver]
|
||||
|
||||
2.0.0rc / 2011-03-14
|
||||
2.0.0rc / 2011-03-14
|
||||
==================
|
||||
|
||||
* Fixed; expose `HTTPSServer` constructor
|
||||
* Fixed express(1) default test charset. Closes #579 [reported by secoif]
|
||||
* Fixed; default charset to utf-8 instead of utf8 for lame IE [reported by NickP]
|
||||
|
||||
2.0.0beta3 / 2011-03-09
|
||||
2.0.0beta3 / 2011-03-09
|
||||
==================
|
||||
|
||||
* Added support for `res.contentType()` literal
|
||||
@@ -387,13 +619,13 @@ Shaw]
|
||||
* Fixed; default `res.send()` string charset to utf8
|
||||
* Removed `Partial` constructor (not currently used)
|
||||
|
||||
2.0.0beta2 / 2011-03-07
|
||||
2.0.0beta2 / 2011-03-07
|
||||
==================
|
||||
|
||||
* Added res.render() `.locals` support back to aid in migration process
|
||||
* Fixed flash example
|
||||
|
||||
2.0.0beta / 2011-03-03
|
||||
2.0.0beta / 2011-03-03
|
||||
==================
|
||||
|
||||
* Added HTTPS support
|
||||
@@ -426,46 +658,46 @@ Shaw]
|
||||
* Fixed; strip unsafe chars from jsonp callbacks
|
||||
* Removed "stream threshold" setting
|
||||
|
||||
1.0.8 / 2011-03-01
|
||||
1.0.8 / 2011-03-01
|
||||
==================
|
||||
|
||||
* Allow `req.query` to be pre-defined (via middleware or other parent app)
|
||||
* "connect": ">= 0.5.0 < 1.0.0". Closes #547
|
||||
* Removed the long deprecated __EXPRESS_ENV__ support
|
||||
|
||||
1.0.7 / 2011-02-07
|
||||
1.0.7 / 2011-02-07
|
||||
==================
|
||||
|
||||
* Fixed `render()` setting inheritance.
|
||||
Mounted apps would not inherit "view engine"
|
||||
|
||||
1.0.6 / 2011-02-07
|
||||
1.0.6 / 2011-02-07
|
||||
==================
|
||||
|
||||
* Fixed `view engine` setting bug when period is in dirname
|
||||
|
||||
1.0.5 / 2011-02-05
|
||||
1.0.5 / 2011-02-05
|
||||
==================
|
||||
|
||||
* Added secret to generated app `session()` call
|
||||
|
||||
1.0.4 / 2011-02-05
|
||||
1.0.4 / 2011-02-05
|
||||
==================
|
||||
|
||||
* Added `qs` dependency to _package.json_
|
||||
* Fixed namespaced `require()`s for latest connect support
|
||||
|
||||
1.0.3 / 2011-01-13
|
||||
1.0.3 / 2011-01-13
|
||||
==================
|
||||
|
||||
* Remove unsafe characters from JSONP callback names [Ryan Grove]
|
||||
|
||||
1.0.2 / 2011-01-10
|
||||
1.0.2 / 2011-01-10
|
||||
==================
|
||||
|
||||
* Removed nested require, using `connect.router`
|
||||
|
||||
1.0.1 / 2010-12-29
|
||||
1.0.1 / 2010-12-29
|
||||
==================
|
||||
|
||||
* Fixed for middleware stacked via `createServer()`
|
||||
@@ -473,7 +705,7 @@ Shaw]
|
||||
would not have access to Express methods such as `res.send()`
|
||||
or props like `req.query` etc.
|
||||
|
||||
1.0.0 / 2010-11-16
|
||||
1.0.0 / 2010-11-16
|
||||
==================
|
||||
|
||||
* Added; deduce partial object names from the last segment.
|
||||
@@ -487,7 +719,7 @@ Shaw]
|
||||
* Added _-s, --session[s]_ flag to express(1) to add session related middleware
|
||||
* Added _--template_ flag to express(1) to specify the
|
||||
template engine to use.
|
||||
* Added _--css_ flag to express(1) to specify the
|
||||
* Added _--css_ flag to express(1) to specify the
|
||||
stylesheet engine to use (or just plain css by default).
|
||||
* Added `app.all()` support [thanks aheckmann]
|
||||
* Added partial direct object support.
|
||||
@@ -500,7 +732,7 @@ Shaw]
|
||||
* Fixed partial local inheritance precedence. [reported by Nick Poulden] Closes #454
|
||||
* Fixed jsonp support; _text/javascript_ as per mailinglist discussion
|
||||
|
||||
1.0.0rc4 / 2010-10-14
|
||||
1.0.0rc4 / 2010-10-14
|
||||
==================
|
||||
|
||||
* Added _NODE_ENV_ support, _EXPRESS_ENV_ is deprecated and will be removed in 1.0.0
|
||||
@@ -519,7 +751,7 @@ Shaw]
|
||||
* Fixed; exposing _./support_ libs to examples so they can run without installs
|
||||
* Fixed mvc example
|
||||
|
||||
1.0.0rc3 / 2010-09-20
|
||||
1.0.0rc3 / 2010-09-20
|
||||
==================
|
||||
|
||||
* Added confirmation for `express(1)` app generation. Closes #391
|
||||
@@ -542,7 +774,7 @@ Shaw]
|
||||
* Fixed bug messing with error handlers when `listenFD()` is called instead of `listen()`. [thanks guillermo]
|
||||
|
||||
|
||||
1.0.0rc2 / 2010-08-17
|
||||
1.0.0rc2 / 2010-08-17
|
||||
==================
|
||||
|
||||
* Added `app.register()` for template engine mapping. Closes #390
|
||||
@@ -555,7 +787,7 @@ Shaw]
|
||||
* Fixed `res.sendfile()` error handling, defer via `next()`
|
||||
* Fixed `res.render()` callback when a layout is used [thanks guillermo]
|
||||
* Fixed; `make install` creating ~/.node_libraries when not present
|
||||
* Fixed issue preventing error handlers from being defined anywhere. Closes #387
|
||||
* Fixed issue preventing error handlers from being defined anywhere. Closes #387
|
||||
|
||||
1.0.0rc / 2010-07-28
|
||||
==================
|
||||
@@ -573,7 +805,7 @@ Shaw]
|
||||
* Fixed "home" setting
|
||||
* Fixed middleware/router precedence issue. Closes #366
|
||||
* Fixed; _configure()_ callbacks called immediately. Closes #368
|
||||
|
||||
|
||||
1.0.0beta2 / 2010-07-23
|
||||
==================
|
||||
|
||||
@@ -708,7 +940,7 @@ Shaw]
|
||||
* Updated dependencies
|
||||
* Removed set("session cookie") in favour of use(Session, { cookie: { ... }})
|
||||
* Removed utils.mixin(); use Object#mergeDeep()
|
||||
|
||||
|
||||
0.8.0 / 2010-03-19
|
||||
==================
|
||||
|
||||
@@ -775,16 +1007,16 @@ Shaw]
|
||||
|
||||
* Added seed.yml for kiwi package management support
|
||||
* Added HTTP client query string support when method is GET. Closes #205
|
||||
|
||||
|
||||
* Added support for arbitrary view engines.
|
||||
For example "foo.engine.html" will now require('engine'),
|
||||
the exports from this module are cached after the first require().
|
||||
|
||||
|
||||
* Added async plugin support
|
||||
|
||||
|
||||
* Removed usage of RESTful route funcs as http client
|
||||
get() etc, use http.get() and friends
|
||||
|
||||
|
||||
* Removed custom exceptions
|
||||
|
||||
0.5.0 / 2010-03-10
|
||||
|
||||
27
Makefile
27
Makefile
@@ -1,22 +1,20 @@
|
||||
|
||||
MOCHA_OPTS= --check-leaks
|
||||
REPORTER = dot
|
||||
|
||||
docs: docs/express.md
|
||||
check: test
|
||||
|
||||
docs/express.md: docs/application.md docs/request.md docs/response.md
|
||||
cat $^ > $@
|
||||
test: test-unit test-acceptance
|
||||
|
||||
docs/%.md: lib/%.js
|
||||
@mkdir -p docs
|
||||
dox --raw < $< | ./support/docs > $@
|
||||
|
||||
test:
|
||||
test-unit:
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--reporter $(REPORTER)
|
||||
--reporter $(REPORTER) \
|
||||
$(MOCHA_OPTS)
|
||||
|
||||
test-acceptance:
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--reporter spec \
|
||||
--reporter $(REPORTER) \
|
||||
--bail \
|
||||
test/acceptance/*.js
|
||||
|
||||
test-cov: lib-cov
|
||||
@@ -25,10 +23,11 @@ test-cov: lib-cov
|
||||
lib-cov:
|
||||
@jscoverage lib lib-cov
|
||||
|
||||
docclean:
|
||||
rm -fr docs
|
||||
|
||||
benchmark:
|
||||
@./support/bench
|
||||
|
||||
.PHONY: docs docclean test test-acceptance benchmark
|
||||
clean:
|
||||
rm -f coverage.html
|
||||
rm -fr lib-cov
|
||||
|
||||
.PHONY: test test-unit test-acceptance benchmark clean
|
||||
|
||||
25
Readme.md
25
Readme.md
@@ -1,7 +1,6 @@
|
||||
|
||||

|
||||
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org). [](http://travis-ci.org/visionmedia/express) [](https://gemnasium.com/visionmedia/express)
|
||||
|
||||
```js
|
||||
var express = require('express');
|
||||
@@ -18,10 +17,6 @@ app.listen(3000);
|
||||
|
||||
$ npm install -g express
|
||||
|
||||
To install the 3.0 alpha:
|
||||
|
||||
$ npm install -g express@3.0
|
||||
|
||||
## Quick Start
|
||||
|
||||
The quickest way to get started with express is to utilize the executable `express(1)` to generate an application as shown below:
|
||||
@@ -56,7 +51,7 @@ app.listen(3000);
|
||||
The Express philosophy is to provide small, robust tooling for HTTP servers. Making
|
||||
it a great solution for single page applications, web sites, hybrids, or public
|
||||
HTTP APIs.
|
||||
|
||||
|
||||
Built on Connect you can use _only_ what you need, and nothing more, applications
|
||||
can be as big or as small as you like, even a single file. Express does
|
||||
not force you to use any specific ORM or template engine. With support for over
|
||||
@@ -70,14 +65,16 @@ app.listen(3000);
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
|
||||
* Visit the [Wiki](http://github.com/visionmedia/express/wiki)
|
||||
* [日本語ドキュメンテーション](http://hideyukisaito.com/doc/expressjs/) by [hideyukisaito](https://github.com/hideyukisaito)
|
||||
* [Русскоязычная документация](http://express-js.ru/)
|
||||
* [Русскоязычная документация](http://jsman.ru/express/)
|
||||
* Run express examples [online](https://runnable.com/express)
|
||||
|
||||
## Viewing Examples
|
||||
|
||||
First install the dev dependencies to install all the example / test suite deps:
|
||||
Clone the Express repo, then install the dev dependencies to install all the example / test suite deps:
|
||||
|
||||
$ git clone git://github.com/visionmedia/express.git --depth 1
|
||||
$ cd express
|
||||
$ npm install -d
|
||||
$ npm install
|
||||
|
||||
then run whichever tests you want:
|
||||
|
||||
@@ -100,14 +97,14 @@ project: express
|
||||
commits: 3559
|
||||
active : 468 days
|
||||
files : 237
|
||||
authors:
|
||||
authors:
|
||||
1891 Tj Holowaychuk 53.1%
|
||||
1285 visionmedia 36.1%
|
||||
182 TJ Holowaychuk 5.1%
|
||||
54 Aaron Heckmann 1.5%
|
||||
34 csausdev 1.0%
|
||||
26 ciaranj 0.7%
|
||||
21 Robert Sköld 0.6%
|
||||
21 Robert Sköld 0.6%
|
||||
6 Guillermo Rauch 0.2%
|
||||
3 Dav Glass 0.1%
|
||||
3 Nick Poulden 0.1%
|
||||
@@ -153,11 +150,11 @@ authors:
|
||||
1 Jonathan Zacsh 0.0%
|
||||
1 Justin Lilly 0.0%
|
||||
1 Ken Sato 0.0%
|
||||
1 Maciej Małecki 0.0%
|
||||
1 Maciej Małecki 0.0%
|
||||
1 Masahiro Hayashi 0.0%
|
||||
```
|
||||
|
||||
## License
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
|
||||
82
bin/express
82
bin/express
@@ -19,6 +19,7 @@ program
|
||||
.option('-s, --sessions', 'add session support')
|
||||
.option('-e, --ejs', 'add ejs engine support (defaults to jade)')
|
||||
.option('-J, --jshtml', 'add jshtml engine support (defaults to jade)')
|
||||
.option('-H, --hogan', 'add hogan.js engine support')
|
||||
.option('-c, --css <engine>', 'add stylesheet <engine> support (less|stylus) (defaults to plain css)')
|
||||
.option('-f, --force', 'force on non-empty directory')
|
||||
.parse(process.argv);
|
||||
@@ -29,13 +30,14 @@ var path = program.args.shift() || '.';
|
||||
|
||||
// end-of-line code
|
||||
|
||||
var eol = 'win32' == os.platform() ? '\r\n' : '\n'
|
||||
var eol = os.EOL
|
||||
|
||||
// Template engine
|
||||
|
||||
program.template = 'jade';
|
||||
if (program.ejs) program.template = 'ejs';
|
||||
if (program.jshtml) program.template = 'jshtml';
|
||||
if (program.hogan) program.template = 'hjs';
|
||||
|
||||
/**
|
||||
* Routes index template.
|
||||
@@ -52,12 +54,27 @@ var index = [
|
||||
, '};'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* Routes users template.
|
||||
*/
|
||||
|
||||
var users = [
|
||||
''
|
||||
, '/*'
|
||||
, ' * GET users listing.'
|
||||
, ' */'
|
||||
, ''
|
||||
, 'exports.list = function(req, res){'
|
||||
, ' res.send("respond with a resource");'
|
||||
, '};'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* Jade layout template.
|
||||
*/
|
||||
|
||||
var jadeLayout = [
|
||||
'!!!'
|
||||
'doctype 5'
|
||||
, 'html'
|
||||
, ' head'
|
||||
, ' title= title'
|
||||
@@ -122,6 +139,23 @@ var jshtmlIndex = [
|
||||
, '<p>Welcome to @write(title)</p>'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* Hogan.js index template.
|
||||
*/
|
||||
var hoganIndex = [
|
||||
'<!DOCTYPE html>'
|
||||
, '<html>'
|
||||
, ' <head>'
|
||||
, ' <title>{{ title }}</title>'
|
||||
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
|
||||
, ' </head>'
|
||||
, ' <body>'
|
||||
, ' <h1>{{ title }}</h1>'
|
||||
, ' <p>Welcome to {{ title }}</p>'
|
||||
, ' </body>'
|
||||
, '</html>'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* Default css template.
|
||||
*/
|
||||
@@ -176,30 +210,34 @@ var app = [
|
||||
, ''
|
||||
, 'var express = require(\'express\')'
|
||||
, ' , routes = require(\'./routes\')'
|
||||
, ' , http = require(\'http\');'
|
||||
, ' , user = require(\'./routes/user\')'
|
||||
, ' , http = require(\'http\')'
|
||||
, ' , path = require(\'path\');'
|
||||
, ''
|
||||
, 'var app = express();'
|
||||
, ''
|
||||
, 'app.configure(function(){'
|
||||
, ' app.set(\'views\', __dirname + \'/views\');'
|
||||
, ' app.set(\'view engine\', \':TEMPLATE\');'
|
||||
, ' app.use(express.favicon());'
|
||||
, ' app.use(express.logger(\'dev\'));{css}'
|
||||
, ' app.use(express.static(__dirname + \'/public\'));'
|
||||
, ' app.use(express.bodyParser());'
|
||||
, ' app.use(express.methodOverride());{sess}'
|
||||
, ' app.use(app.router);'
|
||||
, '});'
|
||||
, '// all environments'
|
||||
, 'app.set(\'port\', process.env.PORT || 3000);'
|
||||
, 'app.set(\'views\', __dirname + \'/views\');'
|
||||
, 'app.set(\'view engine\', \':TEMPLATE\');'
|
||||
, 'app.use(express.favicon());'
|
||||
, 'app.use(express.logger(\'dev\'));'
|
||||
, 'app.use(express.bodyParser());'
|
||||
, 'app.use(express.methodOverride());{sess}'
|
||||
, 'app.use(app.router);{css}'
|
||||
, 'app.use(express.static(path.join(__dirname, \'public\')));'
|
||||
, ''
|
||||
, 'app.configure(\'development\', function(){'
|
||||
, '// development only'
|
||||
, 'if (\'development\' == app.get(\'env\')) {'
|
||||
, ' app.use(express.errorHandler());'
|
||||
, '});'
|
||||
, '}'
|
||||
, ''
|
||||
, 'app.get(\'/\', routes.index);'
|
||||
, 'app.get(\'/users\', user.list);'
|
||||
, ''
|
||||
, 'http.createServer(app).listen(3000);'
|
||||
, ''
|
||||
, 'console.log("Express server listening on port 3000");'
|
||||
, 'http.createServer(app).listen(app.get(\'port\'), function(){'
|
||||
, ' console.log(\'Express server listening on port \' + app.get(\'port\'));'
|
||||
, '});'
|
||||
, ''
|
||||
].join(eol);
|
||||
|
||||
@@ -259,6 +297,7 @@ function createApplicationAt(path) {
|
||||
|
||||
mkdir(path + '/routes', function(){
|
||||
write(path + '/routes/index.js', index);
|
||||
write(path + '/routes/user.js', users);
|
||||
});
|
||||
|
||||
mkdir(path + '/views', function(){
|
||||
@@ -274,6 +313,9 @@ function createApplicationAt(path) {
|
||||
write(path + '/views/layout.jshtml', jshtmlLayout);
|
||||
write(path + '/views/index.jshtml', jshtmlIndex);
|
||||
break;
|
||||
case 'hjs':
|
||||
write(path + '/views/index.hjs', hoganIndex);
|
||||
break;
|
||||
|
||||
}
|
||||
});
|
||||
@@ -284,7 +326,7 @@ function createApplicationAt(path) {
|
||||
app = app.replace('{css}', eol + ' app.use(require(\'less-middleware\')({ src: __dirname + \'/public\' }));');
|
||||
break;
|
||||
case 'stylus':
|
||||
app = app.replace('{css}', eol + ' app.use(require(\'stylus\').middleware({ src: __dirname + \'/public\' }));');
|
||||
app = app.replace('{css}', eol + ' app.use(require(\'stylus\').middleware(__dirname + \'/public\'));');
|
||||
break;
|
||||
default:
|
||||
app = app.replace('{css}', '');
|
||||
@@ -303,7 +345,7 @@ function createApplicationAt(path) {
|
||||
name: 'application-name'
|
||||
, version: '0.0.1'
|
||||
, private: true
|
||||
, scripts: { start: 'node app' }
|
||||
, scripts: { start: 'node app.js' }
|
||||
, dependencies: {
|
||||
express: version
|
||||
}
|
||||
|
||||
@@ -1,181 +0,0 @@
|
||||
|
||||
# app
|
||||
|
||||
Application prototype.
|
||||
|
||||
# app.use()
|
||||
|
||||
Proxy `connect#use()` to apply settings to
|
||||
mounted applications.
|
||||
|
||||
# app.engine()
|
||||
|
||||
Register the given template engine callback `fn`
|
||||
as `ext`.
|
||||
|
||||
By default will `require()` the engine based on the
|
||||
file extension. For example if you try to render
|
||||
a "foo.jade" file Express will invoke the following internally:
|
||||
|
||||
app.engine('jade', require('jade').__express);
|
||||
|
||||
For engines that do not provide `.__express` out of the box,
|
||||
or if you wish to "map" a different extension to the template engine
|
||||
you may use this method. For example mapping the EJS template engine to
|
||||
".html" files
|
||||
|
||||
app.engine('html', require('ejs').renderFile);
|
||||
|
||||
In this case EJS provides a `.renderFile()` method with
|
||||
the same signature that Express expects: `(path, options, callback)`,
|
||||
though note that it aliases this method as `ejs.__express` internally
|
||||
so if you're using ".ejs" extensions you dont need to do anything.
|
||||
|
||||
Some template engines do not follow this convention, the
|
||||
[Consolidate.js](https://github.com/visionmedia/consolidate.js)
|
||||
library was created to map all of node's popular template
|
||||
engines to follow this convention, thus allowing them to
|
||||
work seemlessly within Express.
|
||||
|
||||
# app.param()
|
||||
|
||||
Map the given param placeholder `name`(s) to the given callback(s).
|
||||
|
||||
Parameter mapping is used to provide pre-conditions to routes
|
||||
which use normalized placeholders. For example a _:user_id_ parameter
|
||||
could automatically load a user's information from the database without
|
||||
any additional code,
|
||||
|
||||
The callback uses the samesignature as middleware, the only differencing
|
||||
being that the value of the placeholder is passed, in this case the _id_
|
||||
of the user. Once the `next()` function is invoked, just like middleware
|
||||
it will continue on to execute the route, or subsequent parameter functions.
|
||||
|
||||
app.param('user_id', function(req, res, next, id){
|
||||
User.find(id, function(err, user){
|
||||
if (err) {
|
||||
next(err);
|
||||
} else if (user) {
|
||||
req.user = user;
|
||||
next();
|
||||
} else {
|
||||
next(new Error('failed to load user'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
# app.set()
|
||||
|
||||
Assign `setting` to `val`, or return `setting`'s value.
|
||||
|
||||
app.set('foo', 'bar');
|
||||
app.get('foo');
|
||||
// => "bar"
|
||||
|
||||
Mounted servers inherit their parent server's settings.
|
||||
|
||||
# app.enabled()
|
||||
|
||||
Check if `setting` is enabled (truthy).
|
||||
|
||||
app.enabled('foo')
|
||||
// => false
|
||||
|
||||
app.enable('foo')
|
||||
app.enabled('foo')
|
||||
// => true
|
||||
|
||||
# app.disabled()
|
||||
|
||||
Check if `setting` is disabled.
|
||||
|
||||
app.disabled('foo')
|
||||
// => true
|
||||
|
||||
app.enable('foo')
|
||||
app.disabled('foo')
|
||||
// => false
|
||||
|
||||
# app.enable()
|
||||
|
||||
Enable `setting`.
|
||||
|
||||
# app.disable()
|
||||
|
||||
Disable `setting`.
|
||||
|
||||
# app.configure()
|
||||
|
||||
Configure callback for zero or more envs,
|
||||
when no `env` is specified that callback will
|
||||
be invoked for all environments. Any combination
|
||||
can be used multiple times, in any order desired.
|
||||
|
||||
## Examples
|
||||
|
||||
app.configure(function(){
|
||||
// executed for all envs
|
||||
});
|
||||
|
||||
app.configure('stage', function(){
|
||||
// executed staging env
|
||||
});
|
||||
|
||||
app.configure('stage', 'production', function(){
|
||||
// executed for stage and production
|
||||
});
|
||||
|
||||
## Note
|
||||
|
||||
These callbacks are invoked immediately, and
|
||||
are effectively sugar for the following.
|
||||
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
|
||||
switch (env) {
|
||||
case 'development':
|
||||
...
|
||||
break;
|
||||
case 'stage':
|
||||
...
|
||||
break;
|
||||
case 'production':
|
||||
...
|
||||
break;
|
||||
}
|
||||
|
||||
# app.all()
|
||||
|
||||
Special-cased "all" method, applying the given route `path`,
|
||||
middleware, and callback to _every_ HTTP method.
|
||||
|
||||
# app.render()
|
||||
|
||||
Render the given view `name` name with `options`
|
||||
and a callback accepting an error and the
|
||||
rendered template string.
|
||||
|
||||
## Example
|
||||
|
||||
app.render('email', { name: 'Tobi' }, function(err, html){
|
||||
// ...
|
||||
})
|
||||
|
||||
# app.listen()
|
||||
|
||||
Listen for connections.
|
||||
|
||||
A node `http.Server` is returned, with this
|
||||
application (which is a `Function`) as its
|
||||
callback. If you wish to create both an HTTP
|
||||
and HTTPS server you may do so with the "http"
|
||||
and "https" modules as shown here.
|
||||
|
||||
var http = require('http')
|
||||
, https = require('https')
|
||||
, express = require('express')
|
||||
, app = express();
|
||||
|
||||
http.createServer(app).listen(80);
|
||||
http.createServer({ ... }, app).listen(443);
|
||||
|
||||
511
docs/express.md
511
docs/express.md
@@ -1,511 +0,0 @@
|
||||
|
||||
# app
|
||||
|
||||
Application prototype.
|
||||
|
||||
# app.use()
|
||||
|
||||
Proxy `connect#use()` to apply settings to
|
||||
mounted applications.
|
||||
|
||||
# app.engine()
|
||||
|
||||
Register the given template engine callback `fn`
|
||||
as `ext`.
|
||||
|
||||
By default will `require()` the engine based on the
|
||||
file extension. For example if you try to render
|
||||
a "foo.jade" file Express will invoke the following internally:
|
||||
|
||||
app.engine('jade', require('jade').__express);
|
||||
|
||||
For engines that do not provide `.__express` out of the box,
|
||||
or if you wish to "map" a different extension to the template engine
|
||||
you may use this method. For example mapping the EJS template engine to
|
||||
".html" files
|
||||
|
||||
app.engine('html', require('ejs').renderFile);
|
||||
|
||||
In this case EJS provides a `.renderFile()` method with
|
||||
the same signature that Express expects: `(path, options, callback)`,
|
||||
though note that it aliases this method as `ejs.__express` internally
|
||||
so if you're using ".ejs" extensions you dont need to do anything.
|
||||
|
||||
Some template engines do not follow this convention, the
|
||||
[Consolidate.js](https://github.com/visionmedia/consolidate.js)
|
||||
library was created to map all of node's popular template
|
||||
engines to follow this convention, thus allowing them to
|
||||
work seemlessly within Express.
|
||||
|
||||
# app.param()
|
||||
|
||||
Map the given param placeholder `name`(s) to the given callback(s).
|
||||
|
||||
Parameter mapping is used to provide pre-conditions to routes
|
||||
which use normalized placeholders. For example a _:user_id_ parameter
|
||||
could automatically load a user's information from the database without
|
||||
any additional code,
|
||||
|
||||
The callback uses the samesignature as middleware, the only differencing
|
||||
being that the value of the placeholder is passed, in this case the _id_
|
||||
of the user. Once the `next()` function is invoked, just like middleware
|
||||
it will continue on to execute the route, or subsequent parameter functions.
|
||||
|
||||
app.param('user_id', function(req, res, next, id){
|
||||
User.find(id, function(err, user){
|
||||
if (err) {
|
||||
next(err);
|
||||
} else if (user) {
|
||||
req.user = user;
|
||||
next();
|
||||
} else {
|
||||
next(new Error('failed to load user'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
# app.set()
|
||||
|
||||
Assign `setting` to `val`, or return `setting`'s value.
|
||||
|
||||
app.set('foo', 'bar');
|
||||
app.get('foo');
|
||||
// => "bar"
|
||||
|
||||
Mounted servers inherit their parent server's settings.
|
||||
|
||||
# app.enabled()
|
||||
|
||||
Check if `setting` is enabled (truthy).
|
||||
|
||||
app.enabled('foo')
|
||||
// => false
|
||||
|
||||
app.enable('foo')
|
||||
app.enabled('foo')
|
||||
// => true
|
||||
|
||||
# app.disabled()
|
||||
|
||||
Check if `setting` is disabled.
|
||||
|
||||
app.disabled('foo')
|
||||
// => true
|
||||
|
||||
app.enable('foo')
|
||||
app.disabled('foo')
|
||||
// => false
|
||||
|
||||
# app.enable()
|
||||
|
||||
Enable `setting`.
|
||||
|
||||
# app.disable()
|
||||
|
||||
Disable `setting`.
|
||||
|
||||
# app.configure()
|
||||
|
||||
Configure callback for zero or more envs,
|
||||
when no `env` is specified that callback will
|
||||
be invoked for all environments. Any combination
|
||||
can be used multiple times, in any order desired.
|
||||
|
||||
## Examples
|
||||
|
||||
app.configure(function(){
|
||||
// executed for all envs
|
||||
});
|
||||
|
||||
app.configure('stage', function(){
|
||||
// executed staging env
|
||||
});
|
||||
|
||||
app.configure('stage', 'production', function(){
|
||||
// executed for stage and production
|
||||
});
|
||||
|
||||
## Note
|
||||
|
||||
These callbacks are invoked immediately, and
|
||||
are effectively sugar for the following.
|
||||
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
|
||||
switch (env) {
|
||||
case 'development':
|
||||
...
|
||||
break;
|
||||
case 'stage':
|
||||
...
|
||||
break;
|
||||
case 'production':
|
||||
...
|
||||
break;
|
||||
}
|
||||
|
||||
# app.all()
|
||||
|
||||
Special-cased "all" method, applying the given route `path`,
|
||||
middleware, and callback to _every_ HTTP method.
|
||||
|
||||
# app.render()
|
||||
|
||||
Render the given view `name` name with `options`
|
||||
and a callback accepting an error and the
|
||||
rendered template string.
|
||||
|
||||
## Example
|
||||
|
||||
app.render('email', { name: 'Tobi' }, function(err, html){
|
||||
// ...
|
||||
})
|
||||
|
||||
# app.listen()
|
||||
|
||||
Listen for connections.
|
||||
|
||||
A node `http.Server` is returned, with this
|
||||
application (which is a `Function`) as its
|
||||
callback. If you wish to create both an HTTP
|
||||
and HTTPS server you may do so with the "http"
|
||||
and "https" modules as shown here.
|
||||
|
||||
var http = require('http')
|
||||
, https = require('https')
|
||||
, express = require('express')
|
||||
, app = express();
|
||||
|
||||
http.createServer(app).listen(80);
|
||||
http.createServer({ ... }, app).listen(443);
|
||||
|
||||
|
||||
# req
|
||||
|
||||
Request prototype.
|
||||
|
||||
# req.get
|
||||
|
||||
Return request header.
|
||||
|
||||
The `Referrer` header field is special-cased,
|
||||
both `Referrer` and `Referer` are interchangeable.
|
||||
|
||||
## Examples
|
||||
|
||||
req.get('Content-Type');
|
||||
// => "text/plain"
|
||||
|
||||
req.get('content-type');
|
||||
// => "text/plain"
|
||||
|
||||
req.get('Something');
|
||||
// => undefined
|
||||
|
||||
Aliased as `req.header()`.
|
||||
|
||||
# req.accepts()
|
||||
|
||||
Check if the given `type(s)` is acceptable, returning
|
||||
the best match when true, otherwise `undefined`, in which
|
||||
case you should respond with 406 "Not Acceptable".
|
||||
|
||||
The `type` value may be a single mime type string
|
||||
such as "application/json", the extension name
|
||||
such as "json", a comma-delimted list such as "json, html, text/plain",
|
||||
or an array `["json", "html", "text/plain"]`. When a list
|
||||
or array is given the _best_ match, if any is returned.
|
||||
|
||||
## Examples
|
||||
|
||||
// Accept: text/html
|
||||
req.accepts('html');
|
||||
// => "html"
|
||||
|
||||
// Accept: text/*, application/json
|
||||
req.accepts('html');
|
||||
// => "html"
|
||||
req.accepts('text/html');
|
||||
// => "text/html"
|
||||
req.accepts('json, text');
|
||||
// => "json"
|
||||
req.accepts('application/json');
|
||||
// => "application/json"
|
||||
|
||||
// Accept: text/*, application/json
|
||||
req.accepts('image/png');
|
||||
req.accepts('png');
|
||||
// => undefined
|
||||
|
||||
// Accept: text/*;q=.5, application/json
|
||||
req.accepts(['html', 'json']);
|
||||
req.accepts('html, json');
|
||||
// => "json"
|
||||
|
||||
# req.acceptsCharset()
|
||||
|
||||
Check if the given `charset` is acceptable,
|
||||
otherwise you should respond with 406 "Not Acceptable".
|
||||
|
||||
# req.acceptsLanguage()
|
||||
|
||||
Check if the given `lang` is acceptable,
|
||||
otherwise you should respond with 406 "Not Acceptable".
|
||||
|
||||
# req.param()
|
||||
|
||||
Return the value of param `name` when present or `defaultValue`.
|
||||
|
||||
- Checks body params, ex: id=12, {"id":12}
|
||||
- Checks route placeholders, ex: _/user/:id_
|
||||
- Checks query string params, ex: ?id=12
|
||||
|
||||
To utilize request bodies, `req.body`
|
||||
should be an object. This can be done by using
|
||||
the `connect.bodyParser()` middleware.
|
||||
|
||||
# req.is()
|
||||
|
||||
Check if the incoming request contains the "Content-Type"
|
||||
header field, and it contains the give mime `type`.
|
||||
|
||||
## Examples
|
||||
|
||||
// With Content-Type: text/html; charset=utf-8
|
||||
req.is('html');
|
||||
req.is('text/html');
|
||||
req.is('text/*');
|
||||
// => true
|
||||
|
||||
// When Content-Type is application/json
|
||||
req.is('json');
|
||||
req.is('application/json');
|
||||
req.is('application/*');
|
||||
// => true
|
||||
|
||||
req.is('html');
|
||||
// => false
|
||||
|
||||
Now within our route callbacks, we can use to to assert content types
|
||||
such as "image/jpeg", "image/png", etc.
|
||||
|
||||
app.post('/image/upload', function(req, res, next){
|
||||
if (req.is('image/*')) {
|
||||
// do something
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
# res
|
||||
|
||||
Response prototype.
|
||||
|
||||
# res.status()
|
||||
|
||||
Set status `code`.
|
||||
|
||||
# res.send()
|
||||
|
||||
Send a response.
|
||||
|
||||
## Examples
|
||||
|
||||
res.send(new Buffer('wahoo'));
|
||||
res.send({ some: 'json' });
|
||||
res.send('<p>some html</p>');
|
||||
res.send(404, 'Sorry, cant find that');
|
||||
res.send(404);
|
||||
|
||||
# res.json()
|
||||
|
||||
Send JSON response.
|
||||
|
||||
## Examples
|
||||
|
||||
res.json(null);
|
||||
res.json({ user: 'tj' });
|
||||
res.json(500, 'oh noes!');
|
||||
res.json(404, 'I dont have that');
|
||||
|
||||
# res.sendfile()
|
||||
|
||||
Transfer the file at the given `path`.
|
||||
|
||||
Automatically sets the _Content-Type_ response header field.
|
||||
The callback `fn(err)` is invoked when the transfer is complete
|
||||
or when an error occurs. Be sure to check `res.sentHeader`
|
||||
if you wish to attempt responding, as the header and some data
|
||||
may have already been transferred.
|
||||
|
||||
## Options
|
||||
|
||||
- `maxAge` defaulting to 0
|
||||
- `root` root directory for relative filenames
|
||||
|
||||
## Examples
|
||||
|
||||
The following example illustrates how `res.sendfile()` may
|
||||
be used as an alternative for the `static()` middleware for
|
||||
dynamic situations. The code backing `res.sendfile()` is actually
|
||||
the same code, so HTTP cache support etc is identical.
|
||||
|
||||
app.get('/user/:uid/photos/:file', function(req, res){
|
||||
var uid = req.params.uid
|
||||
, file = req.params.file;
|
||||
|
||||
req.user.mayViewFilesFrom(uid, function(yes){
|
||||
if (yes) {
|
||||
res.sendfile('/uploads/' + uid + '/' + file);
|
||||
} else {
|
||||
res.send(403, 'Sorry! you cant see that.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
# res.download()
|
||||
|
||||
Transfer the file at the given `path` as an attachment.
|
||||
|
||||
Optionally providing an alternate attachment `filename`,
|
||||
and optional callback `fn(err)`. The callback is invoked
|
||||
when the data transfer is complete, or when an error has
|
||||
ocurred. Be sure to check `res.headerSent` if you plan to respond.
|
||||
|
||||
# res.format()
|
||||
|
||||
Respond to the Acceptable formats using an `obj`
|
||||
of mime-type callbacks.
|
||||
|
||||
This method uses `req.accepted`, an array of
|
||||
acceptable types ordered by their quality values.
|
||||
When "Accept" is not present the _first_ callback
|
||||
is invoked, otherwise the first match is used. When
|
||||
no match is performed the server responds with
|
||||
406 "Not Acceptable".
|
||||
|
||||
Content-Type is set for you, however if you choose
|
||||
you may alter this within the callback using `res.type()`
|
||||
or `res.set('Content-Type', ...)`.
|
||||
|
||||
res.format({
|
||||
'text/plain': function(){
|
||||
res.send('hey');
|
||||
},
|
||||
|
||||
'text/html': function(){
|
||||
res.send('<p>hey</p>');
|
||||
},
|
||||
|
||||
'appliation/json': function(){
|
||||
res.send({ message: 'hey' });
|
||||
}
|
||||
});
|
||||
|
||||
In addition to canonicalized MIME types you may
|
||||
## also use extnames mapped to these types
|
||||
|
||||
res.format({
|
||||
text: function(){
|
||||
res.send('hey');
|
||||
},
|
||||
|
||||
html: function(){
|
||||
res.send('<p>hey</p>');
|
||||
},
|
||||
|
||||
json: function(){
|
||||
res.send({ message: 'hey' });
|
||||
}
|
||||
});
|
||||
|
||||
# res.attachment()
|
||||
|
||||
Set _Content-Disposition_ header to _attachment_ with optional `filename`.
|
||||
|
||||
# res.set()
|
||||
|
||||
Set header `field` to `val`, or pass
|
||||
an object of of header fields.
|
||||
|
||||
## Examples
|
||||
|
||||
res.set('Accept', 'application/json');
|
||||
res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
|
||||
|
||||
# res.get()
|
||||
|
||||
Get value for header `field`.
|
||||
|
||||
# res.clearCookie()
|
||||
|
||||
Clear cookie `name`.
|
||||
|
||||
# res.signedCookie()
|
||||
|
||||
Set a signed cookie with the given `name` and `val`.
|
||||
See `res.cookie()` for details.
|
||||
|
||||
# res.cookie()
|
||||
|
||||
Set cookie `name` to `val`, with the given `options`.
|
||||
|
||||
## Options
|
||||
|
||||
- `maxAge` max-age in milliseconds, converted to `expires`
|
||||
- `path` defaults to "/"
|
||||
|
||||
## Examples
|
||||
|
||||
// "Remember Me" for 15 minutes
|
||||
res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
|
||||
|
||||
// save as above
|
||||
res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
|
||||
|
||||
# res.redirect()
|
||||
|
||||
Redirect to the given `url` with optional response `status`
|
||||
defaulting to 302.
|
||||
|
||||
The given `url` can also be the name of a mapped url, for
|
||||
example by default express supports "back" which redirects
|
||||
to the _Referrer_ or _Referer_ headers or "/".
|
||||
|
||||
## Examples
|
||||
|
||||
res.redirect('/foo/bar');
|
||||
res.redirect('http://example.com');
|
||||
res.redirect(301, 'http://example.com');
|
||||
res.redirect('../login'); // /blog/post/1 -> /blog/login
|
||||
|
||||
## Mounting
|
||||
|
||||
When an application is mounted, and `res.redirect()`
|
||||
is given a path that does _not_ lead with "/". For
|
||||
example suppose a "blog" app is mounted at "/blog",
|
||||
the following redirect would result in "/blog/login":
|
||||
|
||||
res.redirect('login');
|
||||
|
||||
While the leading slash would result in a redirect to "/login":
|
||||
|
||||
res.redirect('/login');
|
||||
|
||||
# res.render()
|
||||
|
||||
Render `view` with the given `options` and optional callback `fn`.
|
||||
When a callback function is given a response will _not_ be made
|
||||
automatically, otherwise a response of _200_ and _text/html_ is given.
|
||||
|
||||
## Options
|
||||
|
||||
- `status` Response status code (`res.statusCode`)
|
||||
- `charset` Set the charset (`res.charset`)
|
||||
|
||||
## Reserved locals
|
||||
|
||||
- `cache` boolean hinting to the engine it should cache
|
||||
- `filename` filename of the view being rendered
|
||||
|
||||
118
docs/request.md
118
docs/request.md
@@ -1,118 +0,0 @@
|
||||
|
||||
# req
|
||||
|
||||
Request prototype.
|
||||
|
||||
# req.get
|
||||
|
||||
Return request header.
|
||||
|
||||
The `Referrer` header field is special-cased,
|
||||
both `Referrer` and `Referer` are interchangeable.
|
||||
|
||||
## Examples
|
||||
|
||||
req.get('Content-Type');
|
||||
// => "text/plain"
|
||||
|
||||
req.get('content-type');
|
||||
// => "text/plain"
|
||||
|
||||
req.get('Something');
|
||||
// => undefined
|
||||
|
||||
Aliased as `req.header()`.
|
||||
|
||||
# req.accepts()
|
||||
|
||||
Check if the given `type(s)` is acceptable, returning
|
||||
the best match when true, otherwise `undefined`, in which
|
||||
case you should respond with 406 "Not Acceptable".
|
||||
|
||||
The `type` value may be a single mime type string
|
||||
such as "application/json", the extension name
|
||||
such as "json", a comma-delimted list such as "json, html, text/plain",
|
||||
or an array `["json", "html", "text/plain"]`. When a list
|
||||
or array is given the _best_ match, if any is returned.
|
||||
|
||||
## Examples
|
||||
|
||||
// Accept: text/html
|
||||
req.accepts('html');
|
||||
// => "html"
|
||||
|
||||
// Accept: text/*, application/json
|
||||
req.accepts('html');
|
||||
// => "html"
|
||||
req.accepts('text/html');
|
||||
// => "text/html"
|
||||
req.accepts('json, text');
|
||||
// => "json"
|
||||
req.accepts('application/json');
|
||||
// => "application/json"
|
||||
|
||||
// Accept: text/*, application/json
|
||||
req.accepts('image/png');
|
||||
req.accepts('png');
|
||||
// => undefined
|
||||
|
||||
// Accept: text/*;q=.5, application/json
|
||||
req.accepts(['html', 'json']);
|
||||
req.accepts('html, json');
|
||||
// => "json"
|
||||
|
||||
# req.acceptsCharset()
|
||||
|
||||
Check if the given `charset` is acceptable,
|
||||
otherwise you should respond with 406 "Not Acceptable".
|
||||
|
||||
# req.acceptsLanguage()
|
||||
|
||||
Check if the given `lang` is acceptable,
|
||||
otherwise you should respond with 406 "Not Acceptable".
|
||||
|
||||
# req.param()
|
||||
|
||||
Return the value of param `name` when present or `defaultValue`.
|
||||
|
||||
- Checks body params, ex: id=12, {"id":12}
|
||||
- Checks route placeholders, ex: _/user/:id_
|
||||
- Checks query string params, ex: ?id=12
|
||||
|
||||
To utilize request bodies, `req.body`
|
||||
should be an object. This can be done by using
|
||||
the `connect.bodyParser()` middleware.
|
||||
|
||||
# req.is()
|
||||
|
||||
Check if the incoming request contains the "Content-Type"
|
||||
header field, and it contains the give mime `type`.
|
||||
|
||||
## Examples
|
||||
|
||||
// With Content-Type: text/html; charset=utf-8
|
||||
req.is('html');
|
||||
req.is('text/html');
|
||||
req.is('text/*');
|
||||
// => true
|
||||
|
||||
// When Content-Type is application/json
|
||||
req.is('json');
|
||||
req.is('application/json');
|
||||
req.is('application/*');
|
||||
// => true
|
||||
|
||||
req.is('html');
|
||||
// => false
|
||||
|
||||
Now within our route callbacks, we can use to to assert content types
|
||||
such as "image/jpeg", "image/png", etc.
|
||||
|
||||
app.post('/image/upload', function(req, res, next){
|
||||
if (req.is('image/*')) {
|
||||
// do something
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
212
docs/response.md
212
docs/response.md
@@ -1,212 +0,0 @@
|
||||
|
||||
# res
|
||||
|
||||
Response prototype.
|
||||
|
||||
# res.status()
|
||||
|
||||
Set status `code`.
|
||||
|
||||
# res.send()
|
||||
|
||||
Send a response.
|
||||
|
||||
## Examples
|
||||
|
||||
res.send(new Buffer('wahoo'));
|
||||
res.send({ some: 'json' });
|
||||
res.send('<p>some html</p>');
|
||||
res.send(404, 'Sorry, cant find that');
|
||||
res.send(404);
|
||||
|
||||
# res.json()
|
||||
|
||||
Send JSON response.
|
||||
|
||||
## Examples
|
||||
|
||||
res.json(null);
|
||||
res.json({ user: 'tj' });
|
||||
res.json(500, 'oh noes!');
|
||||
res.json(404, 'I dont have that');
|
||||
|
||||
# res.sendfile()
|
||||
|
||||
Transfer the file at the given `path`.
|
||||
|
||||
Automatically sets the _Content-Type_ response header field.
|
||||
The callback `fn(err)` is invoked when the transfer is complete
|
||||
or when an error occurs. Be sure to check `res.sentHeader`
|
||||
if you wish to attempt responding, as the header and some data
|
||||
may have already been transferred.
|
||||
|
||||
## Options
|
||||
|
||||
- `maxAge` defaulting to 0
|
||||
- `root` root directory for relative filenames
|
||||
|
||||
## Examples
|
||||
|
||||
The following example illustrates how `res.sendfile()` may
|
||||
be used as an alternative for the `static()` middleware for
|
||||
dynamic situations. The code backing `res.sendfile()` is actually
|
||||
the same code, so HTTP cache support etc is identical.
|
||||
|
||||
app.get('/user/:uid/photos/:file', function(req, res){
|
||||
var uid = req.params.uid
|
||||
, file = req.params.file;
|
||||
|
||||
req.user.mayViewFilesFrom(uid, function(yes){
|
||||
if (yes) {
|
||||
res.sendfile('/uploads/' + uid + '/' + file);
|
||||
} else {
|
||||
res.send(403, 'Sorry! you cant see that.');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
# res.download()
|
||||
|
||||
Transfer the file at the given `path` as an attachment.
|
||||
|
||||
Optionally providing an alternate attachment `filename`,
|
||||
and optional callback `fn(err)`. The callback is invoked
|
||||
when the data transfer is complete, or when an error has
|
||||
ocurred. Be sure to check `res.headerSent` if you plan to respond.
|
||||
|
||||
# res.format()
|
||||
|
||||
Respond to the Acceptable formats using an `obj`
|
||||
of mime-type callbacks.
|
||||
|
||||
This method uses `req.accepted`, an array of
|
||||
acceptable types ordered by their quality values.
|
||||
When "Accept" is not present the _first_ callback
|
||||
is invoked, otherwise the first match is used. When
|
||||
no match is performed the server responds with
|
||||
406 "Not Acceptable".
|
||||
|
||||
Content-Type is set for you, however if you choose
|
||||
you may alter this within the callback using `res.type()`
|
||||
or `res.set('Content-Type', ...)`.
|
||||
|
||||
res.format({
|
||||
'text/plain': function(){
|
||||
res.send('hey');
|
||||
},
|
||||
|
||||
'text/html': function(){
|
||||
res.send('<p>hey</p>');
|
||||
},
|
||||
|
||||
'appliation/json': function(){
|
||||
res.send({ message: 'hey' });
|
||||
}
|
||||
});
|
||||
|
||||
In addition to canonicalized MIME types you may
|
||||
## also use extnames mapped to these types
|
||||
|
||||
res.format({
|
||||
text: function(){
|
||||
res.send('hey');
|
||||
},
|
||||
|
||||
html: function(){
|
||||
res.send('<p>hey</p>');
|
||||
},
|
||||
|
||||
json: function(){
|
||||
res.send({ message: 'hey' });
|
||||
}
|
||||
});
|
||||
|
||||
# res.attachment()
|
||||
|
||||
Set _Content-Disposition_ header to _attachment_ with optional `filename`.
|
||||
|
||||
# res.set()
|
||||
|
||||
Set header `field` to `val`, or pass
|
||||
an object of of header fields.
|
||||
|
||||
## Examples
|
||||
|
||||
res.set('Accept', 'application/json');
|
||||
res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
|
||||
|
||||
# res.get()
|
||||
|
||||
Get value for header `field`.
|
||||
|
||||
# res.clearCookie()
|
||||
|
||||
Clear cookie `name`.
|
||||
|
||||
# res.signedCookie()
|
||||
|
||||
Set a signed cookie with the given `name` and `val`.
|
||||
See `res.cookie()` for details.
|
||||
|
||||
# res.cookie()
|
||||
|
||||
Set cookie `name` to `val`, with the given `options`.
|
||||
|
||||
## Options
|
||||
|
||||
- `maxAge` max-age in milliseconds, converted to `expires`
|
||||
- `path` defaults to "/"
|
||||
|
||||
## Examples
|
||||
|
||||
// "Remember Me" for 15 minutes
|
||||
res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
|
||||
|
||||
// save as above
|
||||
res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
|
||||
|
||||
# res.redirect()
|
||||
|
||||
Redirect to the given `url` with optional response `status`
|
||||
defaulting to 302.
|
||||
|
||||
The given `url` can also be the name of a mapped url, for
|
||||
example by default express supports "back" which redirects
|
||||
to the _Referrer_ or _Referer_ headers or "/".
|
||||
|
||||
## Examples
|
||||
|
||||
res.redirect('/foo/bar');
|
||||
res.redirect('http://example.com');
|
||||
res.redirect(301, 'http://example.com');
|
||||
res.redirect('../login'); // /blog/post/1 -> /blog/login
|
||||
|
||||
## Mounting
|
||||
|
||||
When an application is mounted, and `res.redirect()`
|
||||
is given a path that does _not_ lead with "/". For
|
||||
example suppose a "blog" app is mounted at "/blog",
|
||||
the following redirect would result in "/blog/login":
|
||||
|
||||
res.redirect('login');
|
||||
|
||||
While the leading slash would result in a redirect to "/login":
|
||||
|
||||
res.redirect('/login');
|
||||
|
||||
# res.render()
|
||||
|
||||
Render `view` with the given `options` and optional callback `fn`.
|
||||
When a callback function is given a response will _not_ be made
|
||||
automatically, otherwise a response of _200_ and _text/html_ is given.
|
||||
|
||||
## Options
|
||||
|
||||
- `status` Response status code (`res.statusCode`)
|
||||
- `charset` Set the charset (`res.charset`)
|
||||
|
||||
## Reserved locals
|
||||
|
||||
- `cache` boolean hinting to the engine it should cache
|
||||
- `filename` filename of the view being rendered
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, crypto = require('crypto');
|
||||
var express = require('../..')
|
||||
, hash = require('./pass').hash;
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// config
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// middleware
|
||||
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.cookieParser('shhhh, very secret'));
|
||||
app.use(express.session());
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// Session-persisted message middleware
|
||||
|
||||
app.locals.use(function(req,res){
|
||||
app.use(function(req, res, next){
|
||||
var err = req.session.error
|
||||
, msg = req.session.success;
|
||||
delete req.session.error;
|
||||
@@ -25,39 +28,41 @@ app.locals.use(function(req,res){
|
||||
res.locals.message = '';
|
||||
if (err) res.locals.message = '<p class="msg error">' + err + '</p>';
|
||||
if (msg) res.locals.message = '<p class="msg success">' + msg + '</p>';
|
||||
})
|
||||
next();
|
||||
});
|
||||
|
||||
// dummy database
|
||||
|
||||
// Generate a salt for the user to prevent rainbow table attacks
|
||||
// for better security take a look at the bcrypt c++ addon:
|
||||
// https://github.com/ncb000gt/node.bcrypt.js
|
||||
var users = {
|
||||
tj: {
|
||||
name: 'tj'
|
||||
, salt: 'randomly-generated-salt'
|
||||
, pass: hash('foobar', 'randomly-generated-salt')
|
||||
}
|
||||
tj: { name: 'tj' }
|
||||
};
|
||||
|
||||
// Used to generate a hash of the plain-text password + salt
|
||||
function hash(msg, key) {
|
||||
return crypto
|
||||
.createHmac('sha256', key)
|
||||
.update(msg)
|
||||
.digest('hex');
|
||||
}
|
||||
// when you create a user, generate a salt
|
||||
// and hash the password ('foobar' is the pass here)
|
||||
|
||||
hash('foobar', function(err, salt, hash){
|
||||
if (err) throw err;
|
||||
// store the salt & hash in the "db"
|
||||
users.tj.salt = salt;
|
||||
users.tj.hash = hash;
|
||||
});
|
||||
|
||||
|
||||
// Authenticate using our plain-object database of doom!
|
||||
|
||||
function authenticate(name, pass, fn) {
|
||||
console.log('authenticating %s:%s', name, pass);
|
||||
if (!module.parent) console.log('authenticating %s:%s', name, pass);
|
||||
var user = users[name];
|
||||
// query the db for the given username
|
||||
if (!user) return fn(new Error('cannot find user'));
|
||||
// apply the same algorithm to the POSTed password, applying
|
||||
// the hash against the pass / salt, if there is a match we
|
||||
// found the user
|
||||
if (user.pass == hash(pass, user.salt)) return fn(null, user);
|
||||
// Otherwise password is invalid
|
||||
fn(new Error('invalid password'));
|
||||
hash(pass, user.salt, function(err, hash){
|
||||
if (err) return fn(err);
|
||||
if (hash == user.hash) return fn(null, user);
|
||||
fn(new Error('invalid password'));
|
||||
})
|
||||
}
|
||||
|
||||
function restrict(req, res, next) {
|
||||
@@ -74,7 +79,7 @@ app.get('/', function(req, res){
|
||||
});
|
||||
|
||||
app.get('/restricted', restrict, function(req, res){
|
||||
res.send('Wahoo! restricted area');
|
||||
res.send('Wahoo! restricted area, click to <a href="/logout">logout</a>');
|
||||
});
|
||||
|
||||
app.get('/logout', function(req, res){
|
||||
@@ -86,11 +91,6 @@ app.get('/logout', function(req, res){
|
||||
});
|
||||
|
||||
app.get('/login', function(req, res){
|
||||
if (req.session.user) {
|
||||
req.session.success = 'Authenticated as ' + req.session.user.name
|
||||
+ ' click to <a href="/logout">logout</a>. '
|
||||
+ ' You may now access <a href="/restricted">/restricted</a>.';
|
||||
}
|
||||
res.render('login');
|
||||
});
|
||||
|
||||
@@ -104,6 +104,9 @@ app.post('/login', function(req, res){
|
||||
// in the session store to be retrieved,
|
||||
// or in this case the entire user object
|
||||
req.session.user = user;
|
||||
req.session.success = 'Authenticated as ' + user.name
|
||||
+ ' click to <a href="/logout">logout</a>. '
|
||||
+ ' You may now access <a href="/restricted">/restricted</a>.';
|
||||
res.redirect('back');
|
||||
});
|
||||
} else {
|
||||
@@ -118,4 +121,4 @@ app.post('/login', function(req, res){
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
}
|
||||
|
||||
48
examples/auth/pass.js
Normal file
48
examples/auth/pass.js
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
// check out https://github.com/visionmedia/node-pwd
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var crypto = require('crypto');
|
||||
|
||||
/**
|
||||
* Bytesize.
|
||||
*/
|
||||
|
||||
var len = 128;
|
||||
|
||||
/**
|
||||
* Iterations. ~300ms
|
||||
*/
|
||||
|
||||
var iterations = 12000;
|
||||
|
||||
/**
|
||||
* Hashes a password with optional `salt`, otherwise
|
||||
* generate a salt for `pass` and invoke `fn(err, salt, hash)`.
|
||||
*
|
||||
* @param {String} password to hash
|
||||
* @param {String} optional salt
|
||||
* @param {Function} callback
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.hash = function (pwd, salt, fn) {
|
||||
if (3 == arguments.length) {
|
||||
crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){
|
||||
fn(err, (new Buffer(hash, 'binary')).toString('base64'));
|
||||
});
|
||||
} else {
|
||||
fn = salt;
|
||||
crypto.randomBytes(len, function(err, salt){
|
||||
if (err) return fn(err);
|
||||
salt = salt.toString('base64');
|
||||
crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){
|
||||
if (err) return fn(err);
|
||||
fn(null, salt, (new Buffer(hash, 'binary')).toString('base64'));
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
2
examples/auth/views/foot.ejs
Normal file
2
examples/auth/views/foot.ejs
Normal file
@@ -0,0 +1,2 @@
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Authentication Example</title>
|
||||
<title><%= title %></title>
|
||||
<style>
|
||||
body {
|
||||
padding: 50px;
|
||||
@@ -16,6 +16,3 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,3 +1,7 @@
|
||||
|
||||
<% var title = 'Authentication Example' %>
|
||||
<% include head %>
|
||||
|
||||
<h1>Login</h1>
|
||||
<%- message %>
|
||||
Try accessing <a href="/restricted">/restricted</a>, then authenticate with "tj" and "foobar".
|
||||
@@ -13,4 +17,6 @@ Try accessing <a href="/restricted">/restricted</a>, then authenticate with "tj"
|
||||
<p>
|
||||
<input type="submit" value="Login">
|
||||
</p>
|
||||
</form>
|
||||
</form>
|
||||
|
||||
<% include foot %>
|
||||
|
||||
24
examples/big-view/index.js
Normal file
24
examples/big-view/index.js
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
var express = require('../..')
|
||||
, app = express();
|
||||
|
||||
app.set('views', __dirname);
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
var pets = [];
|
||||
|
||||
var n = 1000;
|
||||
while (n--) {
|
||||
pets.push({ name: 'Tobi', age: 2, species: 'ferret' });
|
||||
pets.push({ name: 'Loki', age: 1, species: 'ferret' });
|
||||
pets.push({ name: 'Jane', age: 6, species: 'ferret' });
|
||||
}
|
||||
|
||||
app.use(express.logger('dev'));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('pets', { pets: pets });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express listening on port 3000');
|
||||
12
examples/big-view/pets.jade
Normal file
12
examples/big-view/pets.jade
Normal file
@@ -0,0 +1,12 @@
|
||||
style
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 16px "Helvetica Neue", Helvetica;
|
||||
}
|
||||
|
||||
table
|
||||
for pet in pets
|
||||
tr
|
||||
td= pet.name
|
||||
td= pet.age
|
||||
td= pet.species
|
||||
@@ -1,50 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
|
||||
// config
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
// middleware
|
||||
|
||||
app.configure('development',function(){
|
||||
app.use(express.logger('dev'));
|
||||
})
|
||||
|
||||
app.configure(function(){
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.cookieParser('keyboard cat'));
|
||||
app.use(express.session());
|
||||
app.use(app.router);
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||
});
|
||||
|
||||
// Locals
|
||||
|
||||
app.locals.use(function(req, res){
|
||||
// expose "error" and "message" to all
|
||||
// views that are rendered.
|
||||
res.locals.error = req.session.error || '';
|
||||
res.locals.message = req.session.message || '';
|
||||
// remove them so they're not displayed on subsequent renders
|
||||
delete req.session.error;
|
||||
delete req.session.message;
|
||||
});
|
||||
|
||||
// Routes
|
||||
|
||||
require('./routes/site')(app);
|
||||
require('./routes/post')(app);
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
|
||||
// Fake data store
|
||||
|
||||
var ids = 0
|
||||
, db = {};
|
||||
|
||||
var Post = exports = module.exports = function Post(title, body) {
|
||||
this.id = ++ids;
|
||||
this.title = title;
|
||||
this.body = body;
|
||||
this.createdAt = new Date;
|
||||
};
|
||||
|
||||
Post.prototype.save = function(fn){
|
||||
db[this.id] = this;
|
||||
fn();
|
||||
};
|
||||
|
||||
Post.prototype.validate = function(fn){
|
||||
if (!this.title) return fn(new Error('title required'));
|
||||
if (!this.body) return fn(new Error('body required'));
|
||||
if (this.body.length < 10) {
|
||||
return fn(new Error(
|
||||
'body should be at least 10 characters long, was only ' + this.body.length));
|
||||
}
|
||||
fn();
|
||||
};
|
||||
|
||||
|
||||
Post.prototype.update = function(data, fn){
|
||||
this.updatedAt = new Date;
|
||||
for (var key in data) {
|
||||
if (undefined != data[key]) {
|
||||
this[key] = data[key];
|
||||
}
|
||||
}
|
||||
this.save(fn);
|
||||
};
|
||||
|
||||
Post.prototype.destroy = function(fn){
|
||||
exports.destroy(this.id, fn);
|
||||
};
|
||||
|
||||
exports.count = function(fn){
|
||||
fn(null, Object.keys(db).length);
|
||||
};
|
||||
|
||||
exports.all = function(fn){
|
||||
var arr = Object.keys(db).reduce(function(arr, id){
|
||||
arr.push(db[id]);
|
||||
return arr;
|
||||
}, []);
|
||||
fn(null, arr);
|
||||
};
|
||||
|
||||
exports.get = function(id, fn){
|
||||
fn(null, db[id]);
|
||||
};
|
||||
|
||||
exports.destroy = function(id, fn) {
|
||||
if (db[id]) {
|
||||
delete db[id];
|
||||
fn();
|
||||
} else {
|
||||
fn(new Error('post ' + id + ' does not exist'));
|
||||
}
|
||||
};
|
||||
@@ -1,56 +0,0 @@
|
||||
body {
|
||||
font: 13px "Helvetica Neue", Arial, sans-serif;
|
||||
color: #111;
|
||||
padding: 60px 80px;
|
||||
}
|
||||
h1, h2 {
|
||||
color: #c00;
|
||||
}
|
||||
a {
|
||||
color: #c00;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
label {
|
||||
padding: 6px 0;
|
||||
display: block;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
textarea,
|
||||
input {
|
||||
outline: none;
|
||||
padding: 5px;
|
||||
background: #f1f1f1;
|
||||
border: 1px solid #aaa;
|
||||
}
|
||||
textarea:focus,
|
||||
input:focus {
|
||||
background: #fff;
|
||||
}
|
||||
textarea {
|
||||
width: 500px;
|
||||
height: 200px;
|
||||
}
|
||||
a.edit {
|
||||
margin-left: 10px;
|
||||
font-size: 11px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.date {
|
||||
font-size: 11px;
|
||||
}
|
||||
p.error,
|
||||
p.message {
|
||||
padding: 10px;
|
||||
border: 1px solid;
|
||||
}
|
||||
p.error {
|
||||
background: #FDEAE7;
|
||||
color: #E4250C;
|
||||
}
|
||||
p.message {
|
||||
color: #2EBBE6;
|
||||
background: #F7FBFD;
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var basicAuth = require('../../../lib/express').basicAuth
|
||||
, Post = require('../models/post');
|
||||
|
||||
module.exports = function(app){
|
||||
/**
|
||||
* Apply basic auth to all post related routes
|
||||
*/
|
||||
|
||||
app.all('/post(/*)?', basicAuth(function(user, pass){
|
||||
return 'admin' == user && 'express' == pass;
|
||||
}));
|
||||
|
||||
/**
|
||||
* Map :post to the database, loading
|
||||
* every time :post is present.
|
||||
*/
|
||||
|
||||
app.param('post', function(req, res, next, id){
|
||||
Post.get(id, function(err, post){
|
||||
if (err) return next(err);
|
||||
if (!post) return next(new Error('failed to load post ' + id));
|
||||
req.post = post;
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a post.
|
||||
*/
|
||||
|
||||
app.get('/post/add', function(req, res){
|
||||
res.render('post/form', { post: {}});
|
||||
});
|
||||
|
||||
/**
|
||||
* Save a post.
|
||||
*/
|
||||
|
||||
app.post('/post', function(req, res){
|
||||
var data = req.body.post || {}
|
||||
, post = new Post(data.title, data.body);
|
||||
|
||||
post.validate(function(err){
|
||||
if (err) {
|
||||
req.session.error = err.message;
|
||||
return res.redirect('back');
|
||||
}
|
||||
|
||||
post.save(function(err){
|
||||
req.session.message = 'Successfully created the post.';
|
||||
res.redirect('/post/' + post.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Display the post.
|
||||
*/
|
||||
|
||||
app.get('/post/:post', function(req, res){
|
||||
res.render('post', { post: req.post });
|
||||
});
|
||||
|
||||
/**
|
||||
* Display the post edit form.
|
||||
*/
|
||||
|
||||
app.get('/post/:post/edit', function(req, res){
|
||||
res.render('post/form', { post: req.post });
|
||||
});
|
||||
|
||||
/**
|
||||
* Update post. Typically a data layer would handle this stuff.
|
||||
*/
|
||||
|
||||
app.put('/post/:post', function(req, res, next){
|
||||
var post = req.post;
|
||||
post.validate(function(err){
|
||||
if (err) {
|
||||
req.session.error = err.message;
|
||||
return res.redirect('back');
|
||||
}
|
||||
|
||||
post.update(req.body.post, function(err){
|
||||
if (err) return next(err);
|
||||
req.session.message = 'Successfully updated post';
|
||||
res.redirect('back');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Post = require('../models/post');
|
||||
|
||||
module.exports = function(app){
|
||||
app.get('/', function(req, res){
|
||||
Post.count(function(err, count){
|
||||
Post.all(function(err, posts){
|
||||
res.render('index', {
|
||||
count: count
|
||||
, posts: posts
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
extends layout
|
||||
|
||||
block content
|
||||
h1 Blog
|
||||
|
||||
if count
|
||||
p Display all #{count} post(s)
|
||||
#posts
|
||||
each post in posts
|
||||
include post/index
|
||||
else
|
||||
p
|
||||
| It looks like you have no posts!
|
||||
p
|
||||
| Click
|
||||
a(href='/post/add') here
|
||||
| to create a post. Login
|
||||
| as
|
||||
em "admin"
|
||||
| and
|
||||
em "express"
|
||||
| .
|
||||
@@ -1,8 +0,0 @@
|
||||
!!! 5
|
||||
html
|
||||
head
|
||||
title Blog
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
body
|
||||
#container
|
||||
block content
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
if error
|
||||
p.error= error
|
||||
|
||||
if message
|
||||
p.message= message
|
||||
@@ -1,22 +0,0 @@
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
if post.title
|
||||
h1 Editing #{post.title}
|
||||
else
|
||||
h1 New Post
|
||||
|
||||
include ../messages
|
||||
|
||||
form#post(action='/post' + (post.title ? '/' + post.id : ''), method='post')
|
||||
if post.title
|
||||
input(type='hidden', name='_method', value='put')
|
||||
p
|
||||
label(for='post[title]') Title:
|
||||
input(type='text', name='post[title]', value=post.title)
|
||||
p
|
||||
label(for='post[body]') Body:
|
||||
textarea(name='post[body]')= post.body || ''
|
||||
p
|
||||
input(type='submit', value=post.title ? 'Update' : 'Create')
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
.post
|
||||
// title
|
||||
h2
|
||||
= post.title
|
||||
a.edit(href='/post/' + post.id + '/edit') Edit
|
||||
|
||||
include ../messages
|
||||
|
||||
// dates
|
||||
p.date.created Created at #{post.createdAt}
|
||||
if post.updatedAt
|
||||
p.date.updated Updated at #{post.updatedAt}
|
||||
|
||||
// body
|
||||
pre.body= post.body
|
||||
8
examples/content-negotiation/db.js
Normal file
8
examples/content-negotiation/db.js
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
var users = [];
|
||||
|
||||
users.push({ name: 'Tobi' });
|
||||
users.push({ name: 'Loki' });
|
||||
users.push({ name: 'Jane' });
|
||||
|
||||
module.exports = users;
|
||||
@@ -1,12 +1,7 @@
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
|
||||
var users = [];
|
||||
|
||||
users.push({ name: 'Tobi' });
|
||||
users.push({ name: 'Loki' });
|
||||
users.push({ name: 'Jane' });
|
||||
, app = module.exports = express()
|
||||
, users = require('./db');
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.format({
|
||||
@@ -28,6 +23,18 @@ app.get('/', function(req, res){
|
||||
})
|
||||
});
|
||||
|
||||
// or you could write a tiny middleware like
|
||||
// this to abstract make things a bit more declarative:
|
||||
|
||||
function format(mod) {
|
||||
var obj = require(mod);
|
||||
return function(req, res){
|
||||
res.format(obj);
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/users', format('./users'));
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('listening on port 3000');
|
||||
|
||||
18
examples/content-negotiation/users.js
Normal file
18
examples/content-negotiation/users.js
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
var users = require('./db');
|
||||
|
||||
exports.html = function(req, res){
|
||||
res.send('<ul>' + users.map(function(user){
|
||||
return '<li>' + user.name + '</li>';
|
||||
}).join('') + '</ul>');
|
||||
};
|
||||
|
||||
exports.text = function(req, res){
|
||||
res.send(users.map(function(user){
|
||||
return ' - ' + user.name + '\n';
|
||||
}).join(''));
|
||||
};
|
||||
|
||||
exports.json = function(req, res){
|
||||
res.json(users);
|
||||
};
|
||||
46
examples/cors/index.js
Normal file
46
examples/cors/index.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..')
|
||||
, app = express()
|
||||
, api = express();
|
||||
|
||||
// app middleware
|
||||
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// api middleware
|
||||
|
||||
api.use(express.logger('dev'));
|
||||
api.use(express.bodyParser());
|
||||
|
||||
/**
|
||||
* CORS support.
|
||||
*/
|
||||
|
||||
api.all('*', function(req, res, next){
|
||||
if (!req.get('Origin')) return next();
|
||||
// use "*" here to accept any origin
|
||||
res.set('Access-Control-Allow-Origin', 'http://localhost:3000');
|
||||
res.set('Access-Control-Allow-Methods', 'GET, POST');
|
||||
res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type');
|
||||
// res.set('Access-Control-Allow-Max-Age', 3600);
|
||||
if ('OPTIONS' == req.method) return res.send(200);
|
||||
next();
|
||||
});
|
||||
|
||||
/**
|
||||
* POST a user.
|
||||
*/
|
||||
|
||||
api.post('/user', function(req, res){
|
||||
console.log(req.body);
|
||||
res.send(201);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
api.listen(3001);
|
||||
|
||||
console.log('app listening on 3000');
|
||||
console.log('api listening on 3001');
|
||||
12
examples/cors/public/index.html
Normal file
12
examples/cors/public/index.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
var req = new XMLHttpRequest;
|
||||
req.open('POST', 'http://localhost:3001/user', false);
|
||||
req.setRequestHeader('Content-Type', 'application/json');
|
||||
req.send('{"name":"tobi","species":"ferret"}');
|
||||
console.log(req.responseText);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -31,16 +31,20 @@ app.set('view engine', 'html');
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
{ name: 'tobi', email: 'tobi@learnboost.com' }
|
||||
, { name: 'loki', email: 'loki@learnboost.com' }
|
||||
, { name: 'jane', email: 'jane@learnboost.com' }
|
||||
{ name: 'tobi', email: 'tobi@learnboost.com' },
|
||||
{ name: 'loki', email: 'loki@learnboost.com' },
|
||||
{ name: 'jane', email: 'jane@learnboost.com' }
|
||||
];
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('users', { users: users });
|
||||
res.render('users', {
|
||||
users: users,
|
||||
title: "EJS example",
|
||||
header: "Some users"
|
||||
});
|
||||
});
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
}
|
||||
}
|
||||
|
||||
2
examples/ejs/views/footer.html
Normal file
2
examples/ejs/views/footer.html
Normal file
@@ -0,0 +1,2 @@
|
||||
</body>
|
||||
</html>
|
||||
13
examples/ejs/views/header.html
Normal file
13
examples/ejs/views/header.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title> <%= title %> </title>
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 13px Helvetica, Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -1,6 +1,10 @@
|
||||
<% include header.html %>
|
||||
|
||||
<h1>Users</h1>
|
||||
<ul id="users">
|
||||
<% users.forEach(function(user){ %>
|
||||
<li><%= user.name %> <%= user.email %></li>
|
||||
<li><%= user.name %> <<%= user.email %>></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<% include footer.html %>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -7,9 +6,21 @@ var express = require('../../')
|
||||
, app = module.exports = express()
|
||||
, silent = 'test' == process.env.NODE_ENV;
|
||||
|
||||
// general config
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
// our custom "verbose errors" setting
|
||||
// which we can use in the templates
|
||||
// via settings['verbose errors']
|
||||
app.enable('verbose errors');
|
||||
|
||||
// disable them in production
|
||||
// use $ NODE_ENV=production node examples/error-pages
|
||||
if ('production' == app.settings.env) {
|
||||
app.disable('verbose errors');
|
||||
}
|
||||
|
||||
app.use(express.favicon());
|
||||
|
||||
silent || app.use(express.logger('dev'));
|
||||
@@ -32,9 +43,10 @@ app.use(app.router);
|
||||
// $ curl http://localhost:3000/notfound -H "Accept: text/plain"
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.status(404);
|
||||
|
||||
// respond with html page
|
||||
if (req.accepts('html')) {
|
||||
res.status(404);
|
||||
res.render('404', { url: req.url });
|
||||
return;
|
||||
}
|
||||
@@ -76,16 +88,21 @@ app.get('/', function(req, res){
|
||||
});
|
||||
|
||||
app.get('/404', function(req, res, next){
|
||||
// trigger a 404 since no other middleware
|
||||
// will match /404 after this one, and we're not
|
||||
// responding here
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/403', function(req, res, next){
|
||||
// trigger a 403 error
|
||||
var err = new Error('not allowed!');
|
||||
err.status = 403;
|
||||
next(err);
|
||||
});
|
||||
|
||||
app.get('/500', function(req, res, next){
|
||||
// trigger a generic (500) error
|
||||
next(new Error('keyboard cat!'));
|
||||
});
|
||||
|
||||
@@ -7,4 +7,7 @@ extends error
|
||||
|
||||
block content
|
||||
h1 Error: #{error.message}
|
||||
pre= error.stack
|
||||
if settings['verbose errors']
|
||||
pre= error.stack
|
||||
else
|
||||
p An error ocurred!
|
||||
@@ -8,8 +8,8 @@ block content
|
||||
| visit
|
||||
a(href="/500") 500
|
||||
li
|
||||
| visit
|
||||
| visit
|
||||
a(href="/404") 404
|
||||
li
|
||||
| visit
|
||||
| visit
|
||||
a(href='/403') 403
|
||||
@@ -4,10 +4,10 @@
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
, app = module.exports = express()
|
||||
, test = app.get('env') == 'test';
|
||||
|
||||
if ('test' != process.env.NODE_ENV)
|
||||
app.use(express.logger('dev'));
|
||||
if (!test) app.use(express.logger('dev'));
|
||||
app.use(app.router);
|
||||
|
||||
// the error handler is strategically
|
||||
@@ -24,8 +24,7 @@ app.use(error);
|
||||
|
||||
function error(err, req, res, next) {
|
||||
// log it
|
||||
if ('test' != process.env.NODE_ENV)
|
||||
console.error(err.stack);
|
||||
if (!test) console.error(err.stack);
|
||||
|
||||
// respond with 500 "Internal Server Error".
|
||||
res.send(500);
|
||||
@@ -45,14 +45,6 @@ app.use(function(req, res, next){
|
||||
next();
|
||||
});
|
||||
|
||||
// if you wanted to _always_ expose
|
||||
// the user you might do something like this:
|
||||
/*
|
||||
app.locals.use(function(req, res){
|
||||
if (req.user) res.locals.expose.user = req.user;
|
||||
})
|
||||
*/
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('/user');
|
||||
});
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// Here we use the bodyDecoder middleware
|
||||
// to parse urlencoded request bodies
|
||||
// which populates req.body
|
||||
app.use(express.bodyParser());
|
||||
|
||||
// The methodOverride middleware allows us
|
||||
// to set a hidden input of _method to an arbitrary
|
||||
// HTTP method to support app.put(), app.del() etc
|
||||
app.use(express.methodOverride());
|
||||
|
||||
// Required by session
|
||||
app.use(express.cookieParser());
|
||||
|
||||
// Required by req.flash() for persistent
|
||||
// notifications
|
||||
app.use(express.session({ secret: 'keyboard cat' }));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
// get ?name=foo
|
||||
var name = req.param('name') || '';
|
||||
|
||||
// Switch the button label based if we have a name
|
||||
var label = name ? 'Update' : 'Save';
|
||||
|
||||
// Buffer all flash messages.
|
||||
// Typically this would all be done in a template
|
||||
// however for illustration purposes we iterate
|
||||
// here.
|
||||
|
||||
// The messages in req.flash() persist until called,
|
||||
// at which time they are flushed from the session
|
||||
var msgs = '<ul>',
|
||||
flash = req.flash();
|
||||
Object.keys(flash).forEach(function(type){
|
||||
flash[type].forEach(function(msg){
|
||||
msgs += '<li class="' + type + '">' + msg + '</li>';
|
||||
});
|
||||
});
|
||||
msgs += '</ul>';
|
||||
|
||||
// If we have a name, we are updating,
|
||||
// so add the hidden _method input
|
||||
res.send(msgs
|
||||
+ '<form method="post">'
|
||||
+ (name ? '<input type="hidden" value="put" name="_method" />' : '')
|
||||
+ 'Name: <input type="text" name="name" value="' + name + '" />'
|
||||
+ '<input type="submit" value="' + label + '" />'
|
||||
+ '</form>');
|
||||
});
|
||||
|
||||
app.post('/', function(req, res){
|
||||
if (req.body.name) {
|
||||
// Typically here we would create a resource
|
||||
req.flash('info', 'Saved ' + req.body.name);
|
||||
res.redirect('/?name=' + req.body.name);
|
||||
} else {
|
||||
req.flash('error', 'Error: name required');
|
||||
res.redirect('/');
|
||||
}
|
||||
});
|
||||
|
||||
app.put('/', function(req, res){
|
||||
// Typically here we would update a resource
|
||||
req.flash('info', 'Updated ' + req.body.name);
|
||||
res.redirect('/?name=' + req.body.name);
|
||||
});
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, http = require('http');
|
||||
|
||||
var app = express();
|
||||
|
||||
// Expose our views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
/**
|
||||
* Request github json api `path`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function request(path, fn){
|
||||
var client = http.createClient(80, 'github.com')
|
||||
, req = client.request('GET', '/api/v2/json' + path, { Host: 'github.com' });
|
||||
req.on('response', function(res){
|
||||
res.body = '';
|
||||
res.on('data', function(chunk){ res.body += chunk; });
|
||||
res.on('end', function(){
|
||||
try {
|
||||
fn(null, JSON.parse(res.body));
|
||||
} catch (err) {
|
||||
fn(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
req.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort repositories by watchers desc.
|
||||
*
|
||||
* @param {Array} repos
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function sort(repos){
|
||||
return repos.sort(function(a, b){
|
||||
if (a.watchers == b.watchers) return 0;
|
||||
if (a.watchers > b.watchers) return -1;
|
||||
if (a.watchers < b.watchers) return 1;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tally up total watchers.
|
||||
*
|
||||
* @param {Array} repos
|
||||
* @return {Number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function totalWatchers(repos) {
|
||||
return repos.reduce(function(sum, repo){
|
||||
return sum + repo.watchers;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default to my user name :)
|
||||
*/
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('/repos/visionmedia');
|
||||
});
|
||||
|
||||
/**
|
||||
* Display repos.
|
||||
*/
|
||||
|
||||
app.get('/repos/*', function(req, res, next){
|
||||
var names = req.params[0].split('/')
|
||||
, users = [];
|
||||
(function fetchData(name){
|
||||
// We have a user name
|
||||
if (name) {
|
||||
console.log('... fetching \x1b[33m%s\x1b[0m', name);
|
||||
request('/repos/show/' + name, function(err, user){
|
||||
if (err) {
|
||||
next(err)
|
||||
} else {
|
||||
user.totalWatchers = totalWatchers(user.repositories);
|
||||
user.repos = sort(user.repositories);
|
||||
user.name = name;
|
||||
users.push(user);
|
||||
fetchData(names.shift());
|
||||
}
|
||||
});
|
||||
// No more users
|
||||
} else {
|
||||
console.log('... done');
|
||||
res.render('index', { users: users });
|
||||
}
|
||||
})(names.shift());
|
||||
});
|
||||
|
||||
// Serve statics from ./public
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// Listen on port 3000
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -1,19 +0,0 @@
|
||||
body {
|
||||
padding: 30px 50px;
|
||||
font: 12px/1.4 "Helvetica Neue", Arial, sans-serif;
|
||||
}
|
||||
a {
|
||||
color: #00AAFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.user {
|
||||
margin: 0 10px;
|
||||
float: left;
|
||||
width: 300px;
|
||||
}
|
||||
table td:nth-child(2) {
|
||||
padding: 0 5px;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
extends layout
|
||||
|
||||
block content
|
||||
each user in users
|
||||
.user
|
||||
h2= user.name
|
||||
p.summary
|
||||
| <a href="http://github.com/#{user.name}">#{user.name}</a> has
|
||||
| <strong>#{user.repos.length}</strong> repositories
|
||||
| with a total of <strong>#{user.totalWatchers}</strong> watchers.
|
||||
table#repos
|
||||
for repo in user.repos
|
||||
include repo
|
||||
@@ -1,8 +0,0 @@
|
||||
!!!
|
||||
html
|
||||
head
|
||||
title Github Example
|
||||
link(rel="stylesheet", href="/style.css")
|
||||
body
|
||||
#container
|
||||
block content
|
||||
@@ -1,5 +0,0 @@
|
||||
tr.repo
|
||||
td.name
|
||||
a(href: repo.homepage || repo.url)= repo.name
|
||||
td.watchers
|
||||
= repo.watchers
|
||||
@@ -1,8 +1,4 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
|
||||
var app = express();
|
||||
@@ -9,10 +9,9 @@ var express = require('../../lib/express');
|
||||
|
||||
var pub = __dirname + '/public';
|
||||
|
||||
// Auto-compile sass to css with "compiler"
|
||||
// and then serve with connect's staticProvider
|
||||
// setup middleware
|
||||
|
||||
var app = express.createServer();
|
||||
var app = express();
|
||||
app.use(app.router);
|
||||
app.use(express.static(pub));
|
||||
app.use(express.errorHandler());
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
body {
|
||||
padding: 50px 80px;
|
||||
font: 14px "Helvetica Nueue", "Lucida Grande", Arial, sans-serif;}
|
||||
font: 14px "Helvetica Nueue", "Lucida Grande", Arial, sans-serif;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
var express = require('../..')
|
||||
, fs = require('fs')
|
||||
, md = require('github-flavored-markdown').parse;
|
||||
|
||||
@@ -42,4 +42,4 @@ app.get('/fail', function(req, res){
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
var express = require('../..')
|
||||
, format = require('util').format;
|
||||
|
||||
var app = module.exports = express()
|
||||
4
examples/mvc/controllers/main/index.js
Normal file
4
examples/mvc/controllers/main/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
exports.index = function(req, res){
|
||||
res.redirect('/users');
|
||||
};
|
||||
26
examples/mvc/controllers/pet/index.js
Normal file
26
examples/mvc/controllers/pet/index.js
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
exports.engine = 'jade';
|
||||
|
||||
exports.before = function(req, res, next){
|
||||
var pet = db.pets[req.params.pet_id];
|
||||
if (!pet) return next(new Error('Pet not found'));
|
||||
req.pet = pet;
|
||||
next();
|
||||
};
|
||||
|
||||
exports.show = function(req, res, next){
|
||||
res.render('show', { pet: req.pet });
|
||||
};
|
||||
|
||||
exports.edit = function(req, res, next){
|
||||
res.render('edit', { pet: req.pet });
|
||||
};
|
||||
|
||||
exports.update = function(req, res, next){
|
||||
var body = req.body;
|
||||
req.pet.name = body.user.name;
|
||||
res.message('Information updated!');
|
||||
res.redirect('/pet/' + req.pet.id);
|
||||
};
|
||||
7
examples/mvc/controllers/pet/views/edit.jade
Normal file
7
examples/mvc/controllers/pet/views/edit.jade
Normal file
@@ -0,0 +1,7 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1= pet.name
|
||||
form(action='/pet/#{pet.id}', method='post')
|
||||
input(type='hidden', name='_method', value='put')
|
||||
label Name:
|
||||
input(type='text', name='user[name]', value=pet.name)
|
||||
input(type='submit', value='Update')
|
||||
6
examples/mvc/controllers/pet/views/show.jade
Normal file
6
examples/mvc/controllers/pet/views/show.jade
Normal file
@@ -0,0 +1,6 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
|
||||
h1= pet.name
|
||||
a(href='/pet/#{pet.id}/edit') edit
|
||||
|
||||
p You are viewing #{pet.name}
|
||||
17
examples/mvc/controllers/user-pet/index.js
Normal file
17
examples/mvc/controllers/user-pet/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
exports.name = 'pet';
|
||||
exports.prefix = '/user/:user_id';
|
||||
|
||||
exports.create = function(req, res, next){
|
||||
var id = req.params.user_id;
|
||||
var user = db.users[id];
|
||||
var body = req.body;
|
||||
if (!user) return next(new Error('User not found'));
|
||||
var pet = { name: body.pet.name };
|
||||
pet.id = db.pets.push(pet) - 1;
|
||||
user.pets.push(pet);
|
||||
res.message('Added pet ' + body.pet.name);
|
||||
res.redirect('/user/' + id);
|
||||
};
|
||||
34
examples/mvc/controllers/user/index.js
Normal file
34
examples/mvc/controllers/user/index.js
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
exports.before = function(req, res, next){
|
||||
var id = req.params.user_id;
|
||||
if (!id) return next();
|
||||
// pretend to query a database...
|
||||
process.nextTick(function(){
|
||||
req.user = db.users[id];
|
||||
// cant find that user
|
||||
if (!req.user) return next(new Error('User not found'));
|
||||
// found it, move on to the routes
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
exports.list = function(req, res, next){
|
||||
res.render('list', { users: db.users });
|
||||
};
|
||||
|
||||
exports.edit = function(req, res, next){
|
||||
res.render('edit', { user: req.user });
|
||||
};
|
||||
|
||||
exports.show = function(req, res, next){
|
||||
res.render('show', { user: req.user });
|
||||
};
|
||||
|
||||
exports.update = function(req, res, next){
|
||||
var body = req.body;
|
||||
req.user.name = body.user.name;
|
||||
res.message('Information updated!');
|
||||
res.redirect('/user/' + req.user.id);
|
||||
};
|
||||
12
examples/mvc/controllers/user/views/edit.html
Normal file
12
examples/mvc/controllers/user/views/edit.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1><%= user.name %></h1>
|
||||
<form action='/user/<%= user.id %>' method='post'>
|
||||
<input type="hidden" name="_method" value="put" />
|
||||
<label>Name: <input type="text" name="user[name]" value="<%= user.name %>" /></label>
|
||||
<input type="submit" value="Update" />
|
||||
</form>
|
||||
|
||||
<form action='/user/<%= user.id %>/pet' method='post'>
|
||||
<label>Pet: <input type="text" name="pet[name]" placeholder="name" /></label>
|
||||
<input type="submit" value="Add" />
|
||||
</form>
|
||||
8
examples/mvc/controllers/user/views/list.html
Normal file
8
examples/mvc/controllers/user/views/list.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1>Users</h1>
|
||||
<p>Click a user below to view their pets.</p>
|
||||
<ul>
|
||||
<% users.forEach(function(user){ %>
|
||||
<li><a href="/user/<%= user.id %>"><%= user.name %></a></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
21
examples/mvc/controllers/user/views/show.html
Normal file
21
examples/mvc/controllers/user/views/show.html
Normal file
@@ -0,0 +1,21 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1><%= user.name %> <a href="/user/<%= user.id %>/edit">edit</a></h1>
|
||||
|
||||
<% if (hasMessages) { %>
|
||||
<ul id="messages">
|
||||
<% messages.forEach(function(msg){ %>
|
||||
<li><%= msg %></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
<% } %>
|
||||
|
||||
<% if (user.pets.length) { %>
|
||||
<p>View <%= user.name %>s pets:</p>
|
||||
<ul>
|
||||
<% user.pets.forEach(function(pet){ %>
|
||||
<li><a href="/pet/<%= pet.id %>"><%= pet.name %></a></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
<% } else { %>
|
||||
<p>No pets!</p>
|
||||
<% } %>
|
||||
15
examples/mvc/db.js
Normal file
15
examples/mvc/db.js
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
// faux database
|
||||
|
||||
var pets = exports.pets = [];
|
||||
|
||||
pets.push({ name: 'Tobi', id: 0 });
|
||||
pets.push({ name: 'Loki', id: 1 });
|
||||
pets.push({ name: 'Jane', id: 2 });
|
||||
pets.push({ name: 'Raul', id: 3 });
|
||||
|
||||
var users = exports.users = [];
|
||||
|
||||
users.push({ name: 'TJ', pets: [pets[0], pets[1], pets[2]], id: 0 });
|
||||
users.push({ name: 'Guillermo', pets: [pets[3]], id: 1 });
|
||||
users.push({ name: 'Nathan', pets: [], id: 2 });
|
||||
93
examples/mvc/index.js
Normal file
93
examples/mvc/index.js
Normal file
@@ -0,0 +1,93 @@
|
||||
|
||||
var express = require('../..');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// settings
|
||||
|
||||
// map .renderFile to ".html" files
|
||||
app.engine('html', require('ejs').renderFile);
|
||||
|
||||
// make ".html" the default
|
||||
app.set('view engine', 'html');
|
||||
|
||||
// set views for error and 404 pages
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// define a custom res.message() method
|
||||
// which stores messages in the session
|
||||
app.response.message = function(msg){
|
||||
// reference `req.session` via the `this.req` reference
|
||||
var sess = this.req.session;
|
||||
// simply add the msg to an array for later
|
||||
sess.messages = sess.messages || [];
|
||||
sess.messages.push(msg);
|
||||
return this;
|
||||
};
|
||||
|
||||
// log
|
||||
if (!module.parent) app.use(express.logger('dev'));
|
||||
|
||||
// serve static files
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// session support
|
||||
app.use(express.cookieParser('some secret here'));
|
||||
app.use(express.session());
|
||||
|
||||
// parse request bodies (req.body)
|
||||
app.use(express.bodyParser());
|
||||
|
||||
// support _method (PUT in forms etc)
|
||||
app.use(express.methodOverride());
|
||||
|
||||
// expose the "messages" local variable when views are rendered
|
||||
app.use(function(req, res, next){
|
||||
var msgs = req.session.messages || [];
|
||||
|
||||
// expose "messages" local variable
|
||||
res.locals.messages = msgs;
|
||||
|
||||
// expose "hasMessages"
|
||||
res.locals.hasMessages = !! msgs.length;
|
||||
|
||||
/* This is equivalent:
|
||||
res.locals({
|
||||
messages: msgs,
|
||||
hasMessages: !! msgs.length
|
||||
});
|
||||
*/
|
||||
|
||||
// empty or "flush" the messages so they
|
||||
// don't build up
|
||||
req.session.messages = [];
|
||||
next();
|
||||
});
|
||||
|
||||
// load controllers
|
||||
require('./lib/boot')(app, { verbose: !module.parent });
|
||||
|
||||
// assume "not found" in the error msgs
|
||||
// is a 404. this is somewhat silly, but
|
||||
// valid, you can do whatever you like, set
|
||||
// properties, use instanceof etc.
|
||||
app.use(function(err, req, res, next){
|
||||
// treat as 404
|
||||
if (~err.message.indexOf('not found')) return next();
|
||||
|
||||
// log it
|
||||
console.error(err.stack);
|
||||
|
||||
// error page
|
||||
res.status(500).render('5xx');
|
||||
});
|
||||
|
||||
// assume 404 since no middleware responded
|
||||
app.use(function(req, res, next){
|
||||
res.status(404).render('404', { url: req.originalUrl });
|
||||
});
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('\n listening on port 3000\n');
|
||||
}
|
||||
73
examples/mvc/lib/boot.js
Normal file
73
examples/mvc/lib/boot.js
Normal file
@@ -0,0 +1,73 @@
|
||||
|
||||
var express = require('../../..')
|
||||
, fs = require('fs');
|
||||
|
||||
module.exports = function(parent, options){
|
||||
var verbose = options.verbose;
|
||||
fs.readdirSync(__dirname + '/../controllers').forEach(function(name){
|
||||
verbose && console.log('\n %s:', name);
|
||||
var obj = require('./../controllers/' + name)
|
||||
, name = obj.name || name
|
||||
, prefix = obj.prefix || ''
|
||||
, app = express()
|
||||
, method
|
||||
, path;
|
||||
|
||||
// allow specifying the view engine
|
||||
if (obj.engine) app.set('view engine', obj.engine);
|
||||
app.set('views', __dirname + '/../controllers/' + name + '/views');
|
||||
|
||||
// before middleware support
|
||||
if (obj.before) {
|
||||
path = '/' + name + '/:' + name + '_id';
|
||||
app.all(path, obj.before);
|
||||
verbose && console.log(' ALL %s -> before', path);
|
||||
path = '/' + name + '/:' + name + '_id/*';
|
||||
app.all(path, obj.before);
|
||||
verbose && console.log(' ALL %s -> before', path);
|
||||
}
|
||||
|
||||
// generate routes based
|
||||
// on the exported methods
|
||||
for (var key in obj) {
|
||||
// "reserved" exports
|
||||
if (~['name', 'prefix', 'engine', 'before'].indexOf(key)) continue;
|
||||
// route exports
|
||||
switch (key) {
|
||||
case 'show':
|
||||
method = 'get';
|
||||
path = '/' + name + '/:' + name + '_id';
|
||||
break;
|
||||
case 'list':
|
||||
method = 'get';
|
||||
path = '/' + name + 's';
|
||||
break;
|
||||
case 'edit':
|
||||
method = 'get';
|
||||
path = '/' + name + '/:' + name + '_id/edit';
|
||||
break;
|
||||
case 'update':
|
||||
method = 'put';
|
||||
path = '/' + name + '/:' + name + '_id';
|
||||
break;
|
||||
case 'create':
|
||||
method = 'post';
|
||||
path = '/' + name;
|
||||
break;
|
||||
case 'index':
|
||||
method = 'get';
|
||||
path = '/';
|
||||
break;
|
||||
default:
|
||||
throw new Error('unrecognized route: ' + name + '.' + key);
|
||||
}
|
||||
|
||||
path = prefix + path;
|
||||
app[method](path, obj[key]);
|
||||
verbose && console.log(' %s %s -> %s', method.toUpperCase(), path, key);
|
||||
}
|
||||
|
||||
// mount the app
|
||||
parent.use(app);
|
||||
});
|
||||
};
|
||||
14
examples/mvc/public/style.css
Normal file
14
examples/mvc/public/style.css
Normal file
@@ -0,0 +1,14 @@
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 16px "Helvetica Neue", Helvetica, Arial;
|
||||
}
|
||||
a {
|
||||
color: #107aff;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
h1 a {
|
||||
font-size: 16px;
|
||||
}
|
||||
3
examples/mvc/views/404.html
Normal file
3
examples/mvc/views/404.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1>404: Not Found</h1>
|
||||
<p>Sorry we can't find <%= url %></p>
|
||||
3
examples/mvc/views/5xx.html
Normal file
3
examples/mvc/views/5xx.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1>500: Internal Server Error</h1>
|
||||
<p>Looks like something blew up!</p>
|
||||
54
examples/online/index.js
Normal file
54
examples/online/index.js
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
// first:
|
||||
// $ npm install redis online
|
||||
// $ redis-server
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..')
|
||||
, online = require('online')
|
||||
, redis = require('redis')
|
||||
, db = redis.createClient();
|
||||
|
||||
// online
|
||||
|
||||
online = online(db);
|
||||
|
||||
// app
|
||||
|
||||
var app = express();
|
||||
|
||||
// activity tracking, in this case using
|
||||
// the UA string, you would use req.user.id etc
|
||||
|
||||
app.use(function(req, res, next){
|
||||
// fire-and-forget
|
||||
online.add(req.headers['user-agent']);
|
||||
next();
|
||||
});
|
||||
|
||||
/**
|
||||
* List helper.
|
||||
*/
|
||||
|
||||
function list(ids) {
|
||||
return '<ul>' + ids.map(function(id){
|
||||
return '<li>' + id + '</li>';
|
||||
}).join('') + '</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* GET users online.
|
||||
*/
|
||||
|
||||
app.get('/', function(req, res, next){
|
||||
online.last(5, function(err, ids){
|
||||
if (err) return next(err);
|
||||
res.send('<p>Users online: ' + ids.length + '</p>' + list(ids));
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('listening on port 3000');
|
||||
@@ -1,32 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// Set our default template engine to "jade"
|
||||
// which prevents the need for extensions
|
||||
// (although you can still mix and match)
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
// Dummy record
|
||||
var ninja = {
|
||||
name: 'leonardo',
|
||||
summary: { email: 'hunter.loftis+github@gmail.com', master: 'splinter', description: 'peaceful leader' },
|
||||
weapons: ['katana', 'fists', 'shell'],
|
||||
victims: ['shredder', 'brain', 'beebop', 'rocksteady']
|
||||
};
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('ninja', { ninja: ninja });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -1,5 +0,0 @@
|
||||
!!!
|
||||
html
|
||||
head
|
||||
title Partials Example
|
||||
body!= body
|
||||
@@ -1 +0,0 @@
|
||||
li= value
|
||||
@@ -1 +0,0 @@
|
||||
li.weapon= weapon
|
||||
@@ -1,22 +0,0 @@
|
||||
h1= ninja.name
|
||||
|
||||
// file, partial name, and partial object all match ('summary')
|
||||
// the partial filename prefix '_' is completely optional.
|
||||
|
||||
// In this case we need to specify ninja.summary as the object
|
||||
// option, since it is a "plain" object Express cannot otherwise
|
||||
// tell if it is intended to be locals, or THE summary object
|
||||
#summary!= partial('summary', { object: ninja.summary })
|
||||
|
||||
// file, partial name = '_weapon', resolves to 'weapon' object within partial
|
||||
#weapons
|
||||
h2 Weapons
|
||||
// the weapon partial is rendered once per item in
|
||||
// the weapons array or "collection"
|
||||
ul!= partial('weapon', ninja.weapons)
|
||||
|
||||
// partial name 'victim' resolves to 'victim.jade'
|
||||
// or 'victim/index.jade', providing the "victim" local
|
||||
#victims
|
||||
h2 Victims
|
||||
ul!= partial('victim', ninja.victims)
|
||||
@@ -1,4 +0,0 @@
|
||||
h2 Summary
|
||||
p= summary.email
|
||||
p= summary.description
|
||||
p taught by master #{summary.master}
|
||||
@@ -1,5 +0,0 @@
|
||||
// this is insane overkill, I do not recommend
|
||||
// doing tiny partials like this as it gets expensive
|
||||
// with collections, however this illustrates the new
|
||||
// partial lookup mechanism
|
||||
!= partial('../../li', { object: victim, as: 'value' })
|
||||
@@ -1,26 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var vm = require('vm')
|
||||
, fs = require('fs');
|
||||
|
||||
module.exports = function(app, db){
|
||||
var dir = __dirname + '/routes';
|
||||
// grab a list of our route files
|
||||
fs.readdirSync(dir).forEach(function(file){
|
||||
var str = fs.readFileSync(dir + '/' + file, 'utf8');
|
||||
// inject some pseudo globals by evaluating
|
||||
// the file with vm.runInNewContext()
|
||||
// instead of loading it with require(). require's
|
||||
// internals use similar, so dont be afraid of "boot time".
|
||||
var context = { app: app, db: db };
|
||||
// we have to merge the globals for console, process etc
|
||||
for (var key in global) context[key] = global[key];
|
||||
// note that this is essentially no different than ... just using
|
||||
// global variables, though it's only YOUR code that could influence
|
||||
// them, which is a bonus.
|
||||
vm.runInNewContext(str, context, file);
|
||||
});
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, app = express()
|
||||
, db = { users: [] };
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
app.use(express.bodyParser());
|
||||
|
||||
// pretend db is a database, could be
|
||||
// whatever you like
|
||||
require('./boot')(app, db);
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index');
|
||||
});
|
||||
@@ -1,18 +0,0 @@
|
||||
|
||||
app.get('/users', function(req, res){
|
||||
res.render('user/list', { users: db.users });
|
||||
});
|
||||
|
||||
app.get('/user/add', function(req, res){
|
||||
res.render('user/add');
|
||||
});
|
||||
|
||||
app.post('/user', function(req, res){
|
||||
var user = req.body.user;
|
||||
db.users.push(user);
|
||||
res.redirect('/users');
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res){
|
||||
res.render('user');
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
|
||||
extends layout
|
||||
|
||||
block content
|
||||
h2 Route sharing example
|
||||
ul
|
||||
li: a(href='/user/add') Add user
|
||||
li: a(href='/users') User list
|
||||
@@ -1,11 +0,0 @@
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
title Route loading example
|
||||
style
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 14px/1.5 solid helvetica, arial, sans-serif;
|
||||
}
|
||||
body
|
||||
block content
|
||||
@@ -1,9 +0,0 @@
|
||||
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
h2 Add a user
|
||||
form(action='/user', method='post')
|
||||
p: input(type='text', name='user[name]', placeholder='Username')
|
||||
p: input(type='text', name='user[email]', placeholder='Email')
|
||||
p: input(type='submit', value='Add')
|
||||
@@ -1,10 +0,0 @@
|
||||
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
h1= user.name
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
td Email
|
||||
td= user.email
|
||||
@@ -1,10 +0,0 @@
|
||||
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
h1 Users
|
||||
if users.length
|
||||
for user in users
|
||||
include index
|
||||
else
|
||||
p No users, head over to <a href='/user/add'>/user/add</a> to create one.
|
||||
63
examples/route-map/index.js
Normal file
63
examples/route-map/index.js
Normal file
@@ -0,0 +1,63 @@
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, verbose = process.env.NODE_ENV != 'test'
|
||||
, app = module.exports = express();
|
||||
|
||||
app.map = function(a, route){
|
||||
route = route || '';
|
||||
for (var key in a) {
|
||||
switch (typeof a[key]) {
|
||||
// { '/path': { ... }}
|
||||
case 'object':
|
||||
app.map(a[key], route + key);
|
||||
break;
|
||||
// get: function(){ ... }
|
||||
case 'function':
|
||||
if (verbose) console.log('%s %s', key, route);
|
||||
app[key](route, a[key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var users = {
|
||||
list: function(req, res){
|
||||
res.send('user list');
|
||||
},
|
||||
|
||||
get: function(req, res){
|
||||
res.send('user ' + req.params.uid);
|
||||
},
|
||||
|
||||
del: function(req, res){
|
||||
res.send('delete users');
|
||||
}
|
||||
};
|
||||
|
||||
var pets = {
|
||||
list: function(req, res){
|
||||
res.send('user ' + req.params.uid + '\'s pets');
|
||||
},
|
||||
|
||||
del: function(req, res){
|
||||
res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid);
|
||||
}
|
||||
};
|
||||
|
||||
app.map({
|
||||
'/users': {
|
||||
get: users.list,
|
||||
del: users.del,
|
||||
'/:uid': {
|
||||
get: users.get,
|
||||
'/pets': {
|
||||
get: pets.list,
|
||||
'/:pid': {
|
||||
del: pets.del
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
var app = express();
|
||||
|
||||
// Example requests:
|
||||
// curl http://localhost:3000/user/0
|
||||
@@ -3,17 +3,19 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, app = express.createServer()
|
||||
var express = require('../..')
|
||||
, app = express()
|
||||
, site = require('./site')
|
||||
, post = require('./post')
|
||||
, user = require('./user');
|
||||
|
||||
// Config
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('view engine', 'jade');
|
||||
app.set('views', __dirname + '/views');
|
||||
app.use(express.logger('dev'));
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
@@ -35,4 +37,4 @@ app.put('/user/:id/edit', user.update);
|
||||
app.get('/posts', post.list);
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -2,9 +2,9 @@
|
||||
// Fake posts database
|
||||
|
||||
var posts = [
|
||||
{ title: 'Foo', body: 'some foo bar' }
|
||||
, { title: 'Foo bar', body: 'more foo bar' }
|
||||
, { title: 'Foo bar baz', body: 'more foo bar baz' }
|
||||
{ title: 'Foo', body: 'some foo bar' },
|
||||
{ title: 'Foo bar', body: 'more foo bar' },
|
||||
{ title: 'Foo bar baz', body: 'more foo bar baz' }
|
||||
];
|
||||
|
||||
exports.list = function(req, res){
|
||||
|
||||
@@ -11,10 +11,10 @@ a.edit {
|
||||
opacity: .3;
|
||||
}
|
||||
a.edit::before {
|
||||
content: '[ ';
|
||||
content: ' [';
|
||||
}
|
||||
a.edit::after {
|
||||
content: ' ]';
|
||||
content: ']';
|
||||
}
|
||||
dt {
|
||||
font-weight: bold;
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
// Fake user database
|
||||
|
||||
var users = [
|
||||
{ name: 'TJ', email: 'tj@vision-media.ca' }
|
||||
, { name: 'Tobi', email: 'tobi@vision-media.ca' }
|
||||
{ name: 'TJ', email: 'tj@vision-media.ca' },
|
||||
{ name: 'Tobi', email: 'tobi@vision-media.ca' }
|
||||
];
|
||||
|
||||
exports.list = function(req, res){
|
||||
@@ -22,15 +22,15 @@ exports.load = function(req, res, next){
|
||||
|
||||
exports.view = function(req, res){
|
||||
res.render('users/view', {
|
||||
title: 'Viewing user ' + req.user.name
|
||||
, user: req.user
|
||||
title: 'Viewing user ' + req.user.name,
|
||||
user: req.user
|
||||
});
|
||||
};
|
||||
|
||||
exports.edit = function(req, res){
|
||||
res.render('users/edit', {
|
||||
title: 'Editing user ' + req.user.name
|
||||
, user: req.user
|
||||
title: 'Editing user ' + req.user.name,
|
||||
user: req.user
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<ul>
|
||||
<li>Visit the <a href="/users">users</a> page</li>
|
||||
<li>Visit the <a href="/posts">posts</a> page</li>
|
||||
</ul>
|
||||
6
examples/route-separation/views/index.jade
Normal file
6
examples/route-separation/views/index.jade
Normal file
@@ -0,0 +1,6 @@
|
||||
extends layout
|
||||
|
||||
block content
|
||||
ul
|
||||
li Visit the <a href="/users">users</a> page
|
||||
li Visit the <a href="/posts">posts</a> page
|
||||
@@ -1,9 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title><%= title %></title>
|
||||
<link href="/style.css" rel="stylesheet" />
|
||||
</head>
|
||||
<body>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
6
examples/route-separation/views/layout.jade
Normal file
6
examples/route-separation/views/layout.jade
Normal file
@@ -0,0 +1,6 @@
|
||||
html
|
||||
head
|
||||
title= title
|
||||
link(href="/style.css", rel="stylesheet")
|
||||
body
|
||||
block content
|
||||
@@ -1,7 +0,0 @@
|
||||
<h1>Posts</h1>
|
||||
<dl id="posts">
|
||||
<% posts.forEach(function(post){ %>
|
||||
<dt><%= post.title %></dt>
|
||||
<dd><%= post.body %></dd>
|
||||
<% }) %>
|
||||
</dl>
|
||||
8
examples/route-separation/views/posts/index.jade
Normal file
8
examples/route-separation/views/posts/index.jade
Normal file
@@ -0,0 +1,8 @@
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
h1 Posts
|
||||
dl#posts
|
||||
for post in posts
|
||||
dt= post.title
|
||||
dd= post.body
|
||||
@@ -1,9 +0,0 @@
|
||||
<h1>Editing <%= user.name %></h1>
|
||||
<div id="user">
|
||||
<form method="post">
|
||||
<input type="hidden" value="put" name="_method" />
|
||||
<p>Name: <input type="text" value="<%= user.name %>" name="user[name]"/></p>
|
||||
<p>Email: <input type="text" value="<%= user.email %>" name="user[email]"/></p>
|
||||
<p><input type="submit" value="Save" /></p>
|
||||
</form>
|
||||
</div>
|
||||
13
examples/route-separation/views/users/edit.jade
Normal file
13
examples/route-separation/views/users/edit.jade
Normal file
@@ -0,0 +1,13 @@
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
h1 Editing #{user.name}
|
||||
#user
|
||||
form(method="post")
|
||||
input(type="hidden", value="put", name="_method")
|
||||
p Name:
|
||||
input(type="text", value= user.name, name="user[name]")
|
||||
p Email:
|
||||
input(type="text", value= user.email, name="user[email]")
|
||||
p
|
||||
input(type="submit", value="Save")
|
||||
@@ -1,9 +0,0 @@
|
||||
<h1>Users</h1>
|
||||
<ul id="users">
|
||||
<% users.forEach(function(user, id){ %>
|
||||
<li>
|
||||
<a href="/user/<%= id %>"><%= user.name %></a>
|
||||
<a class="edit" href="/user/<%= id %>/edit">edit</a>
|
||||
</li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
9
examples/route-separation/views/users/index.jade
Normal file
9
examples/route-separation/views/users/index.jade
Normal file
@@ -0,0 +1,9 @@
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
h1 Users
|
||||
#users
|
||||
for user, i in users
|
||||
li
|
||||
a(href="/user/#{i}")= user.name
|
||||
a.edit(href="/user/#{i}/edit") edit
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user