mirror of
https://github.com/expressjs/express.git
synced 2026-02-26 18:35:04 +00:00
Compare commits
906 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63ab25579b | ||
|
|
13c07237fe | ||
|
|
f5d485235e | ||
|
|
72d35b6b4a | ||
|
|
c73d7650b5 | ||
|
|
86328767fe | ||
|
|
e497d068a1 | ||
|
|
926a71f5ac | ||
|
|
55f5a2dc8d | ||
|
|
fe435e497e | ||
|
|
fea768dbcc | ||
|
|
2ccb6cf350 | ||
|
|
0b62f74a7f | ||
|
|
45ebb6cdf4 | ||
|
|
96f47432a5 | ||
|
|
41f8435d32 | ||
|
|
b275613d3a | ||
|
|
82689d68cf | ||
|
|
0c567b3282 | ||
|
|
855176b633 | ||
|
|
b95f2ee820 | ||
|
|
ae92db98f3 | ||
|
|
0b25547ca0 | ||
|
|
548f2865e2 | ||
|
|
40f7a8eaa2 | ||
|
|
9434ad39e1 | ||
|
|
7d983c94c7 | ||
|
|
0b1cacff03 | ||
|
|
b809498cf8 | ||
|
|
f6f78e5f02 | ||
|
|
591e89ed18 | ||
|
|
1616079a2d | ||
|
|
4db51fbeba | ||
|
|
ec8daf0e7b | ||
|
|
5312a990b9 | ||
|
|
935f05bc84 | ||
|
|
fc4eb6dae0 | ||
|
|
08939683c7 | ||
|
|
55699bee8f | ||
|
|
dab9222942 | ||
|
|
4070dabe53 | ||
|
|
a365864229 | ||
|
|
ee3f2b073c | ||
|
|
34b6385dc3 | ||
|
|
11529a2ea0 | ||
|
|
b0f8809e3d | ||
|
|
ec1175daa3 | ||
|
|
3a1d9b8289 | ||
|
|
192be8fea3 | ||
|
|
1afcff955b | ||
|
|
224fe05697 | ||
|
|
2dd332b491 | ||
|
|
33e4193f45 | ||
|
|
c163d2f33d | ||
|
|
a715ba6be4 | ||
|
|
ff5e96c88b | ||
|
|
d842647217 | ||
|
|
12626aed35 | ||
|
|
3387916efc | ||
|
|
f76403b717 | ||
|
|
2d6faf6da7 | ||
|
|
a7227a0964 | ||
|
|
6e9cfa2aea | ||
|
|
571ce16405 | ||
|
|
98585d1d0a | ||
|
|
b78bd3d1fd | ||
|
|
4aa2801054 | ||
|
|
4405b849a9 | ||
|
|
5d74a553d6 | ||
|
|
262b60537f | ||
|
|
e77e644224 | ||
|
|
ce89c00cd9 | ||
|
|
4d8093302f | ||
|
|
4370908674 | ||
|
|
f0b679d02d | ||
|
|
d5ad34b0e9 | ||
|
|
c24463d829 | ||
|
|
ebfa00a9c0 | ||
|
|
d23417e6e8 | ||
|
|
91824514ce | ||
|
|
656e214937 | ||
|
|
4b26bbde2d | ||
|
|
7fcc8b190d | ||
|
|
b326ae89df | ||
|
|
869ddd775c | ||
|
|
997fd74e8c | ||
|
|
e3e41a1118 | ||
|
|
6c8bcd5c4e | ||
|
|
95e63ec287 | ||
|
|
ebca5887cc | ||
|
|
8535d3a990 | ||
|
|
13184c4379 | ||
|
|
eaba4eeb70 | ||
|
|
ac56cf4606 | ||
|
|
6dea32cd18 | ||
|
|
5fab60bc6c | ||
|
|
881e1ba660 | ||
|
|
0e488c19df | ||
|
|
2262a18900 | ||
|
|
28c6952d1c | ||
|
|
01e3530a31 | ||
|
|
77c83d0c57 | ||
|
|
b4eaf89186 | ||
|
|
e4debea297 | ||
|
|
1c96e18d20 | ||
|
|
8bb013ec95 | ||
|
|
6c0031bfd8 | ||
|
|
ab6c189504 | ||
|
|
a12ae729bd | ||
|
|
d53a0cd91e | ||
|
|
eabd4564aa | ||
|
|
d40dc651f3 | ||
|
|
68290ee87a | ||
|
|
6614352563 | ||
|
|
0e5f2f84ea | ||
|
|
b1d0c19ca1 | ||
|
|
dfa7ee4732 | ||
|
|
e9539fc780 | ||
|
|
5f7a37ee51 | ||
|
|
ff3a368b2f | ||
|
|
ccc45a74f8 | ||
|
|
cd9d2ec6a9 | ||
|
|
ce7bbae007 | ||
|
|
6a5dd52deb | ||
|
|
6c2f7fb48d | ||
|
|
4dd970578a | ||
|
|
88dfd36eaa | ||
|
|
5759b3e9f5 | ||
|
|
c939a771c0 | ||
|
|
af1043844f | ||
|
|
dd763ec5b8 | ||
|
|
9c2c21aaaf | ||
|
|
366000184f | ||
|
|
4d1ee23f84 | ||
|
|
6f31218ecc | ||
|
|
6cd4859035 | ||
|
|
4f1cd4f73c | ||
|
|
f15bba7309 | ||
|
|
6f0302fb78 | ||
|
|
0e5613363f | ||
|
|
7a7f18c20b | ||
|
|
b766aad112 | ||
|
|
7488e27609 | ||
|
|
bc9d854763 | ||
|
|
2e20a85810 | ||
|
|
a706408208 | ||
|
|
6f91416020 | ||
|
|
2c5ed88c90 | ||
|
|
6d39d0f8a8 | ||
|
|
159ea67713 | ||
|
|
33959ed350 | ||
|
|
be478d348c | ||
|
|
b0e4e641f9 | ||
|
|
94f10c26cb | ||
|
|
efd2dfb8c8 | ||
|
|
3f2454e3df | ||
|
|
61da3c7d0e | ||
|
|
2c140961ab | ||
|
|
6aa4a450ed | ||
|
|
9f292d873e | ||
|
|
ef3e95ca73 | ||
|
|
f45bd632df | ||
|
|
cc18da5cdf | ||
|
|
5603f86edd | ||
|
|
daadf6033b | ||
|
|
590c919204 | ||
|
|
3bcba79e2d | ||
|
|
b8c8ecebb7 | ||
|
|
43e2cd79cb | ||
|
|
653270bb43 | ||
|
|
734bdf5ca1 | ||
|
|
341c1919d9 | ||
|
|
8e46af1b1d | ||
|
|
e4fc09423e | ||
|
|
0300b61fdd | ||
|
|
b09afad7b1 | ||
|
|
0e0b259556 | ||
|
|
bc38d896ea | ||
|
|
b2518fe135 | ||
|
|
63286e1192 | ||
|
|
c00f2f8596 | ||
|
|
91891e3aee | ||
|
|
728917164c | ||
|
|
bf1980f1b4 | ||
|
|
3c1a964362 | ||
|
|
947fb8b274 | ||
|
|
c5193536e5 | ||
|
|
d08fd64190 | ||
|
|
2470ae6c72 | ||
|
|
916a75cf19 | ||
|
|
daacb749c1 | ||
|
|
f29399c4e1 | ||
|
|
f6ac068ab0 | ||
|
|
7eb65eeca2 | ||
|
|
178fe15091 | ||
|
|
381f278d0a | ||
|
|
534fa181c6 | ||
|
|
80847d8c82 | ||
|
|
bb8abf1f90 | ||
|
|
cf41a8f254 | ||
|
|
1716e3b067 | ||
|
|
12f92a50dc | ||
|
|
51d33edb79 | ||
|
|
2a0c35a108 | ||
|
|
d4de82b853 | ||
|
|
e2102263ce | ||
|
|
d2b1a89b4a | ||
|
|
d9937c628a | ||
|
|
276db8c49a | ||
|
|
1768d94a1a | ||
|
|
6bc7574ab5 | ||
|
|
3dca534995 | ||
|
|
4b1b8e420f | ||
|
|
70767b19ac | ||
|
|
7d277c1c15 | ||
|
|
fa1fcd9fec | ||
|
|
2de6514b4b | ||
|
|
d07c06363f | ||
|
|
4e97533fd2 | ||
|
|
d7d6219a1e | ||
|
|
9b18461bbc | ||
|
|
b77aa38c98 | ||
|
|
cbb251377e | ||
|
|
d6ed469de3 | ||
|
|
49284c236b | ||
|
|
be18487f7d | ||
|
|
094ff11949 | ||
|
|
d2d0afff64 | ||
|
|
33bb8fc4b6 | ||
|
|
b97f6eb506 | ||
|
|
621d074bd8 | ||
|
|
d7e7b2e7d7 | ||
|
|
2d6b735b4f | ||
|
|
a3115882d4 | ||
|
|
3d188fe13e | ||
|
|
8327708ec2 | ||
|
|
c8640b3465 | ||
|
|
3ce5f9b493 | ||
|
|
46f0bfc65f | ||
|
|
0b49e7f1fd | ||
|
|
f6f47f428c | ||
|
|
20635d03fc | ||
|
|
4d032cda05 | ||
|
|
4127ba10b0 | ||
|
|
b6ae091bdf | ||
|
|
a206b4e273 | ||
|
|
2b2733c235 | ||
|
|
7fb7bcc0f7 | ||
|
|
0299bee8fa | ||
|
|
6a581c9961 | ||
|
|
4986b1cb4c | ||
|
|
3de4e4276d | ||
|
|
27f195374d | ||
|
|
ff0de5eb27 | ||
|
|
f4ddef1570 | ||
|
|
9eafaa23d8 | ||
|
|
0b12cc0cac | ||
|
|
fdd0ccabe8 | ||
|
|
8c36eab679 | ||
|
|
5c145b5490 | ||
|
|
d7bef52591 | ||
|
|
1576a95e87 | ||
|
|
7f92fe66e0 | ||
|
|
0cf02d4667 | ||
|
|
ef52b80d75 | ||
|
|
1ca01c0c47 | ||
|
|
fbceae2716 | ||
|
|
ad3ca25c58 | ||
|
|
666ffc62d8 | ||
|
|
6680132392 | ||
|
|
f13f4652da | ||
|
|
059c068c7b | ||
|
|
49947f1476 | ||
|
|
0dddd772c0 | ||
|
|
0f87c6f392 | ||
|
|
1643ae442c | ||
|
|
2594f3103b | ||
|
|
8473b3c338 | ||
|
|
59cb99e9be | ||
|
|
7119f2b16d | ||
|
|
a57efea173 | ||
|
|
4a4ca7347a | ||
|
|
570f60d36e | ||
|
|
d13e613584 | ||
|
|
9204e1f42a | ||
|
|
22ca953e96 | ||
|
|
7989c883fe | ||
|
|
e05a52078a | ||
|
|
ddac571fdf | ||
|
|
982d24b475 | ||
|
|
552b441f8a | ||
|
|
e8f8ea7e05 | ||
|
|
4f5b27dd81 | ||
|
|
cca88a7c47 | ||
|
|
ea427c1bb4 | ||
|
|
0bd6c311cf | ||
|
|
7f606ebf29 | ||
|
|
a3b5adcf4a | ||
|
|
1150ca7264 | ||
|
|
4aea02310a | ||
|
|
17cea29013 | ||
|
|
8449f23f0d | ||
|
|
2cb029f896 | ||
|
|
7e32fa1be6 | ||
|
|
1168d0bb8b | ||
|
|
7d0f1c3db9 | ||
|
|
19abf7684b | ||
|
|
c652cf7eed | ||
|
|
19fd6f85b0 | ||
|
|
b6c5b0511f | ||
|
|
0e42a37edd | ||
|
|
b24ed15878 | ||
|
|
12e070e39a | ||
|
|
b886eb52cf | ||
|
|
d8237b976b | ||
|
|
15590d75b2 | ||
|
|
e8b471ff4f | ||
|
|
767db01b79 | ||
|
|
df413a41f3 | ||
|
|
52775a52ad | ||
|
|
112bc92d78 | ||
|
|
d8df26680f | ||
|
|
1854a5d35f | ||
|
|
54d3ffa9a0 | ||
|
|
0ee4dd82b5 | ||
|
|
454c4b2350 | ||
|
|
696e150f0a | ||
|
|
819265c7ae | ||
|
|
baf8b14a71 | ||
|
|
06e7685d65 | ||
|
|
8858f20d93 | ||
|
|
65f67e2ec0 | ||
|
|
8e63521f68 | ||
|
|
9f4968aaa3 | ||
|
|
afea3c0ae8 | ||
|
|
edaabe66cf | ||
|
|
f724730e1a | ||
|
|
e49d0dc9e3 | ||
|
|
e7a3fbaf48 | ||
|
|
1a9a837c45 | ||
|
|
ab8d116f42 | ||
|
|
d0b6b3dfcf | ||
|
|
f34944c539 | ||
|
|
11c74d72eb | ||
|
|
035685918c | ||
|
|
88cffadcaa | ||
|
|
3b7ca43170 | ||
|
|
7cd86a01da | ||
|
|
dc054d190a | ||
|
|
9019424725 | ||
|
|
928952e7f0 | ||
|
|
a28b7a85cf | ||
|
|
3fc8dc54ee | ||
|
|
0d77305a1a | ||
|
|
323c185079 | ||
|
|
1d0da9036b | ||
|
|
683ba1cd75 | ||
|
|
e4ff5281c9 | ||
|
|
7414a1f463 | ||
|
|
fd3b40533b | ||
|
|
21bb2ef30e | ||
|
|
da4c639954 | ||
|
|
d046208ca2 | ||
|
|
04f383087f | ||
|
|
fd86ab8da2 | ||
|
|
e29fa25bb4 | ||
|
|
b43205ca98 | ||
|
|
112dbb2ab4 | ||
|
|
3e32721e24 | ||
|
|
8ba3f39b33 | ||
|
|
82bdbad5e0 | ||
|
|
d29cf4d5e3 | ||
|
|
1623936a25 | ||
|
|
fa6a40526a | ||
|
|
c6e6203020 | ||
|
|
997a558a73 | ||
|
|
a01326adac | ||
|
|
76e8bfa1dc | ||
|
|
8dc67af606 | ||
|
|
996d319263 | ||
|
|
1c3bd36be6 | ||
|
|
4ea6f21b02 | ||
|
|
916c53737d | ||
|
|
ec5b9f5c61 | ||
|
|
b2382a7336 | ||
|
|
f684a64df7 | ||
|
|
5d03d0eac8 | ||
|
|
544c6665f5 | ||
|
|
cf8005e63f | ||
|
|
25ef8425d2 | ||
|
|
577cc1d1a0 | ||
|
|
3c87a6aede | ||
|
|
7c1f90bf16 | ||
|
|
7bcf5f5085 | ||
|
|
4fe1073f10 | ||
|
|
968b00c3d7 | ||
|
|
ef497fdae4 | ||
|
|
bcdeee2df5 | ||
|
|
23a49ff61e | ||
|
|
b4b2efee0f | ||
|
|
92c45199bd | ||
|
|
bd6908516d | ||
|
|
e6eeec3f03 | ||
|
|
269dc5323f | ||
|
|
efbc3f95ee | ||
|
|
99e839a274 | ||
|
|
32dbda1460 | ||
|
|
bcee730354 | ||
|
|
abe0ffa311 | ||
|
|
b601d64203 | ||
|
|
f381f2d9b6 | ||
|
|
12507cfcd0 | ||
|
|
185e327e29 | ||
|
|
c468f5ff20 | ||
|
|
ce3d1fe07e | ||
|
|
3136e98dce | ||
|
|
d1b1dfd472 | ||
|
|
ca306eace1 | ||
|
|
fd35351594 | ||
|
|
8a15f83d72 | ||
|
|
d91bf81c31 | ||
|
|
fd27f1f4e1 | ||
|
|
f0ad557987 | ||
|
|
9bb47fba30 | ||
|
|
78d489d730 | ||
|
|
8ffb9f9477 | ||
|
|
9cb147370e | ||
|
|
81036aa639 | ||
|
|
e7caaf6757 | ||
|
|
a525252690 | ||
|
|
0ebfc6b7bf | ||
|
|
381b3b0f77 | ||
|
|
ba8a4c5a8d | ||
|
|
2cccbc186e | ||
|
|
83bbf0902d | ||
|
|
10618ced22 | ||
|
|
7f26cfca91 | ||
|
|
b89a597029 | ||
|
|
746044b6c2 | ||
|
|
bdfd288eec | ||
|
|
7e01531e50 | ||
|
|
3ffceff3ed | ||
|
|
75422c16bf | ||
|
|
e66667e465 | ||
|
|
7d6208e0af | ||
|
|
f498b660da | ||
|
|
e606d99dc8 | ||
|
|
6aba1b4c49 | ||
|
|
6ee9433f29 | ||
|
|
ffe663aedf | ||
|
|
2a105df9f2 | ||
|
|
9c731f1883 | ||
|
|
5a4e9125de | ||
|
|
9db1367c2d | ||
|
|
8258ce14f4 | ||
|
|
ac573cf830 | ||
|
|
e799c0fb7b | ||
|
|
73c5533e66 | ||
|
|
3b1f747f96 | ||
|
|
9e9827d236 | ||
|
|
a76d508424 | ||
|
|
c361a06bd4 | ||
|
|
3428543bb8 | ||
|
|
9cdbc80522 | ||
|
|
6775658ed5 | ||
|
|
7df7f7a575 | ||
|
|
7daae1912b | ||
|
|
3205f68510 | ||
|
|
898dcfac8b | ||
|
|
b1efa19f97 | ||
|
|
b45fd70f99 | ||
|
|
7f6c7a19c6 | ||
|
|
142462d539 | ||
|
|
f881784e9b | ||
|
|
5af625903f | ||
|
|
dc94f305cc | ||
|
|
8060a49c6c | ||
|
|
4d3e0d88a2 | ||
|
|
253ce4837a | ||
|
|
ad05eb8222 | ||
|
|
21393c244c | ||
|
|
4279e6ef45 | ||
|
|
3db6dd752f | ||
|
|
fcbe68eeb5 | ||
|
|
5019f38e29 | ||
|
|
9bf1247716 | ||
|
|
2fd31f6ea6 | ||
|
|
9cf7bba8f0 | ||
|
|
2e257d1cf7 | ||
|
|
980a15d847 | ||
|
|
56831d7799 | ||
|
|
6d65ae5ba6 | ||
|
|
c919b4a573 | ||
|
|
fe6f392c2d | ||
|
|
bffb71d4c8 | ||
|
|
3b34a537ee | ||
|
|
ad79ce9c4b | ||
|
|
721f6388c3 | ||
|
|
402ec83157 | ||
|
|
298ac11018 | ||
|
|
bb6e207336 | ||
|
|
f433b7c7cf | ||
|
|
a94278abd1 | ||
|
|
f9ec70edd0 | ||
|
|
9e5a758e7c | ||
|
|
492e933796 | ||
|
|
8ccd9d0eb5 | ||
|
|
16fdc11ccb | ||
|
|
a7cd5a2553 | ||
|
|
9e6b881f85 | ||
|
|
95fa49147b | ||
|
|
f665c57c5c | ||
|
|
9024d24e81 | ||
|
|
f92a7ad0a3 | ||
|
|
db4448dda8 | ||
|
|
0dc5836d5e | ||
|
|
8751d7ecf8 | ||
|
|
c21226aa7c | ||
|
|
3e358458f4 | ||
|
|
766b3aecf7 | ||
|
|
8ab96ab80d | ||
|
|
1f2e00ef8d | ||
|
|
b49453cf0d | ||
|
|
faffcb889c | ||
|
|
311e83e591 | ||
|
|
3c0ec59432 | ||
|
|
d4a2843500 | ||
|
|
fb2d918056 | ||
|
|
e7ad49bbbe | ||
|
|
ad9a414fae | ||
|
|
c18c2a8e68 | ||
|
|
1d54868c12 | ||
|
|
dfefea5e9d | ||
|
|
3fbab91231 | ||
|
|
f7e73e2da0 | ||
|
|
867728b5ab | ||
|
|
87e02c30e7 | ||
|
|
c3470c9c96 | ||
|
|
7f049164b7 | ||
|
|
4e12a72873 | ||
|
|
91e0c27252 | ||
|
|
db4a061ed6 | ||
|
|
f6bbeafd26 | ||
|
|
f14e39d451 | ||
|
|
084f5d891b | ||
|
|
b0f72e13d9 | ||
|
|
8d7d80ef9d | ||
|
|
cf5de082b5 | ||
|
|
1944451082 | ||
|
|
602e5a8200 | ||
|
|
83b8b7acb7 | ||
|
|
e660f19507 | ||
|
|
ff412b927d | ||
|
|
392ef1eb06 | ||
|
|
dcecdc9be6 | ||
|
|
ed69b68892 | ||
|
|
42b982e13c | ||
|
|
e7e2592357 | ||
|
|
739586f96a | ||
|
|
4c0f1f53d3 | ||
|
|
359f12791a | ||
|
|
9354ab62dd | ||
|
|
23ff74bb3f | ||
|
|
98d17e2293 | ||
|
|
ababa6ae5b | ||
|
|
097cd0c242 | ||
|
|
b91cd66fc5 | ||
|
|
787d630157 | ||
|
|
1f938c560a | ||
|
|
a96924a555 | ||
|
|
33dc6629ff | ||
|
|
1b3fb0af8c | ||
|
|
12da523ff7 | ||
|
|
0f49d80623 | ||
|
|
1717516a78 | ||
|
|
328c6d3060 | ||
|
|
566720be15 | ||
|
|
65f13c3cc6 | ||
|
|
31b2e2d7b4 | ||
|
|
8fe8d74056 | ||
|
|
fcc4742056 | ||
|
|
d98e2e7498 | ||
|
|
d37ffa1149 | ||
|
|
cf709f3021 | ||
|
|
7515ee6a78 | ||
|
|
b8d6d258b0 | ||
|
|
35c50601bd | ||
|
|
f7be983a77 | ||
|
|
bc9bcb0317 | ||
|
|
a2553126dd | ||
|
|
0639c45acd | ||
|
|
e4302b2120 | ||
|
|
3d6b4ba013 | ||
|
|
920f46ad65 | ||
|
|
35a66d8a14 | ||
|
|
4e1e252e17 | ||
|
|
0461e55380 | ||
|
|
8dc4ff26f9 | ||
|
|
6f9e927633 | ||
|
|
40a43eb753 | ||
|
|
a8bb4bab2b | ||
|
|
417999884b | ||
|
|
17a739e35f | ||
|
|
555ffe37b2 | ||
|
|
165578a1da | ||
|
|
0bbbc84959 | ||
|
|
92d37671c5 | ||
|
|
2901bd6916 | ||
|
|
66b38b58bc | ||
|
|
dc31ea34b8 | ||
|
|
d58ca520c8 | ||
|
|
4b646c2f8d | ||
|
|
35757ed1f5 | ||
|
|
fed0d5df5c | ||
|
|
c99fa6a192 | ||
|
|
0d468cbe6c | ||
|
|
f92ba6d0cf | ||
|
|
0408e3727e | ||
|
|
be997fd654 | ||
|
|
5c3852b91c | ||
|
|
3c7310ebcb | ||
|
|
61f2929a35 | ||
|
|
c054581370 | ||
|
|
dbea8312bb | ||
|
|
1536a2196e | ||
|
|
374e6c3789 | ||
|
|
203fb05d3e | ||
|
|
ce8555c690 | ||
|
|
9863fa0903 | ||
|
|
2fd3e72a19 | ||
|
|
8ccceacf91 | ||
|
|
d5815922ca | ||
|
|
972c01afc9 | ||
|
|
8894e30869 | ||
|
|
5d8dba5fe0 | ||
|
|
90fbc1a33e | ||
|
|
55dea47b94 | ||
|
|
c7791a207b | ||
|
|
30d18888b9 | ||
|
|
6ac6305b53 | ||
|
|
29e8ccef4e | ||
|
|
ce17efd95b | ||
|
|
5480cb9571 | ||
|
|
b38ffd7376 | ||
|
|
8d8f44f352 | ||
|
|
af5f21b2e2 | ||
|
|
4ac7474e4e | ||
|
|
bdb3bb98f6 | ||
|
|
896609c859 | ||
|
|
50158b851c | ||
|
|
56b672e657 | ||
|
|
b9e9576083 | ||
|
|
79cc5a4d27 | ||
|
|
5f1d57704e | ||
|
|
19983272f3 | ||
|
|
e1ab302234 | ||
|
|
6b1c443212 | ||
|
|
b79a271553 | ||
|
|
260141ee08 | ||
|
|
fb1232043d | ||
|
|
2377fc8bcf | ||
|
|
c610902b67 | ||
|
|
a802405e19 | ||
|
|
0dbaacfe12 | ||
|
|
b8dd60dec7 | ||
|
|
147c2507c3 | ||
|
|
d72f27909f | ||
|
|
1ee5329b1c | ||
|
|
ba0b046a95 | ||
|
|
412eb2a9ce | ||
|
|
dd8e279cac | ||
|
|
642432cb5e | ||
|
|
001c9380be | ||
|
|
2e830fff99 | ||
|
|
06dcb22ae2 | ||
|
|
0120874b8e | ||
|
|
13475977af | ||
|
|
df50669092 | ||
|
|
abd6b7c5c3 | ||
|
|
e36746363a | ||
|
|
1eba854f23 | ||
|
|
6b19e3dc0a | ||
|
|
cb1fbce46b | ||
|
|
cc38cccae1 | ||
|
|
efbe1779e3 | ||
|
|
8e3d0a6569 | ||
|
|
8bd5d54b0e | ||
|
|
4867cf1e7b | ||
|
|
3ea3250dbe | ||
|
|
3bbcbfdcf9 | ||
|
|
c7e84d8044 | ||
|
|
9ae1d0d22d | ||
|
|
51e80ffd48 | ||
|
|
be52dbbaa1 | ||
|
|
76147c78a1 | ||
|
|
59da745d6c | ||
|
|
68996d7561 | ||
|
|
5e12bab5cc | ||
|
|
7693aa5464 | ||
|
|
110f471efa | ||
|
|
2064f412cb | ||
|
|
3228fd3cbc | ||
|
|
bad55f7977 | ||
|
|
3cf7b2e39e | ||
|
|
b443da410a | ||
|
|
87912103c9 | ||
|
|
4ce1ee458e | ||
|
|
b0351a08de | ||
|
|
3321055025 | ||
|
|
5572897998 | ||
|
|
3112f92d08 | ||
|
|
74f55a863a | ||
|
|
9ea18e10c9 | ||
|
|
d84457b9c2 | ||
|
|
643397ed21 | ||
|
|
85bf9ab76a | ||
|
|
6ec1904aac | ||
|
|
f1315b9efa | ||
|
|
a0e6bb5cb2 | ||
|
|
eaf63d94f0 | ||
|
|
45500fba74 | ||
|
|
8472effab3 | ||
|
|
d368aed150 | ||
|
|
e3b60e80c0 | ||
|
|
9df10674f0 | ||
|
|
e3617fb8ab | ||
|
|
0fbfce58c6 | ||
|
|
f8b954bcd9 | ||
|
|
caa25b506d | ||
|
|
6911815171 | ||
|
|
0719e5f402 | ||
|
|
07b731add0 | ||
|
|
d42d8f5b07 | ||
|
|
143e72dd85 | ||
|
|
6835289564 | ||
|
|
1396e0855d | ||
|
|
6a7363e4ae | ||
|
|
9bc63d92a0 | ||
|
|
6b05f60bad | ||
|
|
25e6629bcc | ||
|
|
0796c1d2d2 | ||
|
|
aac1d52c4f | ||
|
|
f41d09a3cf | ||
|
|
8aff64f89a | ||
|
|
4bf9cfd477 | ||
|
|
08cbc442f5 | ||
|
|
a02dd201e6 | ||
|
|
a5f7dcee04 | ||
|
|
0ddd761904 | ||
|
|
991c2a9d05 | ||
|
|
4983c38298 | ||
|
|
337ab24899 | ||
|
|
63c6a9c5ad | ||
|
|
718e68ffae | ||
|
|
f56a5f01c4 | ||
|
|
b77ffe0228 | ||
|
|
fd6439bb36 | ||
|
|
121f8d02f3 | ||
|
|
ff23423d34 | ||
|
|
1d97599f8b | ||
|
|
e465624fd0 | ||
|
|
5ddbb6965f | ||
|
|
a3b5f6d07f | ||
|
|
ac2cbef8be | ||
|
|
dff22e9d09 | ||
|
|
7282b50ad0 | ||
|
|
8c059469fd | ||
|
|
8c3f153dd4 | ||
|
|
185b526e60 | ||
|
|
38996b30b1 | ||
|
|
827dfed7c2 | ||
|
|
28af21baeb | ||
|
|
951c70496b | ||
|
|
a36eeb96f3 | ||
|
|
7018d3d0e6 | ||
|
|
3f14b4de1f | ||
|
|
26c0be4c4e | ||
|
|
cec0c06a70 | ||
|
|
476f8deb07 | ||
|
|
dc5932d177 | ||
|
|
cfd93b7529 | ||
|
|
8b2208f394 | ||
|
|
00a3b01f39 | ||
|
|
3baca251f0 | ||
|
|
72daae1d92 | ||
|
|
fcbe53ddb5 | ||
|
|
e9851672eb | ||
|
|
9a45f7bd3d | ||
|
|
85834fd146 | ||
|
|
a0c1ac7b45 | ||
|
|
7b0dca0f9c | ||
|
|
34c83d7d29 | ||
|
|
7c6882234e | ||
|
|
2e68ddbae9 | ||
|
|
7724fc6af7 | ||
|
|
2939075f03 | ||
|
|
606f68de02 | ||
|
|
c6c71abf4d | ||
|
|
863160ae49 | ||
|
|
edd39fb194 | ||
|
|
a71d264d45 | ||
|
|
8a7a695836 | ||
|
|
de54af4061 | ||
|
|
2f2a652bc9 | ||
|
|
1e638663de | ||
|
|
1684a8792a | ||
|
|
f47c0d9774 | ||
|
|
89e7264e53 | ||
|
|
cada9f61c8 | ||
|
|
373fa55981 | ||
|
|
2bc703cfc2 | ||
|
|
c9865b821d | ||
|
|
9c0de23645 | ||
|
|
b7a38af41d | ||
|
|
661914781e | ||
|
|
6e3f3887e9 | ||
|
|
a66d6bb034 | ||
|
|
2e197e2b98 | ||
|
|
55d1a4f964 | ||
|
|
82a7d7a977 | ||
|
|
dae54b456f | ||
|
|
1b7a044f33 | ||
|
|
18264403b1 | ||
|
|
04d43b7039 | ||
|
|
2dfecfb661 | ||
|
|
250f1f5f6e | ||
|
|
3ac718763f | ||
|
|
05e1555c0d | ||
|
|
855d1e2bf5 | ||
|
|
f0bfb3b2b2 | ||
|
|
4b4db0f7fb | ||
|
|
9bed2b80ee | ||
|
|
bb157c0cbf | ||
|
|
1ef05d4a28 | ||
|
|
0b88208022 | ||
|
|
c9d9ed3493 | ||
|
|
bd8b9f5781 | ||
|
|
9cbcf23df0 | ||
|
|
36e42db05b | ||
|
|
2bf6a1d813 | ||
|
|
e8373d3564 | ||
|
|
e218377a3d | ||
|
|
7d1aed4955 | ||
|
|
b4acbcf1fe | ||
|
|
50cb62c5d2 | ||
|
|
4cf868bd74 | ||
|
|
baa5a7c3e9 | ||
|
|
ee228f7aea | ||
|
|
d5b11c7d1b | ||
|
|
ed7db34bab | ||
|
|
57e45e3af8 | ||
|
|
1dc46478cb | ||
|
|
113ed0927d | ||
|
|
ab8be2d741 | ||
|
|
3b53b11fcd | ||
|
|
9fb661559b | ||
|
|
04882cf72c | ||
|
|
288176bbc9 | ||
|
|
5638a4fc62 | ||
|
|
3b4ce91fa3 | ||
|
|
1c87e5e9a8 | ||
|
|
a887e6a881 | ||
|
|
69290cad6f | ||
|
|
b66c7da05f | ||
|
|
92ddf77453 | ||
|
|
8e2f538983 | ||
|
|
2817d8caf2 | ||
|
|
b7f08fb159 | ||
|
|
0c2768f5bd | ||
|
|
09bede1a92 | ||
|
|
7059d3b71e | ||
|
|
e5de08faa1 | ||
|
|
e43ff076fd | ||
|
|
3ea7381dea | ||
|
|
f1c46f51e5 | ||
|
|
297fb4e0b0 | ||
|
|
9e406dfee2 | ||
|
|
30b7aa8a17 | ||
|
|
c1d16e0016 | ||
|
|
929ffb8d77 | ||
|
|
197a2e3b54 | ||
|
|
6cf6c8b918 | ||
|
|
058d7ec2ea | ||
|
|
752b5f705e | ||
|
|
e7fa579637 | ||
|
|
97781d4112 | ||
|
|
3ddd8e66a7 | ||
|
|
8a1e865e37 | ||
|
|
e850cb3ea3 | ||
|
|
13d3efe8df | ||
|
|
d6ecf785a2 | ||
|
|
19cb39869f | ||
|
|
a38bdf6758 | ||
|
|
bdbdab7fcc | ||
|
|
5aa9670120 | ||
|
|
8ad8cb93cc | ||
|
|
610e172fcf | ||
|
|
6942070a21 | ||
|
|
e283200511 | ||
|
|
54a192a5c5 | ||
|
|
c3bd65eda2 | ||
|
|
7c2ed1d2d6 | ||
|
|
3de81e0147 | ||
|
|
8fe1e2a5b4 | ||
|
|
909dbb81d5 | ||
|
|
7a31a1d311 |
41
.gitignore
vendored
41
.gitignore
vendored
@@ -1,19 +1,26 @@
|
||||
coverage.html
|
||||
.DS_Store
|
||||
lib-cov
|
||||
*.seed
|
||||
# OS X
|
||||
.DS_Store*
|
||||
Icon?
|
||||
._*
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Linux
|
||||
.directory
|
||||
*~
|
||||
|
||||
|
||||
# npm
|
||||
node_modules
|
||||
*.log
|
||||
*.csv
|
||||
*.dat
|
||||
*.out
|
||||
*.pid
|
||||
*.swp
|
||||
*.swo
|
||||
*.gz
|
||||
|
||||
|
||||
# Coveralls
|
||||
coverage
|
||||
|
||||
# Benchmarking
|
||||
benchmarks/graphs
|
||||
testing
|
||||
node_modules/
|
||||
testing
|
||||
.coverage_data
|
||||
cover_html
|
||||
test.js
|
||||
.idea
|
||||
|
||||
0
.gitmodules
vendored
0
.gitmodules
vendored
@@ -1,9 +0,0 @@
|
||||
.git*
|
||||
docs/
|
||||
examples/
|
||||
support/
|
||||
test/
|
||||
testing.js
|
||||
.DS_Store
|
||||
coverage.html
|
||||
lib-cov
|
||||
@@ -1,4 +1,10 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.8"
|
||||
- "0.10"
|
||||
- "0.11"
|
||||
matrix:
|
||||
allow_failures:
|
||||
- node_js: "0.11"
|
||||
fast_finish: true
|
||||
script: "npm run-script test-travis"
|
||||
after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls"
|
||||
|
||||
25
Contributing.md
Normal file
25
Contributing.md
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
## Website Issues
|
||||
|
||||
Issues for the expressjs.com website go here https://github.com/strongloop/expressjs.com
|
||||
|
||||
## PRs and Code contributions
|
||||
|
||||
* Tests must pass.
|
||||
* Follow existing coding style.
|
||||
* If you fix a bug, add a test.
|
||||
|
||||
|
||||
## Issues which are questions
|
||||
|
||||
We will typically close any vague issues or questions that are specific to some app you are writing. Please double check the docs and other references before being trigger happy with posting a question issue.
|
||||
|
||||
Things that will help get your question issue looked at:
|
||||
|
||||
* Full and runnable JS code.
|
||||
* Clear description of the problem or unexpected behavior.
|
||||
* Clear description of the expected result.
|
||||
* Steps you have taken to debug it yourself.
|
||||
|
||||
If you post a question and do not outline the above items or make it easy for us to understand and reproduce your issue, it will be closed.
|
||||
|
||||
1504
History.md
1504
History.md
File diff suppressed because it is too large
Load Diff
4
LICENSE
4
LICENSE
@@ -1,6 +1,6 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2009-2011 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2009-2014 TJ Holowaychuk <tj@vision-media.ca>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
33
Makefile
33
Makefile
@@ -1,33 +0,0 @@
|
||||
|
||||
MOCHA_OPTS= --check-leaks
|
||||
REPORTER = dot
|
||||
|
||||
check: test
|
||||
|
||||
test: test-unit test-acceptance
|
||||
|
||||
test-unit:
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--reporter $(REPORTER) \
|
||||
$(MOCHA_OPTS)
|
||||
|
||||
test-acceptance:
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--reporter $(REPORTER) \
|
||||
--bail \
|
||||
test/acceptance/*.js
|
||||
|
||||
test-cov: lib-cov
|
||||
@EXPRESS_COV=1 $(MAKE) test REPORTER=html-cov > coverage.html
|
||||
|
||||
lib-cov:
|
||||
@jscoverage lib lib-cov
|
||||
|
||||
benchmark:
|
||||
@./support/bench
|
||||
|
||||
clean:
|
||||
rm -f coverage.html
|
||||
rm -fr lib-cov
|
||||
|
||||
.PHONY: test test-unit test-acceptance benchmark clean
|
||||
248
Readme.md
248
Readme.md
@@ -1,179 +1,135 @@
|
||||

|
||||
[](http://expressjs.com/)
|
||||
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org). [](http://travis-ci.org/visionmedia/express) [](https://gemnasium.com/visionmedia/express)
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
|
||||
|
||||
[![NPM Version][npm-image]][npm-url]
|
||||
[![NPM Downloads][downloads-image]][downloads-url]
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
|
||||
```js
|
||||
var express = require('express');
|
||||
var app = express();
|
||||
var express = require('express')
|
||||
var app = express()
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
});
|
||||
app.get('/', function (req, res) {
|
||||
res.send('Hello World')
|
||||
})
|
||||
|
||||
app.listen(3000);
|
||||
app.listen(3000)
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
$ npm install -g express
|
||||
|
||||
## Quick Start
|
||||
|
||||
The quickest way to get started with express is to utilize the executable `express(1)` to generate an application as shown below:
|
||||
|
||||
Create the app:
|
||||
|
||||
$ npm install -g express
|
||||
$ express /tmp/foo && cd /tmp/foo
|
||||
|
||||
Install dependencies:
|
||||
|
||||
$ npm install
|
||||
|
||||
Start the server:
|
||||
|
||||
$ node app
|
||||
```bash
|
||||
$ npm install express
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
* Built on [Connect](http://github.com/senchalabs/connect)
|
||||
* Robust routing
|
||||
* Focus on high performance
|
||||
* Super-high test coverage
|
||||
* HTTP helpers (redirection, caching, etc)
|
||||
* View system supporting 14+ template engines
|
||||
* Content negotiation
|
||||
* Focus on high performance
|
||||
* Environment based configuration
|
||||
* Executable for generating applications quickly
|
||||
* High test coverage
|
||||
|
||||
## Docs & Community
|
||||
|
||||
* [Website and Documentation](http://expressjs.com/) - [[website repo](https://github.com/strongloop/expressjs.com)]
|
||||
* [#express](https://webchat.freenode.net/?channels=express) on freenode IRC
|
||||
* [Github Organization](https://github.com/expressjs) for Official Middleware & Modules
|
||||
* Visit the [Wiki](https://github.com/strongloop/express/wiki)
|
||||
* [Google Group](https://groups.google.com/group/express-js) for discussion
|
||||
* [Русскоязычная документация](http://jsman.ru/express/)
|
||||
* [한국어 문서](http://expressjs.kr) - [[website repo](https://github.com/Hanul/expressjs.kr)]
|
||||
|
||||
**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/strongloop/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/strongloop/express/wiki/New-features-in-4.x).
|
||||
|
||||
## Quick Start
|
||||
|
||||
The quickest way to get started with express is to utilize the executable [`express(1)`](https://github.com/expressjs/generator) to generate an application as shown below:
|
||||
|
||||
Install the executable. The executable's major version will match Express's:
|
||||
|
||||
```bash
|
||||
$ npm install -g express-generator@4
|
||||
```
|
||||
|
||||
Create the app:
|
||||
|
||||
```bash
|
||||
$ express /tmp/foo && cd /tmp/foo
|
||||
```
|
||||
|
||||
Install dependencies:
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
```
|
||||
|
||||
Start the server:
|
||||
|
||||
```bash
|
||||
$ npm start
|
||||
```
|
||||
|
||||
## Philosophy
|
||||
|
||||
The Express philosophy is to provide small, robust tooling for HTTP servers. Making
|
||||
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
|
||||
14 template engines via [Consolidate.js](http://github.com/visionmedia/consolidate.js)
|
||||
Express does not force you to use any specific ORM or template engine. With support for over
|
||||
14 template engines via [Consolidate.js](https://github.com/tj/consolidate.js),
|
||||
you can quickly craft your perfect framework.
|
||||
|
||||
## More Information
|
||||
## Examples
|
||||
|
||||
* Join #express on freenode
|
||||
* [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
|
||||
* Visit the [Wiki](http://github.com/visionmedia/express/wiki)
|
||||
* [Русскоязычная документация](http://jsman.ru/express/)
|
||||
* Run express examples [online](https://runnable.com/express)
|
||||
|
||||
## Viewing Examples
|
||||
|
||||
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
|
||||
|
||||
then run whichever tests you want:
|
||||
|
||||
$ node examples/content-negotiation
|
||||
|
||||
## Running Tests
|
||||
|
||||
To run the test suite first invoke the following command within the repo, installing the development dependencies:
|
||||
|
||||
$ npm install
|
||||
|
||||
then run the tests:
|
||||
|
||||
$ make test
|
||||
|
||||
## Contributors
|
||||
To view the examples, clone the Express repo and install the dependancies:
|
||||
|
||||
```bash
|
||||
$ git clone git://github.com/strongloop/express.git --depth 1
|
||||
$ cd express
|
||||
$ npm install
|
||||
```
|
||||
project: express
|
||||
commits: 3559
|
||||
active : 468 days
|
||||
files : 237
|
||||
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%
|
||||
6 Guillermo Rauch 0.2%
|
||||
3 Dav Glass 0.1%
|
||||
3 Nick Poulden 0.1%
|
||||
2 Randy Merrill 0.1%
|
||||
2 Benny Wong 0.1%
|
||||
2 Hunter Loftis 0.1%
|
||||
2 Jake Gordon 0.1%
|
||||
2 Brian McKinney 0.1%
|
||||
2 Roman Shtylman 0.1%
|
||||
2 Ben Weaver 0.1%
|
||||
2 Dave Hoover 0.1%
|
||||
2 Eivind Fjeldstad 0.1%
|
||||
2 Daniel Shaw 0.1%
|
||||
1 Matt Colyer 0.0%
|
||||
1 Pau Ramon 0.0%
|
||||
1 Pero Pejovic 0.0%
|
||||
1 Peter Rekdal Sunde 0.0%
|
||||
1 Raynos 0.0%
|
||||
1 Teng Siong Ong 0.0%
|
||||
1 Viktor Kelemen 0.0%
|
||||
1 ctide 0.0%
|
||||
1 8bitDesigner 0.0%
|
||||
1 isaacs 0.0%
|
||||
1 mgutz 0.0%
|
||||
1 pikeas 0.0%
|
||||
1 shuwatto 0.0%
|
||||
1 tstrimple 0.0%
|
||||
1 ewoudj 0.0%
|
||||
1 Adam Sanderson 0.0%
|
||||
1 Andrii Kostenko 0.0%
|
||||
1 Andy Hiew 0.0%
|
||||
1 Arpad Borsos 0.0%
|
||||
1 Ashwin Purohit 0.0%
|
||||
1 Benjen 0.0%
|
||||
1 Darren Torpey 0.0%
|
||||
1 Greg Ritter 0.0%
|
||||
1 Gregory Ritter 0.0%
|
||||
1 James Herdman 0.0%
|
||||
1 Jim Snodgrass 0.0%
|
||||
1 Joe McCann 0.0%
|
||||
1 Jonathan Dumaine 0.0%
|
||||
1 Jonathan Palardy 0.0%
|
||||
1 Jonathan Zacsh 0.0%
|
||||
1 Justin Lilly 0.0%
|
||||
1 Ken Sato 0.0%
|
||||
1 Maciej Małecki 0.0%
|
||||
1 Masahiro Hayashi 0.0%
|
||||
|
||||
Then run whichever example you want:
|
||||
|
||||
```bash
|
||||
$ node examples/content-negotiation
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
To run the test suite, first install the dependancies, then run `npm test`:
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
$ npm test
|
||||
```
|
||||
|
||||
## People
|
||||
|
||||
The original author of Express is [TJ Holowaychuk](https://github.com/tj) [![TJ's Gratipay][gratipay-image-visionmedia]][gratipay-url-visionmedia]
|
||||
|
||||
The current lead maintainer is [Douglas Christopher Wilson](https://github.com/dougwilson) [![Doug's Gratipay][gratipay-image-dougwilson]][gratipay-url-dougwilson]
|
||||
|
||||
[List of all contributors](https://github.com/strongloop/express/graphs/contributors)
|
||||
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
[MIT](LICENSE)
|
||||
|
||||
Copyright (c) 2009-2012 TJ Holowaychuk <tj@vision-media.ca>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
[npm-image]: https://img.shields.io/npm/v/express.svg?style=flat
|
||||
[npm-url]: https://npmjs.org/package/express
|
||||
[downloads-image]: https://img.shields.io/npm/dm/express.svg?style=flat
|
||||
[downloads-url]: https://npmjs.org/package/express
|
||||
[travis-image]: https://img.shields.io/travis/strongloop/express.svg?style=flat
|
||||
[travis-url]: https://travis-ci.org/strongloop/express
|
||||
[coveralls-image]: https://img.shields.io/coveralls/strongloop/express.svg?style=flat
|
||||
[coveralls-url]: https://coveralls.io/r/strongloop/express?branch=master
|
||||
[gratipay-image-visionmedia]: https://img.shields.io/gratipay/visionmedia.svg?style=flat
|
||||
[gratipay-url-visionmedia]: https://gratipay.com/visionmedia/
|
||||
[gratipay-image-dougwilson]: https://img.shields.io/gratipay/dougwilson.svg?style=flat
|
||||
[gratipay-url-dougwilson]: https://gratipay.com/dougwilson/
|
||||
|
||||
13
benchmarks/Makefile
Normal file
13
benchmarks/Makefile
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
all:
|
||||
@./run 1 middleware
|
||||
@./run 5 middleware
|
||||
@./run 10 middleware
|
||||
@./run 15 middleware
|
||||
@./run 20 middleware
|
||||
@./run 30 middleware
|
||||
@./run 50 middleware
|
||||
@./run 100 middleware
|
||||
@echo
|
||||
|
||||
.PHONY: all
|
||||
23
benchmarks/middleware.js
Normal file
23
benchmarks/middleware.js
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
var http = require('http');
|
||||
var express = require('..');
|
||||
var app = express();
|
||||
|
||||
// number of middleware
|
||||
|
||||
var n = parseInt(process.env.MW || '1', 10);
|
||||
console.log(' %s middleware', n);
|
||||
|
||||
while (n--) {
|
||||
app.use(function(req, res, next){
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
var body = new Buffer('Hello World');
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.send(body);
|
||||
});
|
||||
|
||||
app.listen(3333);
|
||||
16
benchmarks/run
Executable file
16
benchmarks/run
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo
|
||||
MW=$1 node $2 &
|
||||
pid=$!
|
||||
|
||||
sleep 2
|
||||
|
||||
wrk 'http://localhost:3333/?foo[bar]=baz' \
|
||||
-d 3 \
|
||||
-c 50 \
|
||||
-t 8 \
|
||||
| grep 'Requests/sec' \
|
||||
| awk '{ print " " $2 }'
|
||||
|
||||
kill $pid
|
||||
422
bin/express
422
bin/express
@@ -1,422 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var exec = require('child_process').exec
|
||||
, program = require('commander')
|
||||
, mkdirp = require('mkdirp')
|
||||
, pkg = require('../package.json')
|
||||
, version = pkg.version
|
||||
, os = require('os')
|
||||
, fs = require('fs');
|
||||
|
||||
// CLI
|
||||
|
||||
program
|
||||
.version(version)
|
||||
.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);
|
||||
|
||||
// Path
|
||||
|
||||
var path = program.args.shift() || '.';
|
||||
|
||||
// end-of-line code
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
var index = [
|
||||
''
|
||||
, '/*'
|
||||
, ' * GET home page.'
|
||||
, ' */'
|
||||
, ''
|
||||
, 'exports.index = function(req, res){'
|
||||
, ' res.render(\'index\', { title: \'Express\' });'
|
||||
, '};'
|
||||
].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'
|
||||
, ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')'
|
||||
, ' body'
|
||||
, ' block content'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* Jade index template.
|
||||
*/
|
||||
|
||||
var jadeIndex = [
|
||||
'extends layout'
|
||||
, ''
|
||||
, 'block content'
|
||||
, ' h1= title'
|
||||
, ' p Welcome to #{title}'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* EJS index template.
|
||||
*/
|
||||
|
||||
var ejsIndex = [
|
||||
'<!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);
|
||||
|
||||
/**
|
||||
* JSHTML layout template.
|
||||
*/
|
||||
|
||||
var jshtmlLayout = [
|
||||
'<!DOCTYPE html>'
|
||||
, '<html>'
|
||||
, ' <head>'
|
||||
, ' <title> @write(title) </title>'
|
||||
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
|
||||
, ' </head>'
|
||||
, ' <body>'
|
||||
, ' @write(body)'
|
||||
, ' </body>'
|
||||
, '</html>'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* JSHTML index template.
|
||||
*/
|
||||
|
||||
var jshtmlIndex = [
|
||||
'<h1>@write(title)</h1>'
|
||||
, '<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.
|
||||
*/
|
||||
|
||||
var css = [
|
||||
'body {'
|
||||
, ' padding: 50px;'
|
||||
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
|
||||
, '}'
|
||||
, ''
|
||||
, 'a {'
|
||||
, ' color: #00B7FF;'
|
||||
, '}'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* Default less template.
|
||||
*/
|
||||
|
||||
var less = [
|
||||
'body {'
|
||||
, ' padding: 50px;'
|
||||
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
|
||||
, '}'
|
||||
, ''
|
||||
, 'a {'
|
||||
, ' color: #00B7FF;'
|
||||
, '}'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* Default stylus template.
|
||||
*/
|
||||
|
||||
var stylus = [
|
||||
'body'
|
||||
, ' padding: 50px'
|
||||
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif'
|
||||
, 'a'
|
||||
, ' color: #00B7FF'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* App template.
|
||||
*/
|
||||
|
||||
var app = [
|
||||
''
|
||||
, '/**'
|
||||
, ' * Module dependencies.'
|
||||
, ' */'
|
||||
, ''
|
||||
, 'var express = require(\'express\')'
|
||||
, ' , routes = require(\'./routes\')'
|
||||
, ' , user = require(\'./routes/user\')'
|
||||
, ' , http = require(\'http\')'
|
||||
, ' , path = require(\'path\');'
|
||||
, ''
|
||||
, 'var app = express();'
|
||||
, ''
|
||||
, '// 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\')));'
|
||||
, ''
|
||||
, '// development only'
|
||||
, 'if (\'development\' == app.get(\'env\')) {'
|
||||
, ' app.use(express.errorHandler());'
|
||||
, '}'
|
||||
, ''
|
||||
, 'app.get(\'/\', routes.index);'
|
||||
, 'app.get(\'/users\', user.list);'
|
||||
, ''
|
||||
, 'http.createServer(app).listen(app.get(\'port\'), function(){'
|
||||
, ' console.log(\'Express server listening on port \' + app.get(\'port\'));'
|
||||
, '});'
|
||||
, ''
|
||||
].join(eol);
|
||||
|
||||
// Generate application
|
||||
|
||||
(function createApplication(path) {
|
||||
emptyDirectory(path, function(empty){
|
||||
if (empty || program.force) {
|
||||
createApplicationAt(path);
|
||||
} else {
|
||||
program.confirm('destination is not empty, continue? ', function(ok){
|
||||
if (ok) {
|
||||
process.stdin.destroy();
|
||||
createApplicationAt(path);
|
||||
} else {
|
||||
abort('aborting');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
})(path);
|
||||
|
||||
/**
|
||||
* Create application at the given directory `path`.
|
||||
*
|
||||
* @param {String} path
|
||||
*/
|
||||
|
||||
function createApplicationAt(path) {
|
||||
console.log();
|
||||
process.on('exit', function(){
|
||||
console.log();
|
||||
console.log(' install dependencies:');
|
||||
console.log(' $ cd %s && npm install', path);
|
||||
console.log();
|
||||
console.log(' run the app:');
|
||||
console.log(' $ node app');
|
||||
console.log();
|
||||
});
|
||||
|
||||
mkdir(path, function(){
|
||||
mkdir(path + '/public');
|
||||
mkdir(path + '/public/javascripts');
|
||||
mkdir(path + '/public/images');
|
||||
mkdir(path + '/public/stylesheets', function(){
|
||||
switch (program.css) {
|
||||
case 'less':
|
||||
write(path + '/public/stylesheets/style.less', less);
|
||||
break;
|
||||
case 'stylus':
|
||||
write(path + '/public/stylesheets/style.styl', stylus);
|
||||
break;
|
||||
default:
|
||||
write(path + '/public/stylesheets/style.css', css);
|
||||
}
|
||||
});
|
||||
|
||||
mkdir(path + '/routes', function(){
|
||||
write(path + '/routes/index.js', index);
|
||||
write(path + '/routes/user.js', users);
|
||||
});
|
||||
|
||||
mkdir(path + '/views', function(){
|
||||
switch (program.template) {
|
||||
case 'ejs':
|
||||
write(path + '/views/index.ejs', ejsIndex);
|
||||
break;
|
||||
case 'jade':
|
||||
write(path + '/views/layout.jade', jadeLayout);
|
||||
write(path + '/views/index.jade', jadeIndex);
|
||||
break;
|
||||
case 'jshtml':
|
||||
write(path + '/views/layout.jshtml', jshtmlLayout);
|
||||
write(path + '/views/index.jshtml', jshtmlIndex);
|
||||
break;
|
||||
case 'hjs':
|
||||
write(path + '/views/index.hjs', hoganIndex);
|
||||
break;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// CSS Engine support
|
||||
switch (program.css) {
|
||||
case 'less':
|
||||
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(__dirname + \'/public\'));');
|
||||
break;
|
||||
default:
|
||||
app = app.replace('{css}', '');
|
||||
}
|
||||
|
||||
// Session support
|
||||
app = app.replace('{sess}', program.sessions
|
||||
? eol + 'app.use(express.cookieParser(\'your secret here\'));' + eol + 'app.use(express.session());'
|
||||
: '');
|
||||
|
||||
// Template support
|
||||
app = app.replace(':TEMPLATE', program.template);
|
||||
|
||||
// package.json
|
||||
var pkg = {
|
||||
name: 'application-name'
|
||||
, version: '0.0.1'
|
||||
, private: true
|
||||
, scripts: { start: 'node app.js' }
|
||||
, dependencies: {
|
||||
express: version
|
||||
}
|
||||
}
|
||||
|
||||
if (program.template) pkg.dependencies[program.template] = '*';
|
||||
|
||||
// CSS Engine support
|
||||
switch (program.css) {
|
||||
case 'less':
|
||||
pkg.dependencies['less-middleware'] = '*';
|
||||
break;
|
||||
default:
|
||||
if (program.css) {
|
||||
pkg.dependencies[program.css] = '*';
|
||||
}
|
||||
}
|
||||
|
||||
write(path + '/package.json', JSON.stringify(pkg, null, 2));
|
||||
write(path + '/app.js', app);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given directory `path` is empty.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Function} fn
|
||||
*/
|
||||
|
||||
function emptyDirectory(path, fn) {
|
||||
fs.readdir(path, function(err, files){
|
||||
if (err && 'ENOENT' != err.code) throw err;
|
||||
fn(!files || !files.length);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* echo str > path.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {String} str
|
||||
*/
|
||||
|
||||
function write(path, str) {
|
||||
fs.writeFile(path, str);
|
||||
console.log(' \x1b[36mcreate\x1b[0m : ' + path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mkdir -p.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Function} fn
|
||||
*/
|
||||
|
||||
function mkdir(path, fn) {
|
||||
mkdirp(path, 0755, function(err){
|
||||
if (err) throw err;
|
||||
console.log(' \033[36mcreate\033[0m : ' + path);
|
||||
fn && fn();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit with the given `str`.
|
||||
*
|
||||
* @param {String} str
|
||||
*/
|
||||
|
||||
function abort(str) {
|
||||
console.error(str);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -2,8 +2,10 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..')
|
||||
, hash = require('./pass').hash;
|
||||
var express = require('../..');
|
||||
var hash = require('./pass').hash;
|
||||
var bodyParser = require('body-parser');
|
||||
var session = require('express-session');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
@@ -14,15 +16,18 @@ app.set('views', __dirname + '/views');
|
||||
|
||||
// middleware
|
||||
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.cookieParser('shhhh, very secret'));
|
||||
app.use(express.session());
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
app.use(session({
|
||||
resave: false, // don't save session if unmodified
|
||||
saveUninitialized: false, // don't create session until something stored
|
||||
secret: 'shhhh, very secret'
|
||||
}));
|
||||
|
||||
// Session-persisted message middleware
|
||||
|
||||
app.use(function(req, res, next){
|
||||
var err = req.session.error
|
||||
, msg = req.session.success;
|
||||
var err = req.session.error;
|
||||
var msg = req.session.success;
|
||||
delete req.session.error;
|
||||
delete req.session.success;
|
||||
res.locals.message = '';
|
||||
@@ -62,7 +67,7 @@ function authenticate(name, pass, fn) {
|
||||
if (err) return fn(err);
|
||||
if (hash == user.hash) return fn(null, user);
|
||||
fn(new Error('invalid password'));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
function restrict(req, res, next) {
|
||||
@@ -75,7 +80,7 @@ function restrict(req, res, next) {
|
||||
}
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('login');
|
||||
res.redirect('/login');
|
||||
});
|
||||
|
||||
app.get('/restricted', restrict, function(req, res){
|
||||
@@ -98,9 +103,9 @@ app.post('/login', function(req, res){
|
||||
authenticate(req.body.username, req.body.password, function(err, user){
|
||||
if (user) {
|
||||
// Regenerate session when signing in
|
||||
// to prevent fixation
|
||||
// to prevent fixation
|
||||
req.session.regenerate(function(){
|
||||
// Store the user's primary key
|
||||
// Store the user's primary key
|
||||
// in the session store to be retrieved,
|
||||
// or in this case the entire user object
|
||||
req.session.user = user;
|
||||
@@ -113,11 +118,12 @@ app.post('/login', function(req, res){
|
||||
req.session.error = 'Authentication failed, please check your '
|
||||
+ ' username and password.'
|
||||
+ ' (use "tj" and "foobar")';
|
||||
res.redirect('login');
|
||||
res.redirect('/login');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
// check out https://github.com/visionmedia/node-pwd
|
||||
// check out https://github.com/tj/node-pwd
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
@@ -32,7 +32,7 @@ var iterations = 12000;
|
||||
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'));
|
||||
fn(err, hash.toString('base64'));
|
||||
});
|
||||
} else {
|
||||
fn = salt;
|
||||
@@ -41,8 +41,8 @@ exports.hash = function (pwd, salt, fn) {
|
||||
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'));
|
||||
fn(null, salt, hash.toString('base64'));
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..')
|
||||
, app = express();
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var app = express();
|
||||
|
||||
app.set('views', __dirname);
|
||||
app.set('view engine', 'jade');
|
||||
@@ -14,11 +18,14 @@ while (n--) {
|
||||
pets.push({ name: 'Jane', age: 6, species: 'ferret' });
|
||||
}
|
||||
|
||||
app.use(express.logger('dev'));
|
||||
app.use(logger('dev'));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('pets', { pets: pets });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express listening on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
var users = [];
|
||||
|
||||
users.push({ name: 'Tobi' });
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
var users = require('./db');
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express()
|
||||
, users = require('./db');
|
||||
|
||||
// so either you can deal with different types of formatting
|
||||
// for expected response in index.js
|
||||
app.get('/', function(req, res){
|
||||
res.format({
|
||||
html: function(){
|
||||
@@ -20,22 +21,24 @@ app.get('/', function(req, res){
|
||||
json: function(){
|
||||
res.json(users);
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
// or you could write a tiny middleware like
|
||||
// this to abstract make things a bit more declarative:
|
||||
// this to add a layer of abstraction
|
||||
// and make things a bit more declarative:
|
||||
|
||||
function format(mod) {
|
||||
var obj = require(mod);
|
||||
function format(path) {
|
||||
var obj = require(path);
|
||||
return function(req, res){
|
||||
res.format(obj);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
app.get('/users', format('./users'));
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('listening on port 3000');
|
||||
}
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -7,14 +6,8 @@ var express = require('../../');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// ignore GET /favicon.ico
|
||||
app.use(express.favicon());
|
||||
|
||||
// pass a secret to cookieParser() for signed cookies
|
||||
app.use(express.cookieParser('manny is cool'));
|
||||
|
||||
// add req.session cookie support
|
||||
app.use(express.cookieSession());
|
||||
app.use(cookieSession({ secret: 'manny is cool' }));
|
||||
|
||||
// do something with the session
|
||||
app.use(count);
|
||||
@@ -26,7 +19,8 @@ function count(req, res) {
|
||||
res.send('viewed ' + n + ' times\n');
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express server listening on port 3000');
|
||||
}
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -1,31 +1,24 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
|
||||
|
||||
// add favicon() before logger() so
|
||||
// GET /favicon.ico requests are not
|
||||
// logged, because this middleware
|
||||
// reponds to /favicon.ico and does not
|
||||
// call next()
|
||||
app.use(express.favicon());
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
// custom log format
|
||||
if ('test' != process.env.NODE_ENV)
|
||||
app.use(express.logger(':method :url'));
|
||||
if ('test' != process.env.NODE_ENV) app.use(logger(':method :url'));
|
||||
|
||||
// parses request cookies, populating
|
||||
// req.cookies and req.signedCookies
|
||||
// when the secret is passed, used
|
||||
// when the secret is passed, used
|
||||
// for signing the cookies.
|
||||
app.use(express.cookieParser('my secret here'));
|
||||
app.use(cookieParser('my secret here'));
|
||||
|
||||
// parses json, x-www-form-urlencoded, and multipart/form-data
|
||||
app.use(express.bodyParser());
|
||||
// parses x-www-form-urlencoded
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
if (req.cookies.remember) {
|
||||
@@ -48,7 +41,8 @@ app.post('/', function(req, res){
|
||||
res.redirect('back');
|
||||
});
|
||||
|
||||
if (!module.parent){
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/**
|
||||
* 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');
|
||||
@@ -1,12 +0,0 @@
|
||||
<!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>
|
||||
@@ -1,44 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('<ul>'
|
||||
+ '<li>Download <a href="/files/amazing.txt">amazing.txt</a>.</li>'
|
||||
+ '<li>Download <a href="/files/missing.txt">missing.txt</a>.</li>'
|
||||
+ '</ul>');
|
||||
});
|
||||
|
||||
// /files/* is accessed via req.params[0]
|
||||
// but here we name it :file
|
||||
app.get('/files/:file(*)', function(req, res, next){
|
||||
var file = req.params.file
|
||||
, path = __dirname + '/files/' + file;
|
||||
|
||||
res.download(path);
|
||||
});
|
||||
|
||||
// error handling middleware. Because it's
|
||||
// below our routes, you will be able to
|
||||
// "intercept" errors, otherwise Connect
|
||||
// will respond with 500 "Internal Server Error".
|
||||
app.use(function(err, req, res, next){
|
||||
// special-case 404s,
|
||||
// remember you could
|
||||
// render a 404 template here
|
||||
if (404 == err.status) {
|
||||
res.statusCode = 404;
|
||||
res.send('Cant find that file, sorry!');
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
2
examples/downloads/files/CCTV大赛上海分赛区.txt
Normal file
2
examples/downloads/files/CCTV大赛上海分赛区.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
Only for test.
|
||||
The file name is faked.
|
||||
1
examples/downloads/files/utf-8 한中日.txt
Normal file
1
examples/downloads/files/utf-8 한中日.txt
Normal file
@@ -0,0 +1 @@
|
||||
한中日
|
||||
36
examples/downloads/index.js
Normal file
36
examples/downloads/index.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('<ul>'
|
||||
+ '<li>Download <a href="/files/amazing.txt">amazing.txt</a>.</li>'
|
||||
+ '<li>Download <a href="/files/utf-8 한中日.txt">utf-8 한中日.txt</a>.</li>'
|
||||
+ '<li>Download <a href="/files/missing.txt">missing.txt</a>.</li>'
|
||||
+ '<li>Download <a href="/files/CCTV大赛上海分赛区.txt">CCTV大赛上海分赛区.txt</a>.</li>'
|
||||
+ '</ul>');
|
||||
});
|
||||
|
||||
// /files/* is accessed via req.params[0]
|
||||
// but here we name it :file
|
||||
app.get('/files/:file(*)', function(req, res, next){
|
||||
var file = req.params.file;
|
||||
var path = __dirname + '/files/' + file;
|
||||
|
||||
res.download(path, function(err){
|
||||
if (!err) return; // file sent
|
||||
if (err && err.status !== 404) return next(err); // non-404 error
|
||||
// file for download not found
|
||||
res.statusCode = 404;
|
||||
res.send('Cant find that file, sorry!');
|
||||
});
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -44,7 +43,8 @@ app.get('/', function(req, res){
|
||||
});
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express()
|
||||
, silent = 'test' == process.env.NODE_ENV;
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
var logger = require('morgan');
|
||||
var silent = 'test' == process.env.NODE_ENV;
|
||||
|
||||
// general config
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
// our custom "verbose errors" setting
|
||||
// which we can use in the templates
|
||||
@@ -17,22 +18,36 @@ 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');
|
||||
}
|
||||
if ('production' == app.settings.env) app.disable('verbose errors');
|
||||
|
||||
app.use(express.favicon());
|
||||
silent || app.use(logger('dev'));
|
||||
|
||||
silent || app.use(express.logger('dev'));
|
||||
// Routes
|
||||
|
||||
// "app.router" positions our routes
|
||||
// above the middleware defined below,
|
||||
// this means that Express will attempt
|
||||
// to match & call routes _before_ continuing
|
||||
// on, at which point we assume it's a 404 because
|
||||
// no route has handled the request.
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.ejs');
|
||||
});
|
||||
|
||||
app.use(app.router);
|
||||
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!'));
|
||||
});
|
||||
|
||||
// Error handlers
|
||||
|
||||
// Since this is the last non-error-handling
|
||||
// middleware use()d, we assume 404, as nothing else
|
||||
@@ -44,7 +59,7 @@ app.use(app.router);
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.status(404);
|
||||
|
||||
|
||||
// respond with html page
|
||||
if (req.accepts('html')) {
|
||||
res.render('404', { url: req.url });
|
||||
@@ -81,32 +96,8 @@ app.use(function(err, req, res, next){
|
||||
res.render('500', { error: err });
|
||||
});
|
||||
|
||||
// Routes
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.jade');
|
||||
});
|
||||
|
||||
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!'));
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
silent || console.log('Express started on port 3000');
|
||||
}
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
3
examples/error-pages/views/404.ejs
Normal file
3
examples/error-pages/views/404.ejs
Normal file
@@ -0,0 +1,3 @@
|
||||
<% include error_header %>
|
||||
<h2>Cannot find <%= url %></h2>
|
||||
<% include footer %>
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
extends error
|
||||
|
||||
block content
|
||||
h2 Cannot find #{url}
|
||||
8
examples/error-pages/views/500.ejs
Normal file
8
examples/error-pages/views/500.ejs
Normal file
@@ -0,0 +1,8 @@
|
||||
<% include error_header %>
|
||||
<h2>Error: <%= error.message %></h2>
|
||||
<% if (settings['verbose errors']) { %>
|
||||
<pre><%= error.stack %></pre>
|
||||
<% } else { %>
|
||||
<p>An error occurred!</p>
|
||||
<% } %>
|
||||
<% include footer %>
|
||||
@@ -1,13 +0,0 @@
|
||||
|
||||
// note that we extend a different
|
||||
// layout with jade for 4xx & 5xx
|
||||
// responses
|
||||
|
||||
extends error
|
||||
|
||||
block content
|
||||
h1 Error: #{error.message}
|
||||
if settings['verbose errors']
|
||||
pre= error.stack
|
||||
else
|
||||
p An error ocurred!
|
||||
@@ -1,6 +0,0 @@
|
||||
html
|
||||
head
|
||||
title Error
|
||||
body
|
||||
h1 An error occurred!
|
||||
block content
|
||||
8
examples/error-pages/views/error_header.ejs
Normal file
8
examples/error-pages/views/error_header.ejs
Normal file
@@ -0,0 +1,8 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Error</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>An error occurred!</h1>
|
||||
2
examples/error-pages/views/footer.ejs
Normal file
2
examples/error-pages/views/footer.ejs
Normal file
@@ -0,0 +1,2 @@
|
||||
</body>
|
||||
</html>
|
||||
18
examples/error-pages/views/index.ejs
Normal file
18
examples/error-pages/views/index.ejs
Normal file
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Custom Pages Example</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>My Site</h1>
|
||||
<h2>Pages Example</h2>
|
||||
|
||||
<ul>
|
||||
<li>visit <a href="/500">500</a></li>
|
||||
<li>visit <a href="/404">404</a></li>
|
||||
<li>visit <a href="/403">403</a></li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
extends layout
|
||||
|
||||
block content
|
||||
h2 Pages Example
|
||||
ul
|
||||
li
|
||||
| visit
|
||||
a(href="/500") 500
|
||||
li
|
||||
| visit
|
||||
a(href="/404") 404
|
||||
li
|
||||
| visit
|
||||
a(href='/403') 403
|
||||
@@ -1,6 +0,0 @@
|
||||
html
|
||||
head
|
||||
title Custom Pages Example
|
||||
body
|
||||
h1 My Site
|
||||
block content
|
||||
@@ -1,20 +1,13 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express()
|
||||
, test = app.get('env') == 'test';
|
||||
var express = require('../../');
|
||||
var logger = require('morgan');
|
||||
var app = module.exports = express();
|
||||
var test = app.get('env') == 'test';
|
||||
|
||||
if (!test) app.use(express.logger('dev'));
|
||||
app.use(app.router);
|
||||
|
||||
// the error handler is strategically
|
||||
// placed *below* the app.router; if it
|
||||
// were above it would not receive errors
|
||||
// from app.get() etc
|
||||
app.use(error);
|
||||
if (!test) app.use(logger('dev'));
|
||||
|
||||
// error handling middleware have an arity of 4
|
||||
// instead of the typical (req, res, next),
|
||||
@@ -27,7 +20,8 @@ function error(err, req, res, next) {
|
||||
if (!test) console.error(err.stack);
|
||||
|
||||
// respond with 500 "Internal Server Error".
|
||||
res.send(500);
|
||||
res.status(500);
|
||||
res.send('Internal Server Error');
|
||||
}
|
||||
|
||||
app.get('/', function(req, res){
|
||||
@@ -42,7 +36,13 @@ app.get('/next', function(req, res, next){
|
||||
});
|
||||
});
|
||||
|
||||
// the error handler is placed after routes
|
||||
// if it were above it would not receive errors
|
||||
// from app.get() etc
|
||||
app.use(error);
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
var express = require('../..')
|
||||
, app = express();
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var app = express();
|
||||
|
||||
app.set('view engine', 'jade');
|
||||
app.set('views', __dirname + '/views');
|
||||
@@ -20,10 +21,10 @@ User.prototype.toJSON = function(){
|
||||
return {
|
||||
id: this.id,
|
||||
name: this.name
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
app.use(express.logger('dev'));
|
||||
app.use(logger('dev'));
|
||||
|
||||
// earlier on expose an object
|
||||
// that we can tack properties on.
|
||||
@@ -56,5 +57,8 @@ app.get('/user', function(req, res){
|
||||
res.render('page');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('app listening on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
var express = require('../../');
|
||||
|
||||
var app = express();
|
||||
@@ -7,5 +6,8 @@ app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -12,9 +11,7 @@ var pub = __dirname + '/public';
|
||||
// setup middleware
|
||||
|
||||
var app = express();
|
||||
app.use(app.router);
|
||||
app.use(express.static(pub));
|
||||
app.use(express.errorHandler());
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
@@ -41,5 +38,14 @@ app.get('/', function(req, res){
|
||||
res.render('users', { users: users });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
// change this to a better error handler in your code
|
||||
// sending stacktrace to users in production is not good
|
||||
app.use(function(err, req, res, next) {
|
||||
res.send(err.stack);
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
!!! 5
|
||||
doctype html
|
||||
html
|
||||
include header
|
||||
body
|
||||
block content
|
||||
block content
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..')
|
||||
, fs = require('fs')
|
||||
, md = require('marked').parse;
|
||||
var express = require('../..');
|
||||
var fs = require('fs');
|
||||
var md = require('marked').parse;
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
@@ -18,13 +17,13 @@ app.engine('md', function(path, options, fn){
|
||||
var html = md(str);
|
||||
html = html.replace(/\{([^}]+)\}/g, function(_, name){
|
||||
return options[name] || '';
|
||||
})
|
||||
});
|
||||
fn(null, html);
|
||||
} catch(err) {
|
||||
fn(err);
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
@@ -33,12 +32,13 @@ app.set('view engine', 'md');
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index', { title: 'Markdown Example' });
|
||||
})
|
||||
});
|
||||
|
||||
app.get('/fail', function(req, res){
|
||||
res.render('missing', { title: 'Markdown Example' });
|
||||
})
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
|
||||
13
examples/multi-router/controllers/api_v1.js
Normal file
13
examples/multi-router/controllers/api_v1.js
Normal file
@@ -0,0 +1,13 @@
|
||||
var express = require('../../..');
|
||||
|
||||
var apiv1 = express.Router();
|
||||
|
||||
apiv1.get('/', function(req, res) {
|
||||
res.send('Hello from APIv1 root route.');
|
||||
});
|
||||
|
||||
apiv1.get('/users', function(req, res) {
|
||||
res.send('List of APIv1 users.');
|
||||
});
|
||||
|
||||
module.exports = apiv1;
|
||||
13
examples/multi-router/controllers/api_v2.js
Normal file
13
examples/multi-router/controllers/api_v2.js
Normal file
@@ -0,0 +1,13 @@
|
||||
var express = require('../../..');
|
||||
|
||||
var apiv2 = express.Router();
|
||||
|
||||
apiv2.get('/', function(req, res) {
|
||||
res.send('Hello from APIv2 root route.');
|
||||
});
|
||||
|
||||
apiv2.get('/users', function(req, res) {
|
||||
res.send('List of APIv2 users.');
|
||||
});
|
||||
|
||||
module.exports = apiv2;
|
||||
16
examples/multi-router/index.js
Normal file
16
examples/multi-router/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
var express = require('../..');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
app.use('/api/v1', require('./controllers/api_v1'));
|
||||
app.use('/api/v2', require('./controllers/api_v2'));
|
||||
|
||||
app.get('/', function(req, res) {
|
||||
res.send('Hello form root route.');
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,16 +1,12 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..')
|
||||
, format = require('util').format;
|
||||
var express = require('../..');
|
||||
var multiparty = require('multiparty');
|
||||
var format = require('util').format;
|
||||
|
||||
var app = module.exports = express()
|
||||
|
||||
// bodyParser in connect 2.x uses node-formidable to parse
|
||||
// the multipart form data.
|
||||
app.use(express.bodyParser())
|
||||
var app = module.exports = express();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('<form method="post" enctype="multipart/form-data">'
|
||||
@@ -21,16 +17,44 @@ app.get('/', function(req, res){
|
||||
});
|
||||
|
||||
app.post('/', function(req, res, next){
|
||||
// the uploaded file can be found as `req.files.image` and the
|
||||
// title field as `req.body.title`
|
||||
res.send(format('\nuploaded %s (%d Kb) to %s as %s'
|
||||
, req.files.image.name
|
||||
, req.files.image.size / 1024 | 0
|
||||
, req.files.image.path
|
||||
, req.body.title));
|
||||
// create a form to begin parsing
|
||||
var form = new multiparty.Form();
|
||||
var image;
|
||||
var title;
|
||||
|
||||
form.on('error', next);
|
||||
form.on('close', function(){
|
||||
res.send(format('\nuploaded %s (%d Kb) as %s'
|
||||
, image.filename
|
||||
, image.size / 1024 | 0
|
||||
, title));
|
||||
});
|
||||
|
||||
// listen on field event for title
|
||||
form.on('field', function(name, val){
|
||||
if (name !== 'title') return;
|
||||
title = val;
|
||||
});
|
||||
|
||||
// listen on part event for image file
|
||||
form.on('part', function(part){
|
||||
if (!part.filename) return;
|
||||
if (part.name !== 'image') return part.resume();
|
||||
image = {};
|
||||
image.filename = part.filename;
|
||||
image.size = 0;
|
||||
part.on('data', function(buf){
|
||||
image.size += buf.length;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// parse the form
|
||||
form.parse(req);
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
app.listen(4000);
|
||||
console.log('Express started on port 4000');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
exports.index = function(req, res){
|
||||
res.redirect('/users');
|
||||
};
|
||||
@@ -1,11 +1,14 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
exports.engine = 'jade';
|
||||
exports.engine = 'ejs';
|
||||
|
||||
exports.before = function(req, res, next){
|
||||
var pet = db.pets[req.params.pet_id];
|
||||
if (!pet) return next(new Error('Pet not found'));
|
||||
if (!pet) return next('route');
|
||||
req.pet = pet;
|
||||
next();
|
||||
};
|
||||
@@ -20,7 +23,7 @@ exports.edit = function(req, res, next){
|
||||
|
||||
exports.update = function(req, res, next){
|
||||
var body = req.body;
|
||||
req.pet.name = body.user.name;
|
||||
req.pet.name = body.pet.name;
|
||||
res.message('Information updated!');
|
||||
res.redirect('/pet/' + req.pet.id);
|
||||
};
|
||||
|
||||
15
examples/mvc/controllers/pet/views/edit.ejs
Normal file
15
examples/mvc/controllers/pet/views/edit.ejs
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title>Edit <%= pet.name %></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1><%= pet.name %></h1>
|
||||
<form action="/pet/<%= pet.id %>?_method=put" method="post">
|
||||
<label>Name: <input type="text" name="user[name]" value="<%= pet.name %>"></label>
|
||||
<input type="submit" value="Update">
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +0,0 @@
|
||||
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')
|
||||
13
examples/mvc/controllers/pet/views/show.ejs
Normal file
13
examples/mvc/controllers/pet/views/show.ejs
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<title><%= pet.name %></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1><%= pet.name %> <a href="/pet/<%= pet.id %>/edit">edit</a></h1>
|
||||
|
||||
<p>You are viewing <%= pet.name %></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +0,0 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
|
||||
h1= pet.name
|
||||
a(href='/pet/#{pet.id}/edit') edit
|
||||
|
||||
p You are viewing #{pet.name}
|
||||
@@ -1,3 +1,6 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
@@ -8,7 +11,7 @@ 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'));
|
||||
if (!user) return next('route');
|
||||
var pet = { name: body.pet.name };
|
||||
pet.id = db.pets.push(pet) - 1;
|
||||
user.pets.push(pet);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
@@ -8,11 +11,11 @@ exports.before = function(req, res, next){
|
||||
process.nextTick(function(){
|
||||
req.user = db.users[id];
|
||||
// cant find that user
|
||||
if (!req.user) return next(new Error('User not found'));
|
||||
if (!req.user) return next('route');
|
||||
// found it, move on to the routes
|
||||
next();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.list = function(req, res, next){
|
||||
res.render('list', { users: db.users });
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<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>
|
||||
11
examples/mvc/controllers/user/views/edit.jade
Normal file
11
examples/mvc/controllers/user/views/edit.jade
Normal file
@@ -0,0 +1,11 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1= user.name
|
||||
form(action='/user/#{user.id}?_method=put', method='post')
|
||||
label= 'Name: '
|
||||
input(type='text', name='user[name]', value='#{user.name}')
|
||||
input(type='submit', value='Update')
|
||||
|
||||
form(action='/user/#{user.id}/pet', method='post')
|
||||
label= 'Pet: '
|
||||
input(type='text', name='pet[name]', placeholder='Name')
|
||||
input(type='submit', value='Add')
|
||||
@@ -1,8 +0,0 @@
|
||||
<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>
|
||||
7
examples/mvc/controllers/user/views/list.jade
Normal file
7
examples/mvc/controllers/user/views/list.jade
Normal file
@@ -0,0 +1,7 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1 Users
|
||||
p Click a user below to view their pets.
|
||||
ul
|
||||
each user in users
|
||||
li
|
||||
a(href='/user/#{user.id}')= user.name
|
||||
@@ -1,21 +0,0 @@
|
||||
<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>
|
||||
<% } %>
|
||||
17
examples/mvc/controllers/user/views/show.jade
Normal file
17
examples/mvc/controllers/user/views/show.jade
Normal file
@@ -0,0 +1,17 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1= user.name + ' '
|
||||
a(href='/user/#{user.id}/edit') edit
|
||||
|
||||
if (hasMessages)
|
||||
ul#messages
|
||||
each msg in messages
|
||||
li= msg
|
||||
|
||||
if (user.pets.length)
|
||||
p View #{user.name}'s pets:
|
||||
ul
|
||||
each pet in user.pets
|
||||
li
|
||||
a(href='/pet/#{pet.id}')= pet.name
|
||||
else
|
||||
p No pets!
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
// faux database
|
||||
|
||||
var pets = exports.pets = [];
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var session = require('express-session');
|
||||
var bodyParser = require('body-parser');
|
||||
var methodOverride = require('method-override');
|
||||
|
||||
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 our default template engine to "jade"
|
||||
// which prevents the need for extensions
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
// set views for error and 404 pages
|
||||
app.set('views', __dirname + '/views');
|
||||
@@ -25,20 +31,23 @@ app.response.message = function(msg){
|
||||
};
|
||||
|
||||
// log
|
||||
if (!module.parent) app.use(express.logger('dev'));
|
||||
if (!module.parent) app.use(logger('dev'));
|
||||
|
||||
// serve static files
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// session support
|
||||
app.use(express.cookieParser('some secret here'));
|
||||
app.use(express.session());
|
||||
app.use(session({
|
||||
resave: false, // don't save session if unmodified
|
||||
saveUninitialized: false, // don't create session until something stored
|
||||
secret: 'some secret here'
|
||||
}));
|
||||
|
||||
// parse request bodies (req.body)
|
||||
app.use(express.bodyParser());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
|
||||
// support _method (PUT in forms etc)
|
||||
app.use(express.methodOverride());
|
||||
// allow overriding methods in query (?_method=put)
|
||||
app.use(methodOverride('_method'));
|
||||
|
||||
// expose the "messages" local variable when views are rendered
|
||||
app.use(function(req, res, next){
|
||||
@@ -66,16 +75,9 @@ app.use(function(req, res, 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);
|
||||
if (!module.parent) console.error(err.stack);
|
||||
|
||||
// error page
|
||||
res.status(500).render('5xx');
|
||||
@@ -86,7 +88,8 @@ app.use(function(req, res, next){
|
||||
res.status(404).render('404', { url: req.originalUrl });
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('\n listening on port 3000\n');
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -1,32 +1,26 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../..')
|
||||
, fs = require('fs');
|
||||
var express = require('../../..');
|
||||
var 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;
|
||||
var obj = require('./../controllers/' + name);
|
||||
var name = obj.name || name;
|
||||
var prefix = obj.prefix || '';
|
||||
var app = express();
|
||||
var handler;
|
||||
var method;
|
||||
var 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) {
|
||||
@@ -59,15 +53,25 @@ module.exports = function(parent, options){
|
||||
path = '/';
|
||||
break;
|
||||
default:
|
||||
/* istanbul ignore next */
|
||||
throw new Error('unrecognized route: ' + name + '.' + key);
|
||||
}
|
||||
|
||||
// setup
|
||||
handler = obj[key];
|
||||
path = prefix + path;
|
||||
app[method](path, obj[key]);
|
||||
verbose && console.log(' %s %s -> %s', method.toUpperCase(), path, key);
|
||||
|
||||
// before middleware support
|
||||
if (obj.before) {
|
||||
app[method](path, obj.before, handler);
|
||||
verbose && console.log(' %s %s -> before -> %s', method.toUpperCase(), path, key);
|
||||
} else {
|
||||
app[method](path, obj[key]);
|
||||
verbose && console.log(' %s %s -> %s', method.toUpperCase(), path, key);
|
||||
}
|
||||
}
|
||||
|
||||
// mount the app
|
||||
parent.use(app);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1>404: Not Found</h1>
|
||||
<p>Sorry we can't find <%= url %></p>
|
||||
3
examples/mvc/views/404.jade
Normal file
3
examples/mvc/views/404.jade
Normal file
@@ -0,0 +1,3 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1 404: Not Found
|
||||
p Sorry we can't find #{url}
|
||||
@@ -1,3 +0,0 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1>500: Internal Server Error</h1>
|
||||
<p>Looks like something blew up!</p>
|
||||
3
examples/mvc/views/5xx.jade
Normal file
3
examples/mvc/views/5xx.jade
Normal file
@@ -0,0 +1,3 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1 500: Internal Server Error
|
||||
p Looks like something blew up!
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
// first:
|
||||
// $ npm install redis online
|
||||
// $ redis-server
|
||||
@@ -7,10 +6,10 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..')
|
||||
, online = require('online')
|
||||
, redis = require('redis')
|
||||
, db = redis.createClient();
|
||||
var express = require('../..');
|
||||
var online = require('online');
|
||||
var redis = require('redis');
|
||||
var db = redis.createClient();
|
||||
|
||||
// online
|
||||
|
||||
@@ -50,5 +49,8 @@ app.get('/', function(req, res, next){
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('listening on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
|
||||
// Faux database
|
||||
|
||||
@@ -18,9 +17,9 @@ var users = [
|
||||
|
||||
// Convert :to and :from to integers
|
||||
|
||||
app.param(['to', 'from'], function(req, res, next, num, name){
|
||||
req.params[name] = num = parseInt(num, 10);
|
||||
if( isNaN(num) ){
|
||||
app.param(['to', 'from'], function(req, res, next, num, name){
|
||||
req.params[name] = parseInt(num, 10);
|
||||
if( isNaN(req.params[name]) ){
|
||||
next(new Error('failed to parseInt '+num));
|
||||
} else {
|
||||
next();
|
||||
@@ -58,13 +57,14 @@ app.get('/user/:user', function(req, res, next){
|
||||
*/
|
||||
|
||||
app.get('/users/:from-:to', function(req, res, next){
|
||||
var from = req.params.from
|
||||
, to = req.params.to
|
||||
, names = users.map(function(user){ return user.name; });
|
||||
var from = req.params.from;
|
||||
var to = req.params.to;
|
||||
var names = users.map(function(user){ return user.name; });
|
||||
res.send('users ' + names.slice(from, to).join(', '));
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -12,13 +11,16 @@ var app = module.exports = express();
|
||||
app.resource = function(path, obj) {
|
||||
this.get(path, obj.index);
|
||||
this.get(path + '/:a..:b.:format?', function(req, res){
|
||||
var a = parseInt(req.params.a, 10)
|
||||
, b = parseInt(req.params.b, 10)
|
||||
, format = req.params.format;
|
||||
var a = parseInt(req.params.a, 10);
|
||||
var b = parseInt(req.params.b, 10);
|
||||
var format = req.params.format;
|
||||
obj.range(req, res, a, b, format);
|
||||
});
|
||||
this.get(path + '/:id', obj.show);
|
||||
this.del(path + '/:id', obj.destroy);
|
||||
this.delete(path + '/:id', function(req, res){
|
||||
var id = parseInt(req.params.id, 10);
|
||||
obj.destroy(req, res, id);
|
||||
});
|
||||
};
|
||||
|
||||
// Fake records
|
||||
@@ -41,8 +43,7 @@ var User = {
|
||||
show: function(req, res){
|
||||
res.send(users[req.params.id] || { error: 'Cannot find user' });
|
||||
},
|
||||
destroy: function(req, res){
|
||||
var id = req.params.id;
|
||||
destroy: function(req, res, id){
|
||||
var destroyed = id in users;
|
||||
delete users[id];
|
||||
res.send(destroyed ? 'destroyed' : 'Cannot find user');
|
||||
@@ -82,10 +83,11 @@ app.get('/', function(req, res){
|
||||
, '<li>GET /users/1..3.json</li>'
|
||||
, '<li>DELETE /users/4</li>'
|
||||
, '</ul>'
|
||||
].join('\n'));
|
||||
].join('\n'));
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, verbose = process.env.NODE_ENV != 'test'
|
||||
, app = module.exports = express();
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var verbose = process.env.NODE_ENV != 'test';
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
app.map = function(a, route){
|
||||
route = route || '';
|
||||
@@ -29,7 +34,7 @@ var users = {
|
||||
res.send('user ' + req.params.uid);
|
||||
},
|
||||
|
||||
del: function(req, res){
|
||||
delete: function(req, res){
|
||||
res.send('delete users');
|
||||
}
|
||||
};
|
||||
@@ -39,7 +44,7 @@ var pets = {
|
||||
res.send('user ' + req.params.uid + '\'s pets');
|
||||
},
|
||||
|
||||
del: function(req, res){
|
||||
delete: function(req, res){
|
||||
res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid);
|
||||
}
|
||||
};
|
||||
@@ -47,17 +52,21 @@ var pets = {
|
||||
app.map({
|
||||
'/users': {
|
||||
get: users.list,
|
||||
del: users.del,
|
||||
delete: users.delete,
|
||||
'/:uid': {
|
||||
get: users.get,
|
||||
'/pets': {
|
||||
get: pets.list,
|
||||
'/:pid': {
|
||||
del: pets.del
|
||||
delete: pets.delete
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -77,9 +77,12 @@ app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){
|
||||
res.send('Editing user ' + req.user.name);
|
||||
});
|
||||
|
||||
app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
|
||||
app.delete('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
|
||||
res.send('Deleted user ' + req.user.name);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -1,22 +1,32 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..')
|
||||
, app = express()
|
||||
, site = require('./site')
|
||||
, post = require('./post')
|
||||
, user = require('./user');
|
||||
var express = require('../..');
|
||||
var app = express();
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var bodyParser = require('body-parser');
|
||||
var methodOverride = require('method-override');
|
||||
var site = require('./site');
|
||||
var post = require('./post');
|
||||
var user = require('./user');
|
||||
|
||||
module.exports = app;
|
||||
|
||||
// Config
|
||||
|
||||
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());
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.use(express.logger('dev'));
|
||||
}
|
||||
|
||||
app.use(methodOverride('_method'));
|
||||
app.use(cookieParser());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// General
|
||||
@@ -25,7 +35,7 @@ app.get('/', site.index);
|
||||
|
||||
// User
|
||||
|
||||
app.all('/users', user.list);
|
||||
app.get('/users', user.list);
|
||||
app.all('/user/:id/:op?', user.load);
|
||||
app.get('/user/:id', user.view);
|
||||
app.get('/user/:id/view', user.view);
|
||||
@@ -36,5 +46,8 @@ app.put('/user/:id/edit', user.update);
|
||||
|
||||
app.get('/posts', post.list);
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
// Fake posts database
|
||||
|
||||
var posts = [
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
exports.index = function(req, res){
|
||||
res.render('index', { title: 'Route Separation Example' });
|
||||
};
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
// Fake user database
|
||||
|
||||
var users = [
|
||||
@@ -16,7 +15,9 @@ exports.load = function(req, res, next){
|
||||
if (req.user) {
|
||||
next();
|
||||
} else {
|
||||
next(new Error('cannot find user ' + id));
|
||||
var err = new Error('cannot find user ' + id);
|
||||
err.status = 404;
|
||||
next(err);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -41,4 +42,4 @@ exports.update = function(req, res){
|
||||
req.user.name = user.name;
|
||||
req.user.email = user.email;
|
||||
res.redirect('back');
|
||||
};
|
||||
};
|
||||
|
||||
@@ -3,8 +3,7 @@ extends ../layout
|
||||
block content
|
||||
h1 Editing #{user.name}
|
||||
#user
|
||||
form(method="post")
|
||||
input(type="hidden", value="put", name="_method")
|
||||
form(action="?_method=put", method="post")
|
||||
p Name:
|
||||
input(type="text", value= user.name, name="user[name]")
|
||||
p Email:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
var search = document.querySelector('[type=search]');
|
||||
var code = document.querySelector('pre');
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
// first:
|
||||
// $ npm install redis
|
||||
// $ redis-server
|
||||
@@ -7,9 +6,10 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..')
|
||||
, redis = require('redis')
|
||||
, db = redis.createClient();
|
||||
var express = require('../..');
|
||||
var redis = require('redis');
|
||||
|
||||
var db = redis.createClient();
|
||||
|
||||
// npm install redis
|
||||
|
||||
@@ -47,15 +47,18 @@ app.get('/search/:query?', function(req, res){
|
||||
});
|
||||
|
||||
/**
|
||||
* GET client javascript. Here we use sendfile()
|
||||
* GET client javascript. Here we use sendFile()
|
||||
* because serving __dirname with the static() middleware
|
||||
* would also mean serving our server "index.js" and the "search.jade"
|
||||
* template.
|
||||
*/
|
||||
|
||||
app.get('/client.js', function(req, res){
|
||||
res.sendfile(__dirname + '/client.js');
|
||||
res.sendFile(__dirname + '/client.js');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('app listening on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
!!! 5
|
||||
doctype
|
||||
html
|
||||
head
|
||||
title Search example
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
|
||||
// first:
|
||||
// $ npm install redis
|
||||
// $ redis-server
|
||||
|
||||
var express = require('../..');
|
||||
var session = require('express-session');
|
||||
|
||||
var app = express();
|
||||
|
||||
app.use(express.logger('dev'));
|
||||
|
||||
// Required by session() middleware
|
||||
// pass the secret for signed cookies
|
||||
// (required by session())
|
||||
app.use(express.cookieParser('keyboard cat'));
|
||||
|
||||
// Populates req.session
|
||||
app.use(express.session());
|
||||
app.use(session({
|
||||
resave: false, // don't save session if unmodified
|
||||
saveUninitialized: false, // don't create session until something stored
|
||||
secret: 'keyboard cat'
|
||||
}));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
var body = '';
|
||||
@@ -28,5 +25,8 @@ app.get('/', function(req, res){
|
||||
res.send(body + '<p>viewed <strong>' + req.session.views + '</strong> times.</p>');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var session = require('express-session');
|
||||
|
||||
// pass the express to the connect redis module
|
||||
// allowing it to inherit from express.session.Store
|
||||
var RedisStore = require('connect-redis')(express);
|
||||
// allowing it to inherit from session.Store
|
||||
var RedisStore = require('connect-redis')(session);
|
||||
|
||||
var app = express();
|
||||
|
||||
app.use(express.logger('dev'));
|
||||
|
||||
// Required by session() middleware
|
||||
// pass the secret for signed cookies
|
||||
// (required by session())
|
||||
app.use(express.cookieParser('keyboard cat'));
|
||||
app.use(logger('dev'));
|
||||
|
||||
// Populates req.session
|
||||
app.use(express.session({ store: new RedisStore }));
|
||||
app.use(session({
|
||||
resave: false, // don't save session if unmodified
|
||||
saveUninitialized: false, // don't create session until something stored
|
||||
secret: 'keyboard cat',
|
||||
store: new RedisStore
|
||||
}));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
var body = '';
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var app = express();
|
||||
|
||||
// log requests
|
||||
app.use(express.logger('dev'));
|
||||
app.use(logger('dev'));
|
||||
|
||||
// express on its own has no notion
|
||||
// of a "file". The express.static()
|
||||
@@ -28,17 +32,9 @@ app.use('/static', express.static(__dirname + '/public'));
|
||||
// this will allow "GET /style.css" instead of "GET /css/style.css":
|
||||
app.use(express.static(__dirname + '/public/css'));
|
||||
|
||||
// this examples does not have any routes, however
|
||||
// you may `app.use(app.router)` before or after these
|
||||
// static() middleware. If placed before them your routes
|
||||
// will be matched BEFORE file serving takes place. If placed
|
||||
// after as shown here then file serving is performed BEFORE
|
||||
// any routes are hit:
|
||||
app.use(app.router);
|
||||
|
||||
app.listen(3000);
|
||||
console.log('listening on port 3000');
|
||||
console.log('try:');
|
||||
console.log(' GET /hello.txt');
|
||||
console.log(' GET /js/app.js');
|
||||
console.log(' GET /css/style.css');
|
||||
console.log(' GET /css/style.css');
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var vhost = require('vhost');
|
||||
|
||||
/*
|
||||
edit /etc/hosts:
|
||||
@@ -16,31 +18,34 @@ edit /etc/hosts:
|
||||
|
||||
var main = express();
|
||||
|
||||
main.use(express.logger('dev'));
|
||||
if (!module.parent) main.use(logger('dev'));
|
||||
|
||||
main.get('/', function(req, res){
|
||||
res.send('Hello from main app!')
|
||||
res.send('Hello from main app!');
|
||||
});
|
||||
|
||||
main.get('/:sub', function(req, res){
|
||||
res.send('requsted ' + req.params.sub);
|
||||
res.send('requested ' + req.params.sub);
|
||||
});
|
||||
|
||||
// Redirect app
|
||||
|
||||
var redirect = express();
|
||||
|
||||
redirect.all('*', function(req, res){
|
||||
console.log(req.subdomains);
|
||||
res.redirect('http://example.com:3000/' + req.subdomains[0]);
|
||||
redirect.use(function(req, res){
|
||||
if (!module.parent) console.log(req.vhost);
|
||||
res.redirect('http://example.com:3000/' + req.vhost[0]);
|
||||
});
|
||||
|
||||
// Vhost app
|
||||
|
||||
var app = express();
|
||||
var app = module.exports = express();
|
||||
|
||||
app.use(express.vhost('*.example.com', redirect)) // Serves all subdomains via Redirect app
|
||||
app.use(express.vhost('example.com', main)); // Serves top level domain via Main server app
|
||||
app.use(vhost('*.example.com', redirect)); // Serves all subdomains via Redirect app
|
||||
app.use(vhost('example.com', main)); // Serves top level domain via Main server app
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var http = require('http')
|
||||
, path = require('path')
|
||||
, extname = path.extname
|
||||
var http = require('http');
|
||||
var path = require('path');
|
||||
var extname = path.extname;
|
||||
|
||||
/**
|
||||
* Expose `GithubView`.
|
||||
@@ -35,13 +34,13 @@ function GithubView(name, options){
|
||||
GithubView.prototype.render = function(options, fn){
|
||||
var self = this;
|
||||
var opts = {
|
||||
host: 'rawgithub.com',
|
||||
port: 80,
|
||||
host: 'raw.githubusercontent.com',
|
||||
port: 443,
|
||||
path: this.path,
|
||||
method: 'GET'
|
||||
};
|
||||
|
||||
http.request(opts, function(res) {
|
||||
https.request(opts, function(res) {
|
||||
var buf = '';
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', function(str){ buf += str });
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, http = require('http')
|
||||
, GithubView = require('./github-view')
|
||||
, md = require('marked').parse;
|
||||
var express = require('../../');
|
||||
var http = require('http');
|
||||
var GithubView = require('./github-view');
|
||||
var md = require('marked').parse;
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
@@ -16,15 +15,15 @@ app.engine('md', function(str, options, fn){
|
||||
var html = md(str);
|
||||
html = html.replace(/\{([^}]+)\}/g, function(_, name){
|
||||
return options[name] || '';
|
||||
})
|
||||
});
|
||||
fn(null, html);
|
||||
} catch(err) {
|
||||
fn(err);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// pointing to a particular github repo to load files from it
|
||||
app.set('views', 'visionmedia/express');
|
||||
app.set('views', 'strongloop/express');
|
||||
|
||||
// register a new view constructor
|
||||
app.set('view', GithubView);
|
||||
@@ -34,13 +33,14 @@ app.get('/', function(req, res){
|
||||
// app.locals, res.locals, and locals passed
|
||||
// work like they normally would
|
||||
res.render('examples/markdown/views/index.md', { title: 'Example' });
|
||||
})
|
||||
});
|
||||
|
||||
app.get('/Readme.md', function(req, res){
|
||||
// rendering a view from https://github.com/visionmedia/express/blob/master/Readme.md
|
||||
// rendering a view from https://github.com/strongloop/express/blob/master/Readme.md
|
||||
res.render('Readme.md');
|
||||
})
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..')
|
||||
, User = require('./user')
|
||||
, app = express();
|
||||
var express = require('../..');
|
||||
var User = require('./user');
|
||||
var app = express();
|
||||
|
||||
app.set('views', __dirname);
|
||||
app.set('view engine', 'jade');
|
||||
@@ -69,7 +72,7 @@ app.get('/middleware', count, users, function(req, res, next){
|
||||
// this approach is much like the last
|
||||
// however we're explicitly exposing
|
||||
// the locals within each middleware
|
||||
//
|
||||
//
|
||||
// note that this may not always work
|
||||
// well, for example here we filter
|
||||
// the users in the middleware, which
|
||||
@@ -142,5 +145,8 @@ app.all('/api/*', function(req, res, next){
|
||||
|
||||
*/
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Application listening on port 3000');
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
doctype 5
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
title= title
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
module.exports = User;
|
||||
|
||||
// faux model
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -29,7 +28,7 @@ function error(status, msg) {
|
||||
app.use('/api', function(req, res, next){
|
||||
var key = req.query['api-key'];
|
||||
|
||||
// key isnt present
|
||||
// key isn't present
|
||||
if (!key) return next(error(400, 'api key required'));
|
||||
|
||||
// key is invalid
|
||||
@@ -40,29 +39,6 @@ app.use('/api', function(req, res, next){
|
||||
next();
|
||||
});
|
||||
|
||||
// position our routes above the error handling middleware,
|
||||
// and below our API middleware, since we want the API validation
|
||||
// to take place BEFORE our routes
|
||||
app.use(app.router);
|
||||
|
||||
// middleware with an arity of 4 are considered
|
||||
// error handling middleware. When you next(err)
|
||||
// it will be passed through the defined middleware
|
||||
// in order, but ONLY those with an arity of 4, ignoring
|
||||
// regular middleware.
|
||||
app.use(function(err, req, res, next){
|
||||
// whatever you want here, feel free to populate
|
||||
// properties on `err` to treat it differently in here.
|
||||
res.send(err.status || 500, { error: err.message });
|
||||
});
|
||||
|
||||
// our custom JSON 404 middleware. Since it's placed last
|
||||
// it will be the last middleware called, if all others
|
||||
// invoke next() and do not respond.
|
||||
app.use(function(req, res){
|
||||
res.send(404, { error: "Lame, can't find that" });
|
||||
});
|
||||
|
||||
// map of valid api keys, typically mapped to
|
||||
// account info with some sort of database like redis.
|
||||
// api keys do _not_ serve as authentication, merely to
|
||||
@@ -73,7 +49,7 @@ var apiKeys = ['foo', 'bar', 'baz'];
|
||||
// these two objects will serve as our faux database
|
||||
|
||||
var repos = [
|
||||
{ name: 'express', url: 'http://github.com/visionmedia/express' }
|
||||
{ name: 'express', url: 'http://github.com/strongloop/express' }
|
||||
, { name: 'stylus', url: 'http://github.com/learnboost/stylus' }
|
||||
, { name: 'cluster', url: 'http://github.com/learnboost/cluster' }
|
||||
];
|
||||
@@ -102,14 +78,35 @@ app.get('/api/repos', function(req, res, next){
|
||||
});
|
||||
|
||||
app.get('/api/user/:name/repos', function(req, res, next){
|
||||
var name = req.params.name
|
||||
, user = userRepos[name];
|
||||
|
||||
var name = req.params.name;
|
||||
var user = userRepos[name];
|
||||
|
||||
if (user) res.send(user);
|
||||
else next();
|
||||
});
|
||||
|
||||
// middleware with an arity of 4 are considered
|
||||
// error handling middleware. When you next(err)
|
||||
// it will be passed through the defined middleware
|
||||
// in order, but ONLY those with an arity of 4, ignoring
|
||||
// regular middleware.
|
||||
app.use(function(err, req, res, next){
|
||||
// whatever you want here, feel free to populate
|
||||
// properties on `err` to treat it differently in here.
|
||||
res.status(err.status || 500);
|
||||
res.send({ error: err.message });
|
||||
});
|
||||
|
||||
// our custom JSON 404 middleware. Since it's placed last
|
||||
// it will be the last middleware called, if all others
|
||||
// invoke next() and do not respond.
|
||||
app.use(function(req, res){
|
||||
res.status(404);
|
||||
res.send({ error: "Lame, can't find that" });
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express server listening on port 3000');
|
||||
}
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
|
||||
4
index.js
4
index.js
@@ -1,4 +1,2 @@
|
||||
|
||||
module.exports = process.env.EXPRESS_COV
|
||||
? require('./lib-cov/express')
|
||||
: require('./lib/express');
|
||||
module.exports = require('./lib/express');
|
||||
|
||||
@@ -2,17 +2,22 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var connect = require('connect')
|
||||
, Router = require('./router')
|
||||
, methods = require('methods')
|
||||
, middleware = require('./middleware')
|
||||
, debug = require('debug')('express:application')
|
||||
, locals = require('./utils').locals
|
||||
, View = require('./view')
|
||||
, utils = connect.utils
|
||||
, path = require('path')
|
||||
, http = require('http')
|
||||
, join = path.join;
|
||||
var finalhandler = require('finalhandler');
|
||||
var flatten = require('./utils').flatten;
|
||||
var Router = require('./router');
|
||||
var methods = require('methods');
|
||||
var middleware = require('./middleware/init');
|
||||
var query = require('./middleware/query');
|
||||
var debug = require('debug')('express:application');
|
||||
var View = require('./view');
|
||||
var http = require('http');
|
||||
var compileETag = require('./utils').compileETag;
|
||||
var compileQueryParser = require('./utils').compileQueryParser;
|
||||
var compileTrust = require('./utils').compileTrust;
|
||||
var deprecate = require('depd')('express');
|
||||
var merge = require('utils-merge');
|
||||
var resolve = require('path').resolve;
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
/**
|
||||
* Application prototype.
|
||||
@@ -46,13 +51,14 @@ app.init = function(){
|
||||
app.defaultConfiguration = function(){
|
||||
// default settings
|
||||
this.enable('x-powered-by');
|
||||
this.set('env', process.env.NODE_ENV || 'development');
|
||||
this.set('etag', 'weak');
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
this.set('env', env);
|
||||
this.set('query parser', 'extended');
|
||||
this.set('subdomain offset', 2);
|
||||
debug('booting in %s mode', this.get('env'));
|
||||
this.set('trust proxy', false);
|
||||
|
||||
// implicit middleware
|
||||
this.use(connect.query());
|
||||
this.use(middleware.init(this));
|
||||
debug('booting in %s mode', env);
|
||||
|
||||
// inherit protos
|
||||
this.on('mount', function(parent){
|
||||
@@ -62,79 +68,161 @@ app.defaultConfiguration = function(){
|
||||
this.settings.__proto__ = parent.settings;
|
||||
});
|
||||
|
||||
// router
|
||||
this._router = new Router(this);
|
||||
this.routes = this._router.map;
|
||||
this.__defineGetter__('router', function(){
|
||||
this._usedRouter = true;
|
||||
this._router.caseSensitive = this.enabled('case sensitive routing');
|
||||
this._router.strict = this.enabled('strict routing');
|
||||
return this._router.middleware;
|
||||
});
|
||||
|
||||
// setup locals
|
||||
this.locals = locals(this);
|
||||
this.locals = Object.create(null);
|
||||
|
||||
// top-most app is mounted at /
|
||||
this.mountpath = '/';
|
||||
|
||||
// default locals
|
||||
this.locals.settings = this.settings;
|
||||
|
||||
// default configuration
|
||||
this.set('view', View);
|
||||
this.set('views', process.cwd() + '/views');
|
||||
this.set('views', resolve('views'));
|
||||
this.set('jsonp callback name', 'callback');
|
||||
|
||||
this.configure('development', function(){
|
||||
this.set('json spaces', 2);
|
||||
});
|
||||
|
||||
this.configure('production', function(){
|
||||
if (env === 'production') {
|
||||
this.enable('view cache');
|
||||
}
|
||||
|
||||
Object.defineProperty(this, 'router', {
|
||||
get: function() {
|
||||
throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Proxy `connect#use()` to apply settings to
|
||||
* mounted applications.
|
||||
* lazily adds the base router if it has not yet been added.
|
||||
*
|
||||
* We cannot add the base router in the defaultConfiguration because
|
||||
* it reads app settings which might be set after that has run.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
app.lazyrouter = function() {
|
||||
if (!this._router) {
|
||||
this._router = new Router({
|
||||
caseSensitive: this.enabled('case sensitive routing'),
|
||||
strict: this.enabled('strict routing')
|
||||
});
|
||||
|
||||
this._router.use(query(this.get('query parser fn')));
|
||||
this._router.use(middleware.init(this));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispatch a req, res pair into the application. Starts pipeline processing.
|
||||
*
|
||||
* If no _done_ callback is provided, then default error handlers will respond
|
||||
* in the event of an error bubbling through the stack.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
app.handle = function(req, res, done) {
|
||||
var router = this._router;
|
||||
|
||||
// final handler
|
||||
done = done || finalhandler(req, res, {
|
||||
env: this.get('env'),
|
||||
onerror: logerror.bind(this)
|
||||
});
|
||||
|
||||
// no routes
|
||||
if (!router) {
|
||||
debug('no routes defined on app');
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
router.handle(req, res, done);
|
||||
};
|
||||
|
||||
/**
|
||||
* Proxy `Router#use()` to add middleware to the app router.
|
||||
* See Router#use() documentation for details.
|
||||
*
|
||||
* If the _fn_ parameter is an express app, then it will be
|
||||
* mounted at the _route_ specified.
|
||||
*
|
||||
* @param {String|Function|Server} route
|
||||
* @param {Function|Server} fn
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.use = function(route, fn){
|
||||
var app;
|
||||
app.use = function use(fn) {
|
||||
var offset = 0;
|
||||
var path = '/';
|
||||
|
||||
// default route to '/'
|
||||
if ('string' != typeof route) fn = route, route = '/';
|
||||
// default path to '/'
|
||||
// disambiguate app.use([fn])
|
||||
if (typeof fn !== 'function') {
|
||||
var arg = fn;
|
||||
|
||||
// express app
|
||||
if (fn.handle && fn.set) app = fn;
|
||||
while (Array.isArray(arg) && arg.length !== 0) {
|
||||
arg = arg[0];
|
||||
}
|
||||
|
||||
// restore .app property on req and res
|
||||
if (app) {
|
||||
app.route = route;
|
||||
fn = function(req, res, next) {
|
||||
// first arg is the path
|
||||
if (typeof arg !== 'function') {
|
||||
offset = 1;
|
||||
path = fn;
|
||||
}
|
||||
}
|
||||
|
||||
var fns = flatten(slice.call(arguments, offset));
|
||||
|
||||
if (fns.length === 0) {
|
||||
throw new TypeError('app.use() requires middleware functions');
|
||||
}
|
||||
|
||||
// setup router
|
||||
this.lazyrouter();
|
||||
var router = this._router;
|
||||
|
||||
fns.forEach(function (fn) {
|
||||
// non-express app
|
||||
if (!fn || !fn.handle || !fn.set) {
|
||||
return router.use(path, fn);
|
||||
}
|
||||
|
||||
debug('.use app under %s', path);
|
||||
fn.mountpath = path;
|
||||
fn.parent = this;
|
||||
|
||||
// restore .app property on req and res
|
||||
router.use(path, function mounted_app(req, res, next) {
|
||||
var orig = req.app;
|
||||
app.handle(req, res, function(err){
|
||||
fn.handle(req, res, function (err) {
|
||||
req.__proto__ = orig.request;
|
||||
res.__proto__ = orig.response;
|
||||
next(err);
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
connect.proto.use.call(this, route, fn);
|
||||
|
||||
// mounted an app
|
||||
if (app) {
|
||||
app.parent = this;
|
||||
app.emit('mount', this);
|
||||
}
|
||||
// mounted an app
|
||||
fn.emit('mount', this);
|
||||
}, this);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Proxy to the app `Router#route()`
|
||||
* Returns a new `Route` instance for the _path_.
|
||||
*
|
||||
* Routes are isolated middleware stacks for specific paths.
|
||||
* See the Route api docs for details.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.route = function(path){
|
||||
this.lazyrouter();
|
||||
return this._router.route(path);
|
||||
};
|
||||
|
||||
/**
|
||||
* Register the given template engine callback `fn`
|
||||
* as `ext`.
|
||||
@@ -158,7 +246,7 @@ app.use = function(route, fn){
|
||||
* 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)
|
||||
* [Consolidate.js](https://github.com/tj/consolidate.js)
|
||||
* library was created to map all of node's popular template
|
||||
* engines to follow this convention, thus allowing them to
|
||||
* work seamlessly within Express.
|
||||
@@ -177,30 +265,10 @@ app.engine = function(ext, fn){
|
||||
};
|
||||
|
||||
/**
|
||||
* Map the given param placeholder `name`(s) to the given callback(s).
|
||||
* Proxy to `Router#param()` with one added api feature. The _name_ parameter
|
||||
* can be an array of names.
|
||||
*
|
||||
* 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'));
|
||||
* }
|
||||
* });
|
||||
* });
|
||||
* See the Router#param() docs for more details.
|
||||
*
|
||||
* @param {String|Array} name
|
||||
* @param {Function} fn
|
||||
@@ -209,27 +277,16 @@ app.engine = function(ext, fn){
|
||||
*/
|
||||
|
||||
app.param = function(name, fn){
|
||||
var self = this
|
||||
, fns = [].slice.call(arguments, 1);
|
||||
this.lazyrouter();
|
||||
|
||||
// array
|
||||
if (Array.isArray(name)) {
|
||||
name.forEach(function(name){
|
||||
fns.forEach(function(fn){
|
||||
self.param(name, fn);
|
||||
});
|
||||
});
|
||||
// param logic
|
||||
} else if ('function' == typeof name) {
|
||||
this._router.param(name);
|
||||
// single
|
||||
} else {
|
||||
if (':' == name[0]) name = name.substr(1);
|
||||
fns.forEach(function(fn){
|
||||
self._router.param(name, fn);
|
||||
});
|
||||
name.forEach(function(key) {
|
||||
this.param(key, fn);
|
||||
}, this);
|
||||
return this;
|
||||
}
|
||||
|
||||
this._router.param(name, fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -243,18 +300,37 @@ app.param = function(name, fn){
|
||||
* Mounted servers inherit their parent server's settings.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @param {String} val
|
||||
* @param {*} [val]
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.set = function(setting, val){
|
||||
if (1 == arguments.length) {
|
||||
if (arguments.length === 1) {
|
||||
// app.get(setting)
|
||||
return this.settings[setting];
|
||||
} else {
|
||||
this.settings[setting] = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
// set value
|
||||
this.settings[setting] = val;
|
||||
|
||||
// trigger matched settings
|
||||
switch (setting) {
|
||||
case 'etag':
|
||||
debug('compile etag %s', val);
|
||||
this.set('etag fn', compileETag(val));
|
||||
break;
|
||||
case 'query parser':
|
||||
debug('compile query parser %s', val);
|
||||
this.set('query parser fn', compileQueryParser(val));
|
||||
break;
|
||||
case 'trust proxy':
|
||||
debug('compile trust proxy %s', val);
|
||||
this.set('trust proxy fn', compileTrust(val));
|
||||
break;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -273,7 +349,7 @@ app.set = function(setting, val){
|
||||
|
||||
app.path = function(){
|
||||
return this.parent
|
||||
? this.parent.path() + this.route
|
||||
? this.parent.path() + this.mountpath
|
||||
: '';
|
||||
};
|
||||
|
||||
@@ -339,60 +415,6 @@ app.disable = function(setting){
|
||||
return this.set(setting, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
* }
|
||||
*
|
||||
* @param {String} env...
|
||||
* @param {Function} fn
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.configure = function(env, fn){
|
||||
var envs = 'all'
|
||||
, args = [].slice.call(arguments);
|
||||
fn = args.pop();
|
||||
if (args.length) envs = args;
|
||||
if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delegate `.VERB(...)` calls to `router.VERB(...)`.
|
||||
*/
|
||||
@@ -401,16 +423,10 @@ methods.forEach(function(method){
|
||||
app[method] = function(path){
|
||||
if ('get' == method && 1 == arguments.length) return this.set(path);
|
||||
|
||||
// deprecated
|
||||
if (Array.isArray(path)) {
|
||||
console.trace('passing an array to app.VERB() is deprecated and will be removed in 4.0');
|
||||
}
|
||||
this.lazyrouter();
|
||||
|
||||
// if no router attached yet, attach the router
|
||||
if (!this._usedRouter) this.use(this.router);
|
||||
|
||||
// setup route
|
||||
this._router[method].apply(this._router, arguments);
|
||||
var route = this._router.route(path);
|
||||
route[method].apply(route, slice.call(arguments, 1));
|
||||
return this;
|
||||
};
|
||||
});
|
||||
@@ -426,16 +442,20 @@ methods.forEach(function(method){
|
||||
*/
|
||||
|
||||
app.all = function(path){
|
||||
var args = arguments;
|
||||
this.lazyrouter();
|
||||
|
||||
var route = this._router.route(path);
|
||||
var args = slice.call(arguments, 1);
|
||||
methods.forEach(function(method){
|
||||
app[method].apply(this, args);
|
||||
}, this);
|
||||
route[method].apply(route, args);
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// del -> delete alias
|
||||
|
||||
app.del = app.delete;
|
||||
app.del = deprecate.function(app.delete, 'app.del: Use app.delete instead');
|
||||
|
||||
/**
|
||||
* Render the given view `name` name with `options`
|
||||
@@ -455,10 +475,10 @@ app.del = app.delete;
|
||||
*/
|
||||
|
||||
app.render = function(name, options, fn){
|
||||
var opts = {}
|
||||
, cache = this.cache
|
||||
, engines = this.engines
|
||||
, view;
|
||||
var opts = {};
|
||||
var cache = this.cache;
|
||||
var engines = this.engines;
|
||||
var view;
|
||||
|
||||
// support callback function as second arg
|
||||
if ('function' == typeof options) {
|
||||
@@ -466,13 +486,15 @@ app.render = function(name, options, fn){
|
||||
}
|
||||
|
||||
// merge app.locals
|
||||
utils.merge(opts, this.locals);
|
||||
merge(opts, this.locals);
|
||||
|
||||
// merge options._locals
|
||||
if (options._locals) utils.merge(opts, options._locals);
|
||||
if (options._locals) {
|
||||
merge(opts, options._locals);
|
||||
}
|
||||
|
||||
// merge options
|
||||
utils.merge(opts, options);
|
||||
merge(opts, options);
|
||||
|
||||
// set .cache unless explicitly provided
|
||||
opts.cache = null == opts.cache
|
||||
@@ -491,7 +513,10 @@ app.render = function(name, options, fn){
|
||||
});
|
||||
|
||||
if (!view.path) {
|
||||
var err = new Error('Failed to lookup view "' + name + '"');
|
||||
var dirs = Array.isArray(view.root) && view.root.length > 1
|
||||
? 'directories "' + view.root.slice(0, -1).join('", "') + '" or "' + view.root[view.root.length - 1] + '"'
|
||||
: 'directory "' + view.root + '"'
|
||||
var err = new Error('Failed to lookup view "' + name + '" in views ' + dirs);
|
||||
err.view = view;
|
||||
return fn(err);
|
||||
}
|
||||
@@ -533,3 +558,14 @@ app.listen = function(){
|
||||
var server = http.createServer(this);
|
||||
return server.listen.apply(server, arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
* Log error using console.error.
|
||||
*
|
||||
* @param {Error} err
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function logerror(err){
|
||||
if (this.get('env') !== 'test') console.error(err.stack || err.toString());
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var connect = require('connect')
|
||||
, proto = require('./application')
|
||||
, Route = require('./router/route')
|
||||
, Router = require('./router')
|
||||
, req = require('./request')
|
||||
, res = require('./response')
|
||||
, utils = connect.utils;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var mixin = require('merge-descriptors');
|
||||
var proto = require('./application');
|
||||
var Route = require('./router/route');
|
||||
var Router = require('./router');
|
||||
var req = require('./request');
|
||||
var res = require('./response');
|
||||
|
||||
/**
|
||||
* Expose `createApplication()`.
|
||||
@@ -16,12 +16,6 @@ var connect = require('connect')
|
||||
|
||||
exports = module.exports = createApplication;
|
||||
|
||||
/**
|
||||
* Expose mime.
|
||||
*/
|
||||
|
||||
exports.mime = connect.mime;
|
||||
|
||||
/**
|
||||
* Create an express application.
|
||||
*
|
||||
@@ -30,41 +24,19 @@ exports.mime = connect.mime;
|
||||
*/
|
||||
|
||||
function createApplication() {
|
||||
var app = connect();
|
||||
utils.merge(app, proto);
|
||||
var app = function(req, res, next) {
|
||||
app.handle(req, res, next);
|
||||
};
|
||||
|
||||
mixin(app, proto);
|
||||
mixin(app, EventEmitter.prototype);
|
||||
|
||||
app.request = { __proto__: req, app: app };
|
||||
app.response = { __proto__: res, app: app };
|
||||
app.init();
|
||||
return app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose connect.middleware as express.*
|
||||
* for example `express.logger` etc.
|
||||
*/
|
||||
|
||||
for (var key in connect.middleware) {
|
||||
Object.defineProperty(
|
||||
exports
|
||||
, key
|
||||
, Object.getOwnPropertyDescriptor(connect.middleware, key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Error on createServer().
|
||||
*/
|
||||
|
||||
exports.createServer = function(){
|
||||
console.warn('Warning: express.createServer() is deprecated, express');
|
||||
console.warn('applications no longer inherit from http.Server,');
|
||||
console.warn('please use:');
|
||||
console.warn('');
|
||||
console.warn(' var express = require("express");');
|
||||
console.warn(' var app = express();');
|
||||
console.warn('');
|
||||
return createApplication();
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose the prototypes.
|
||||
*/
|
||||
@@ -80,7 +52,42 @@ exports.response = res;
|
||||
exports.Route = Route;
|
||||
exports.Router = Router;
|
||||
|
||||
// Error handler title
|
||||
/**
|
||||
* Expose middleware
|
||||
*/
|
||||
|
||||
exports.errorHandler.title = 'Express';
|
||||
exports.query = require('./middleware/query');
|
||||
exports.static = require('serve-static');
|
||||
|
||||
/**
|
||||
* Replace removed middleware with an appropriate error message.
|
||||
*/
|
||||
|
||||
[
|
||||
'json',
|
||||
'urlencoded',
|
||||
'bodyParser',
|
||||
'compress',
|
||||
'cookieSession',
|
||||
'session',
|
||||
'logger',
|
||||
'cookieParser',
|
||||
'favicon',
|
||||
'responseTime',
|
||||
'errorHandler',
|
||||
'timeout',
|
||||
'methodOverride',
|
||||
'vhost',
|
||||
'csrf',
|
||||
'directory',
|
||||
'limit',
|
||||
'multipart',
|
||||
'staticCache',
|
||||
].forEach(function (name) {
|
||||
Object.defineProperty(exports, name, {
|
||||
get: function () {
|
||||
throw new Error('Most middleware (like ' + name + ') is no longer bundled with Express and must be installed separately. Please see https://github.com/senchalabs/connect#middleware.');
|
||||
},
|
||||
configurable: true
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('./utils');
|
||||
|
||||
/**
|
||||
* Initialization middleware, exposing the
|
||||
* request and response to eachother, as well
|
||||
* request and response to each other, as well
|
||||
* as defaulting the X-Powered-By header field.
|
||||
*
|
||||
* @param {Function} app
|
||||
@@ -25,8 +18,9 @@ exports.init = function(app){
|
||||
req.__proto__ = app.request;
|
||||
res.__proto__ = app.response;
|
||||
|
||||
res.locals = res.locals || utils.locals(res);
|
||||
res.locals = res.locals || Object.create(null);
|
||||
|
||||
next();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
30
lib/middleware/query.js
Normal file
30
lib/middleware/query.js
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var parseUrl = require('parseurl');
|
||||
var qs = require('qs');
|
||||
|
||||
/**
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function query(options) {
|
||||
var queryparse = qs.parse;
|
||||
|
||||
if (typeof options === 'function') {
|
||||
queryparse = options;
|
||||
options = undefined;
|
||||
}
|
||||
|
||||
return function query(req, res, next){
|
||||
if (!req.query) {
|
||||
var val = parseUrl(req).query;
|
||||
req.query = queryparse(val, options);
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
};
|
||||
351
lib/request.js
351
lib/request.js
@@ -1,15 +1,16 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var http = require('http')
|
||||
, utils = require('./utils')
|
||||
, connect = require('connect')
|
||||
, fresh = require('fresh')
|
||||
, parseRange = require('range-parser')
|
||||
, parse = connect.utils.parseUrl
|
||||
, mime = connect.mime;
|
||||
var accepts = require('accepts');
|
||||
var deprecate = require('depd')('express');
|
||||
var isIP = require('net').isIP;
|
||||
var typeis = require('type-is');
|
||||
var http = require('http');
|
||||
var fresh = require('fresh');
|
||||
var parseRange = require('range-parser');
|
||||
var parse = require('parseurl');
|
||||
var proxyaddr = require('proxy-addr');
|
||||
|
||||
/**
|
||||
* Request prototype.
|
||||
@@ -56,6 +57,8 @@ req.header = function(name){
|
||||
};
|
||||
|
||||
/**
|
||||
* To do: update docs.
|
||||
*
|
||||
* 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".
|
||||
@@ -63,6 +66,7 @@ req.header = function(name){
|
||||
* 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",
|
||||
* an argument 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.
|
||||
*
|
||||
@@ -89,6 +93,7 @@ req.header = function(name){
|
||||
*
|
||||
* // Accept: text/*;q=.5, application/json
|
||||
* req.accepts(['html', 'json']);
|
||||
* req.accepts('html', 'json');
|
||||
* req.accepts('html, json');
|
||||
* // => "json"
|
||||
*
|
||||
@@ -97,54 +102,61 @@ req.header = function(name){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.accepts = function(type){
|
||||
return utils.accepts(type, this.get('Accept'));
|
||||
req.accepts = function(){
|
||||
var accept = accepts(this);
|
||||
return accept.types.apply(accept, arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the given `encoding` is accepted.
|
||||
* Check if the given `encoding`s are accepted.
|
||||
*
|
||||
* @param {String} encoding
|
||||
* @param {String} ...encoding
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.acceptsEncoding = function(encoding){
|
||||
return !! ~this.acceptedEncodings.indexOf(encoding);
|
||||
req.acceptsEncodings = function(){
|
||||
var accept = accepts(this);
|
||||
return accept.encodings.apply(accept, arguments);
|
||||
};
|
||||
|
||||
req.acceptsEncoding = deprecate.function(req.acceptsEncodings,
|
||||
'req.acceptsEncoding: Use acceptsEncodings instead');
|
||||
|
||||
/**
|
||||
* Check if the given `charset` is acceptable,
|
||||
* Check if the given `charset`s are acceptable,
|
||||
* otherwise you should respond with 406 "Not Acceptable".
|
||||
*
|
||||
* @param {String} charset
|
||||
* @param {String} ...charset
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.acceptsCharset = function(charset){
|
||||
var accepted = this.acceptedCharsets;
|
||||
return accepted.length
|
||||
? !! ~accepted.indexOf(charset)
|
||||
: true;
|
||||
req.acceptsCharsets = function(){
|
||||
var accept = accepts(this);
|
||||
return accept.charsets.apply(accept, arguments);
|
||||
};
|
||||
|
||||
req.acceptsCharset = deprecate.function(req.acceptsCharsets,
|
||||
'req.acceptsCharset: Use acceptsCharsets instead');
|
||||
|
||||
/**
|
||||
* Check if the given `lang` is acceptable,
|
||||
* Check if the given `lang`s are acceptable,
|
||||
* otherwise you should respond with 406 "Not Acceptable".
|
||||
*
|
||||
* @param {String} lang
|
||||
* @param {String} ...lang
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.acceptsLanguage = function(lang){
|
||||
var accepted = this.acceptedLanguages;
|
||||
return accepted.length
|
||||
? !! ~accepted.indexOf(lang)
|
||||
: true;
|
||||
req.acceptsLanguages = function(){
|
||||
var accept = accepts(this);
|
||||
return accept.languages.apply(accept, arguments);
|
||||
};
|
||||
|
||||
req.acceptsLanguage = deprecate.function(req.acceptsLanguages,
|
||||
'req.acceptsLanguage: Use acceptsLanguages instead');
|
||||
|
||||
/**
|
||||
* Parse Range header field,
|
||||
* capping to the given `size`.
|
||||
@@ -171,98 +183,6 @@ req.range = function(size){
|
||||
return parseRange(size, range);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return an array of encodings.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* ['gzip', 'deflate']
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('acceptedEncodings', function(){
|
||||
var accept = this.get('Accept-Encoding');
|
||||
return accept
|
||||
? accept.trim().split(/ *, */)
|
||||
: [];
|
||||
});
|
||||
|
||||
/**
|
||||
* Return an array of Accepted media types
|
||||
* ordered from highest quality to lowest.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* [ { value: 'application/json',
|
||||
* quality: 1,
|
||||
* type: 'application',
|
||||
* subtype: 'json' },
|
||||
* { value: 'text/html',
|
||||
* quality: 0.5,
|
||||
* type: 'text',
|
||||
* subtype: 'html' } ]
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('accepted', function(){
|
||||
var accept = this.get('Accept');
|
||||
return accept
|
||||
? utils.parseAccept(accept)
|
||||
: [];
|
||||
});
|
||||
|
||||
/**
|
||||
* Return an array of Accepted languages
|
||||
* ordered from highest quality to lowest.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* Accept-Language: en;q=.5, en-us
|
||||
* ['en-us', 'en']
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('acceptedLanguages', function(){
|
||||
var accept = this.get('Accept-Language');
|
||||
return accept
|
||||
? utils
|
||||
.parseParams(accept)
|
||||
.map(function(obj){
|
||||
return obj.value;
|
||||
})
|
||||
: [];
|
||||
});
|
||||
|
||||
/**
|
||||
* Return an array of Accepted charsets
|
||||
* ordered from highest quality to lowest.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* Accept-Charset: iso-8859-5;q=.2, unicode-1-1;q=0.8
|
||||
* ['unicode-1-1', 'iso-8859-5']
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('acceptedCharsets', function(){
|
||||
var accept = this.get('Accept-Charset');
|
||||
return accept
|
||||
? utils
|
||||
.parseParams(accept)
|
||||
.map(function(obj){
|
||||
return obj.value;
|
||||
})
|
||||
: [];
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the value of param `name` when present or `defaultValue`.
|
||||
*
|
||||
@@ -272,7 +192,7 @@ req.__defineGetter__('acceptedCharsets', function(){
|
||||
*
|
||||
* To utilize request bodies, `req.body`
|
||||
* should be an object. This can be done by using
|
||||
* the `connect.bodyParser()` middleware.
|
||||
* the `bodyParser()` middleware.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Mixed} [defaultValue]
|
||||
@@ -280,13 +200,20 @@ req.__defineGetter__('acceptedCharsets', function(){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.param = function(name, defaultValue){
|
||||
req.param = function param(name, defaultValue) {
|
||||
var params = this.params || {};
|
||||
var body = this.body || {};
|
||||
var query = this.query || {};
|
||||
|
||||
var args = arguments.length === 1
|
||||
? 'name'
|
||||
: 'name, default';
|
||||
deprecate('req.param(' + args + '): Use req.params, req.body, or req.query instead');
|
||||
|
||||
if (null != params[name] && params.hasOwnProperty(name)) return params[name];
|
||||
if (null != body[name]) return body[name];
|
||||
if (null != query[name]) return query[name];
|
||||
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
@@ -316,38 +243,38 @@ req.param = function(name, defaultValue){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.is = function(type){
|
||||
var ct = this.get('Content-Type');
|
||||
if (!ct) return false;
|
||||
ct = ct.split(';')[0];
|
||||
if (!~type.indexOf('/')) type = mime.lookup(type);
|
||||
if (~type.indexOf('*')) {
|
||||
type = type.split('/');
|
||||
ct = ct.split('/');
|
||||
if ('*' == type[0] && type[1] == ct[1]) return true;
|
||||
if ('*' == type[1] && type[0] == ct[0]) return true;
|
||||
return false;
|
||||
}
|
||||
return !! ~ct.indexOf(type);
|
||||
req.is = function(types){
|
||||
if (!Array.isArray(types)) types = [].slice.call(arguments);
|
||||
return typeis(this, types);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the protocol string "http" or "https"
|
||||
* when requested with TLS. When the "trust proxy"
|
||||
* setting is enabled the "X-Forwarded-Proto" header
|
||||
* field will be trusted. If you're running behind
|
||||
* a reverse proxy that supplies https for you this
|
||||
* may be enabled.
|
||||
* setting trusts the socket address, the
|
||||
* "X-Forwarded-Proto" header field will be trusted
|
||||
* and used if present.
|
||||
*
|
||||
* If you're running behind a reverse proxy that
|
||||
* supplies https for you this may be enabled.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('protocol', function(){
|
||||
var trustProxy = this.app.get('trust proxy');
|
||||
if (this.connection.encrypted) return 'https';
|
||||
if (!trustProxy) return 'http';
|
||||
var proto = this.get('X-Forwarded-Proto') || 'http';
|
||||
defineGetter(req, 'protocol', function protocol(){
|
||||
var proto = this.connection.encrypted
|
||||
? 'https'
|
||||
: 'http';
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
|
||||
if (!trust(this.connection.remoteAddress)) {
|
||||
return proto;
|
||||
}
|
||||
|
||||
// Note: X-Forwarded-Proto is normally only ever a
|
||||
// single value, but this is to be safe.
|
||||
proto = this.get('X-Forwarded-Proto') || proto;
|
||||
return proto.split(/\s*,\s*/)[0];
|
||||
});
|
||||
|
||||
@@ -360,71 +287,41 @@ req.__defineGetter__('protocol', function(){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('secure', function(){
|
||||
defineGetter(req, 'secure', function secure(){
|
||||
return 'https' == this.protocol;
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the remote address, or when
|
||||
* "trust proxy" is `true` return
|
||||
* the upstream addr.
|
||||
* Return the remote address from the trusted proxy.
|
||||
*
|
||||
* The is the remote address on the socket unless
|
||||
* "trust proxy" is set.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('ip', function(){
|
||||
return this.ips[0] || this.connection.remoteAddress;
|
||||
defineGetter(req, 'ip', function ip(){
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
return proxyaddr(this, trust);
|
||||
});
|
||||
|
||||
/**
|
||||
* When "trust proxy" is `true`, parse
|
||||
* the "X-Forwarded-For" ip address list.
|
||||
* When "trust proxy" is set, trusted proxy addresses + client.
|
||||
*
|
||||
* For example if the value were "client, proxy1, proxy2"
|
||||
* you would receive the array `["client", "proxy1", "proxy2"]`
|
||||
* where "proxy2" is the furthest down-stream.
|
||||
* where "proxy2" is the furthest down-stream and "proxy1" and
|
||||
* "proxy2" were trusted.
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('ips', function(){
|
||||
var trustProxy = this.app.get('trust proxy');
|
||||
var val = this.get('X-Forwarded-For');
|
||||
return trustProxy && val
|
||||
? val.split(/ *, */)
|
||||
: [];
|
||||
});
|
||||
|
||||
/**
|
||||
* Return basic auth credentials.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* // http://tobi:hello@example.com
|
||||
* req.auth
|
||||
* // => { username: 'tobi', password: 'hello' }
|
||||
*
|
||||
* @return {Object} or undefined
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('auth', function(){
|
||||
// missing
|
||||
var auth = this.get('Authorization');
|
||||
if (!auth) return;
|
||||
|
||||
// malformed
|
||||
var parts = auth.split(' ');
|
||||
if ('basic' != parts[0].toLowerCase()) return;
|
||||
if (!parts[1]) return;
|
||||
auth = parts[1];
|
||||
|
||||
// credentials
|
||||
auth = new Buffer(auth, 'base64').toString().match(/^([^:]*):(.*)$/);
|
||||
if (!auth) return;
|
||||
return { username: auth[1], password: auth[2] };
|
||||
defineGetter(req, 'ips', function ips() {
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
var addrs = proxyaddr.all(this, trust);
|
||||
return addrs.slice(1).reverse();
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -442,12 +339,17 @@ req.__defineGetter__('auth', function(){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('subdomains', function(){
|
||||
defineGetter(req, 'subdomains', function subdomains() {
|
||||
var hostname = this.hostname;
|
||||
|
||||
if (!hostname) return [];
|
||||
|
||||
var offset = this.app.get('subdomain offset');
|
||||
return (this.host || '')
|
||||
.split('.')
|
||||
.reverse()
|
||||
.slice(offset);
|
||||
var subdomains = !isIP(hostname)
|
||||
? hostname.split('.').reverse()
|
||||
: [hostname];
|
||||
|
||||
return subdomains.slice(offset);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -457,25 +359,48 @@ req.__defineGetter__('subdomains', function(){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('path', function(){
|
||||
defineGetter(req, 'path', function path() {
|
||||
return parse(this).pathname;
|
||||
});
|
||||
|
||||
/**
|
||||
* Parse the "Host" header field hostname.
|
||||
* Parse the "Host" header field to a hostname.
|
||||
*
|
||||
* When the "trust proxy" setting trusts the socket
|
||||
* address, the "X-Forwarded-Host" header field will
|
||||
* be trusted.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('host', function(){
|
||||
var trustProxy = this.app.get('trust proxy');
|
||||
var host = trustProxy && this.get('X-Forwarded-Host');
|
||||
host = host || this.get('Host');
|
||||
defineGetter(req, 'hostname', function hostname(){
|
||||
var trust = this.app.get('trust proxy fn');
|
||||
var host = this.get('X-Forwarded-Host');
|
||||
|
||||
if (!host || !trust(this.connection.remoteAddress)) {
|
||||
host = this.get('Host');
|
||||
}
|
||||
|
||||
if (!host) return;
|
||||
return host.split(':')[0];
|
||||
|
||||
// IPv6 literal support
|
||||
var offset = host[0] === '['
|
||||
? host.indexOf(']') + 1
|
||||
: 0;
|
||||
var index = host.indexOf(':', offset);
|
||||
|
||||
return ~index
|
||||
? host.substring(0, index)
|
||||
: host;
|
||||
});
|
||||
|
||||
// TODO: change req.host to return host in next major
|
||||
|
||||
defineGetter(req, 'host', deprecate.function(function host(){
|
||||
return this.hostname;
|
||||
}, 'req.host: Use req.hostname instead'));
|
||||
|
||||
/**
|
||||
* Check if the request is fresh, aka
|
||||
* Last-Modified and/or the ETag
|
||||
@@ -485,7 +410,7 @@ req.__defineGetter__('host', function(){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('fresh', function(){
|
||||
defineGetter(req, 'fresh', function(){
|
||||
var method = this.method;
|
||||
var s = this.res.statusCode;
|
||||
|
||||
@@ -494,7 +419,7 @@ req.__defineGetter__('fresh', function(){
|
||||
|
||||
// 2xx or 304 as per rfc2616 14.26
|
||||
if ((s >= 200 && s < 300) || 304 == s) {
|
||||
return fresh(this.headers, this.res._headers);
|
||||
return fresh(this.headers, (this.res._headers || {}));
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -509,7 +434,7 @@ req.__defineGetter__('fresh', function(){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('stale', function(){
|
||||
defineGetter(req, 'stale', function stale(){
|
||||
return !this.fresh;
|
||||
});
|
||||
|
||||
@@ -520,7 +445,23 @@ req.__defineGetter__('stale', function(){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('xhr', function(){
|
||||
defineGetter(req, 'xhr', function xhr(){
|
||||
var val = this.get('X-Requested-With') || '';
|
||||
return 'xmlhttprequest' == val.toLowerCase();
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper function for creating a getter on an object.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @param {String} name
|
||||
* @param {Function} getter
|
||||
* @api private
|
||||
*/
|
||||
function defineGetter(obj, name, getter) {
|
||||
Object.defineProperty(obj, name, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: getter
|
||||
});
|
||||
};
|
||||
|
||||
707
lib/response.js
707
lib/response.js
@@ -2,21 +2,25 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var http = require('http')
|
||||
, path = require('path')
|
||||
, connect = require('connect')
|
||||
, utils = connect.utils
|
||||
, sign = require('cookie-signature').sign
|
||||
, normalizeType = require('./utils').normalizeType
|
||||
, normalizeTypes = require('./utils').normalizeTypes
|
||||
, etag = require('./utils').etag
|
||||
, statusCodes = http.STATUS_CODES
|
||||
, cookie = require('cookie')
|
||||
, send = require('send')
|
||||
, mime = connect.mime
|
||||
, basename = path.basename
|
||||
, extname = path.extname
|
||||
, join = path.join;
|
||||
var contentDisposition = require('content-disposition');
|
||||
var deprecate = require('depd')('express');
|
||||
var escapeHtml = require('escape-html');
|
||||
var http = require('http');
|
||||
var isAbsolute = require('./utils').isAbsolute;
|
||||
var onFinished = require('on-finished');
|
||||
var path = require('path');
|
||||
var merge = require('utils-merge');
|
||||
var sign = require('cookie-signature').sign;
|
||||
var normalizeType = require('./utils').normalizeType;
|
||||
var normalizeTypes = require('./utils').normalizeTypes;
|
||||
var setCharset = require('./utils').setCharset;
|
||||
var statusCodes = http.STATUS_CODES;
|
||||
var cookie = require('cookie');
|
||||
var send = require('send');
|
||||
var extname = path.extname;
|
||||
var mime = send.mime;
|
||||
var resolve = path.resolve;
|
||||
var vary = require('vary');
|
||||
|
||||
/**
|
||||
* Response prototype.
|
||||
@@ -55,7 +59,9 @@ res.status = function(code){
|
||||
*/
|
||||
|
||||
res.links = function(links){
|
||||
return this.set('Link', Object.keys(links).map(function(rel){
|
||||
var link = this.get('Link') || '';
|
||||
if (link) link += ', ';
|
||||
return this.set('Link', link + Object.keys(links).map(function(rel){
|
||||
return '<' + links[rel] + '>; rel="' + rel + '"';
|
||||
}).join(', '));
|
||||
};
|
||||
@@ -68,69 +74,100 @@ res.links = function(links){
|
||||
* 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);
|
||||
*
|
||||
* @param {Mixed} body or status
|
||||
* @param {Mixed} body
|
||||
* @return {ServerResponse}
|
||||
* @param {string|number|boolean|object|Buffer} body
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.send = function(body){
|
||||
var req = this.req;
|
||||
var head = 'HEAD' == req.method;
|
||||
res.send = function send(body) {
|
||||
var chunk = body;
|
||||
var encoding;
|
||||
var len;
|
||||
var req = this.req;
|
||||
var type;
|
||||
|
||||
// settings
|
||||
var app = this.app;
|
||||
|
||||
// allow status / body
|
||||
if (2 == arguments.length) {
|
||||
if (arguments.length === 2) {
|
||||
// res.send(body, status) backwards compat
|
||||
if ('number' != typeof body && 'number' == typeof arguments[1]) {
|
||||
if (typeof arguments[0] !== 'number' && typeof arguments[1] === 'number') {
|
||||
deprecate('res.send(body, status): Use res.status(status).send(body) instead');
|
||||
this.statusCode = arguments[1];
|
||||
} else {
|
||||
this.statusCode = body;
|
||||
body = arguments[1];
|
||||
deprecate('res.send(status, body): Use res.status(status).send(body) instead');
|
||||
this.statusCode = arguments[0];
|
||||
chunk = arguments[1];
|
||||
}
|
||||
}
|
||||
|
||||
switch (typeof body) {
|
||||
// response status
|
||||
case 'number':
|
||||
this.get('Content-Type') || this.type('txt');
|
||||
this.statusCode = body;
|
||||
body = http.STATUS_CODES[body];
|
||||
break;
|
||||
// disambiguate res.send(status) and res.send(status, num)
|
||||
if (typeof chunk === 'number' && arguments.length === 1) {
|
||||
// res.send(status) will set status message as text string
|
||||
if (!this.get('Content-Type')) {
|
||||
this.type('txt');
|
||||
}
|
||||
|
||||
deprecate('res.send(status): Use res.sendStatus(status) instead');
|
||||
this.statusCode = chunk;
|
||||
chunk = http.STATUS_CODES[chunk];
|
||||
}
|
||||
|
||||
switch (typeof chunk) {
|
||||
// string defaulting to html
|
||||
case 'string':
|
||||
if (!this.get('Content-Type')) {
|
||||
this.charset = this.charset || 'utf-8';
|
||||
this.type('html');
|
||||
}
|
||||
break;
|
||||
case 'boolean':
|
||||
case 'number':
|
||||
case 'object':
|
||||
if (null == body) {
|
||||
body = '';
|
||||
} else if (Buffer.isBuffer(body)) {
|
||||
this.get('Content-Type') || this.type('bin');
|
||||
if (chunk === null) {
|
||||
chunk = '';
|
||||
} else if (Buffer.isBuffer(chunk)) {
|
||||
if (!this.get('Content-Type')) {
|
||||
this.type('bin');
|
||||
}
|
||||
} else {
|
||||
return this.json(body);
|
||||
return this.json(chunk);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// populate Content-Length
|
||||
if (undefined !== body && !this.get('Content-Length')) {
|
||||
this.set('Content-Length', len = Buffer.isBuffer(body)
|
||||
? body.length
|
||||
: Buffer.byteLength(body));
|
||||
// write strings in utf-8
|
||||
if (typeof chunk === 'string') {
|
||||
encoding = 'utf8';
|
||||
type = this.get('Content-Type');
|
||||
|
||||
// reflect this in content-type
|
||||
if (typeof type === 'string') {
|
||||
this.set('Content-Type', setCharset(type, 'utf-8'));
|
||||
}
|
||||
}
|
||||
|
||||
// populate Content-Length
|
||||
if (chunk !== undefined) {
|
||||
if (!Buffer.isBuffer(chunk)) {
|
||||
// convert chunk to Buffer; saves later double conversions
|
||||
chunk = new Buffer(chunk, encoding);
|
||||
encoding = undefined;
|
||||
}
|
||||
|
||||
len = chunk.length;
|
||||
this.set('Content-Length', len);
|
||||
}
|
||||
|
||||
// method check
|
||||
var isHead = req.method === 'HEAD';
|
||||
|
||||
// ETag support
|
||||
// TODO: W/ support
|
||||
if (len > 1024 && 'GET' == req.method) {
|
||||
if (!this.get('ETag')) {
|
||||
this.set('ETag', etag(body));
|
||||
if (len !== undefined && (isHead || req.method === 'GET')) {
|
||||
var etag = app.get('etag fn');
|
||||
if (etag && !this.get('ETag')) {
|
||||
etag = etag(chunk, encoding);
|
||||
etag && this.set('ETag', etag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,11 +179,17 @@ res.send = function(body){
|
||||
this.removeHeader('Content-Type');
|
||||
this.removeHeader('Content-Length');
|
||||
this.removeHeader('Transfer-Encoding');
|
||||
body = '';
|
||||
chunk = '';
|
||||
}
|
||||
|
||||
if (isHead) {
|
||||
// skip body for HEAD
|
||||
this.end();
|
||||
} else {
|
||||
// respond
|
||||
this.end(chunk, encoding);
|
||||
}
|
||||
|
||||
// respond
|
||||
this.end(head ? null : body);
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -157,24 +200,24 @@ res.send = function(body){
|
||||
*
|
||||
* res.json(null);
|
||||
* res.json({ user: 'tj' });
|
||||
* res.json(500, 'oh noes!');
|
||||
* res.json(404, 'I dont have that');
|
||||
*
|
||||
* @param {Mixed} obj or status
|
||||
* @param {Mixed} obj
|
||||
* @return {ServerResponse}
|
||||
* @param {string|number|boolean|object} obj
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.json = function(obj){
|
||||
res.json = function json(obj) {
|
||||
var val = obj;
|
||||
|
||||
// allow status / body
|
||||
if (2 == arguments.length) {
|
||||
if (arguments.length === 2) {
|
||||
// res.json(body, status) backwards compat
|
||||
if ('number' == typeof arguments[1]) {
|
||||
if (typeof arguments[1] === 'number') {
|
||||
deprecate('res.json(obj, status): Use res.status(status).json(obj) instead');
|
||||
this.statusCode = arguments[1];
|
||||
} else {
|
||||
this.statusCode = obj;
|
||||
obj = arguments[1];
|
||||
deprecate('res.json(status, obj): Use res.status(status).json(obj) instead');
|
||||
this.statusCode = arguments[0];
|
||||
val = arguments[1];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,10 +225,12 @@ res.json = function(obj){
|
||||
var app = this.app;
|
||||
var replacer = app.get('json replacer');
|
||||
var spaces = app.get('json spaces');
|
||||
var body = JSON.stringify(obj, replacer, spaces);
|
||||
var body = JSON.stringify(val, replacer, spaces);
|
||||
|
||||
// content-type
|
||||
this.get('Content-Type') || this.set('Content-Type', 'application/json');
|
||||
if (!this.get('Content-Type')) {
|
||||
this.set('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
return this.send(body);
|
||||
};
|
||||
@@ -197,24 +242,24 @@ res.json = function(obj){
|
||||
*
|
||||
* res.jsonp(null);
|
||||
* res.jsonp({ user: 'tj' });
|
||||
* res.jsonp(500, 'oh noes!');
|
||||
* res.jsonp(404, 'I dont have that');
|
||||
*
|
||||
* @param {Mixed} obj or status
|
||||
* @param {Mixed} obj
|
||||
* @return {ServerResponse}
|
||||
* @param {string|number|boolean|object} obj
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.jsonp = function(obj){
|
||||
res.jsonp = function jsonp(obj) {
|
||||
var val = obj;
|
||||
|
||||
// allow status / body
|
||||
if (2 == arguments.length) {
|
||||
if (arguments.length === 2) {
|
||||
// res.json(body, status) backwards compat
|
||||
if ('number' == typeof arguments[1]) {
|
||||
if (typeof arguments[1] === 'number') {
|
||||
deprecate('res.jsonp(obj, status): Use res.status(status).json(obj) instead');
|
||||
this.statusCode = arguments[1];
|
||||
} else {
|
||||
this.statusCode = obj;
|
||||
obj = arguments[1];
|
||||
deprecate('res.jsonp(status, obj): Use res.status(status).jsonp(obj) instead');
|
||||
this.statusCode = arguments[0];
|
||||
val = arguments[1];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,26 +267,66 @@ res.jsonp = function(obj){
|
||||
var app = this.app;
|
||||
var replacer = app.get('json replacer');
|
||||
var spaces = app.get('json spaces');
|
||||
var body = JSON.stringify(obj, replacer, spaces)
|
||||
.replace(/\u2028/g, '\\u2028')
|
||||
.replace(/\u2029/g, '\\u2029');
|
||||
var body = JSON.stringify(val, replacer, spaces);
|
||||
var callback = this.req.query[app.get('jsonp callback name')];
|
||||
|
||||
// content-type
|
||||
this.charset = this.charset || 'utf-8';
|
||||
this.set('Content-Type', 'application/json');
|
||||
if (!this.get('Content-Type')) {
|
||||
this.set('X-Content-Type-Options', 'nosniff');
|
||||
this.set('Content-Type', 'application/json');
|
||||
}
|
||||
|
||||
// fixup callback
|
||||
if (Array.isArray(callback)) {
|
||||
callback = callback[0];
|
||||
}
|
||||
|
||||
// jsonp
|
||||
if (callback) {
|
||||
if (callback instanceof Array) callback = callback[0];
|
||||
if (typeof callback === 'string' && callback.length !== 0) {
|
||||
this.charset = 'utf-8';
|
||||
this.set('X-Content-Type-Options', 'nosniff');
|
||||
this.set('Content-Type', 'text/javascript');
|
||||
var cb = callback.replace(/[^\[\]\w$.]/g, '');
|
||||
body = cb + ' && ' + cb + '(' + body + ');';
|
||||
|
||||
// restrict callback charset
|
||||
callback = callback.replace(/[^\[\]\w$.]/g, '');
|
||||
|
||||
// replace chars not allowed in JavaScript that are in JSON
|
||||
body = body
|
||||
.replace(/\u2028/g, '\\u2028')
|
||||
.replace(/\u2029/g, '\\u2029');
|
||||
|
||||
// the /**/ is a specific security mitigation for "Rosetta Flash JSONP abuse"
|
||||
// the typeof check is just to reduce client error noise
|
||||
body = '/**/ typeof ' + callback + ' === \'function\' && ' + callback + '(' + body + ');';
|
||||
}
|
||||
|
||||
return this.send(body);
|
||||
};
|
||||
|
||||
/**
|
||||
* Send given HTTP status code.
|
||||
*
|
||||
* Sets the response status to `statusCode` and the body of the
|
||||
* response to the standard description from node's http.STATUS_CODES
|
||||
* or the statusCode number if no description.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* res.sendStatus(200);
|
||||
*
|
||||
* @param {number} statusCode
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.sendStatus = function sendStatus(statusCode) {
|
||||
var body = http.STATUS_CODES[statusCode] || String(statusCode);
|
||||
|
||||
this.statusCode = statusCode;
|
||||
this.type('txt');
|
||||
|
||||
return this.send(body);
|
||||
};
|
||||
|
||||
/**
|
||||
* Transfer the file at the given `path`.
|
||||
*
|
||||
@@ -253,8 +338,90 @@ res.jsonp = function(obj){
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `maxAge` defaulting to 0
|
||||
* - `root` root directory for relative filenames
|
||||
* - `maxAge` defaulting to 0 (can be string converted by `ms`)
|
||||
* - `root` root directory for relative filenames
|
||||
* - `headers` object of headers to serve with file
|
||||
* - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them
|
||||
*
|
||||
* Other options are passed along to `send`.
|
||||
*
|
||||
* 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.');
|
||||
* }
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.sendFile = function sendFile(path, options, fn) {
|
||||
var req = this.req;
|
||||
var res = this;
|
||||
var next = req.next;
|
||||
|
||||
if (!path) {
|
||||
throw new TypeError('path argument is required to res.sendFile');
|
||||
}
|
||||
|
||||
// support function as second arg
|
||||
if (typeof options === 'function') {
|
||||
fn = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (!options.root && !isAbsolute(path)) {
|
||||
throw new TypeError('path must be absolute or specify root to res.sendFile');
|
||||
}
|
||||
|
||||
// create file stream
|
||||
var pathname = encodeURI(path);
|
||||
var file = send(req, pathname, options);
|
||||
|
||||
// transfer
|
||||
sendfile(res, file, options, function (err) {
|
||||
if (fn) return fn(err);
|
||||
if (err && err.code === 'EISDIR') return next();
|
||||
|
||||
// next() all but write errors
|
||||
if (err && err.code !== 'ECONNABORT' && err.syscall !== 'write') {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 (can be string converted by `ms`)
|
||||
* - `root` root directory for relative filenames
|
||||
* - `headers` object of headers to serve with file
|
||||
* - `dotfiles` serve dotfiles, defaulting to false; can be `"allow"` to send them
|
||||
*
|
||||
* Other options are passed along to `send`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
@@ -276,96 +443,71 @@ res.jsonp = function(obj){
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Object|Function} options or fn
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.sendfile = function(path, options, fn){
|
||||
var self = this
|
||||
, req = self.req
|
||||
, next = this.req.next
|
||||
, options = options || {}
|
||||
, done;
|
||||
var req = this.req;
|
||||
var res = this;
|
||||
var next = req.next;
|
||||
|
||||
// support function as second arg
|
||||
if ('function' == typeof options) {
|
||||
if (typeof options === 'function') {
|
||||
fn = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
// socket errors
|
||||
req.socket.on('error', error);
|
||||
options = options || {};
|
||||
|
||||
// errors
|
||||
function error(err) {
|
||||
if (done) return;
|
||||
done = true;
|
||||
|
||||
// clean up
|
||||
cleanup();
|
||||
if (!self.headerSent) self.removeHeader('Content-Disposition');
|
||||
|
||||
// callback available
|
||||
if (fn) return fn(err);
|
||||
|
||||
// list in limbo if there's no callback
|
||||
if (self.headerSent) return;
|
||||
|
||||
// delegate
|
||||
next(err);
|
||||
}
|
||||
|
||||
// streaming
|
||||
function stream() {
|
||||
if (done) return;
|
||||
cleanup();
|
||||
if (fn) self.on('finish', fn);
|
||||
}
|
||||
|
||||
// cleanup
|
||||
function cleanup() {
|
||||
req.socket.removeListener('error', error);
|
||||
}
|
||||
// create file stream
|
||||
var file = send(req, path, options);
|
||||
|
||||
// transfer
|
||||
var file = send(req, path);
|
||||
if (options.root) file.root(options.root);
|
||||
file.maxage(options.maxAge || 0);
|
||||
file.on('error', error);
|
||||
file.on('directory', next);
|
||||
file.on('stream', stream);
|
||||
file.pipe(this);
|
||||
this.on('finish', cleanup);
|
||||
sendfile(res, file, options, function (err) {
|
||||
if (fn) return fn(err);
|
||||
if (err && err.code === 'EISDIR') return next();
|
||||
|
||||
// next() all but write errors
|
||||
if (err && err.code !== 'ECONNABORT' && err.syscall !== 'write') {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
res.sendfile = deprecate.function(res.sendfile,
|
||||
'res.sendfile: Use res.sendFile instead');
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* ocurred. Be sure to check `res.headersSent` if you plan to respond.
|
||||
*
|
||||
* This method uses `res.sendfile()`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {String|Function} filename or fn
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.download = function(path, filename, fn){
|
||||
res.download = function download(path, filename, fn) {
|
||||
// support function as second arg
|
||||
if ('function' == typeof filename) {
|
||||
if (typeof filename === 'function') {
|
||||
fn = filename;
|
||||
filename = null;
|
||||
}
|
||||
|
||||
filename = filename || path;
|
||||
this.set('Content-Disposition', 'attachment; filename="' + basename(filename) + '"');
|
||||
return this.sendfile(path, fn);
|
||||
|
||||
// set Content-Disposition when file is sent
|
||||
var headers = {
|
||||
'Content-Disposition': contentDisposition(filename)
|
||||
};
|
||||
|
||||
// Resolve the full path for sendFile
|
||||
var fullPath = resolve(path);
|
||||
|
||||
return this.sendFile(fullPath, { headers: headers }, fn);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -450,8 +592,8 @@ res.type = function(type){
|
||||
*/
|
||||
|
||||
res.format = function(obj){
|
||||
var req = this.req
|
||||
, next = req.next;
|
||||
var req = this.req;
|
||||
var next = req.next;
|
||||
|
||||
var fn = obj.default;
|
||||
if (fn) delete obj.default;
|
||||
@@ -459,7 +601,7 @@ res.format = function(obj){
|
||||
|
||||
var key = req.accepts(keys);
|
||||
|
||||
this.set('Vary', 'Accept');
|
||||
this.vary("Accept");
|
||||
|
||||
if (key) {
|
||||
this.set('Content-Type', normalizeType(key).value);
|
||||
@@ -484,14 +626,45 @@ res.format = function(obj){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.attachment = function(filename){
|
||||
if (filename) this.type(extname(filename));
|
||||
this.set('Content-Disposition', filename
|
||||
? 'attachment; filename="' + basename(filename) + '"'
|
||||
: 'attachment');
|
||||
res.attachment = function attachment(filename) {
|
||||
if (filename) {
|
||||
this.type(extname(filename));
|
||||
}
|
||||
|
||||
this.set('Content-Disposition', contentDisposition(filename));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Append additional header `field` with value `val`.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* res.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
|
||||
* res.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
|
||||
* res.append('Warning', '199 Miscellaneous warning');
|
||||
*
|
||||
* @param {String} field
|
||||
* @param {String|Array} val
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.append = function append(field, val) {
|
||||
var prev = this.get(field);
|
||||
var value = val;
|
||||
|
||||
if (prev) {
|
||||
// concat the new and prev vals
|
||||
value = Array.isArray(prev) ? prev.concat(val)
|
||||
: Array.isArray(val) ? [prev].concat(val)
|
||||
: [prev, val];
|
||||
}
|
||||
|
||||
return this.set(field, value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set header `field` to `val`, or pass
|
||||
* an object of header fields.
|
||||
@@ -511,10 +684,14 @@ res.attachment = function(filename){
|
||||
*/
|
||||
|
||||
res.set =
|
||||
res.header = function(field, val){
|
||||
if (2 == arguments.length) {
|
||||
res.header = function header(field, val) {
|
||||
if (arguments.length === 2) {
|
||||
if (Array.isArray(val)) val = val.map(String);
|
||||
else val = String(val);
|
||||
if ('content-type' == field.toLowerCase() && !/;\s*charset\s*=/.test(val)) {
|
||||
var charset = mime.charsets.lookup(val.split(';')[0]);
|
||||
if (charset) val += '; charset=' + charset.toLowerCase();
|
||||
}
|
||||
this.setHeader(field, val);
|
||||
} else {
|
||||
for (var key in field) {
|
||||
@@ -541,14 +718,14 @@ res.get = function(field){
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Object} options
|
||||
* @param {ServerResponse} for chaining
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.clearCookie = function(name, options){
|
||||
var opts = { expires: new Date(1), path: '/' };
|
||||
return this.cookie(name, '', options
|
||||
? utils.merge(opts, options)
|
||||
? merge(opts, options)
|
||||
: opts);
|
||||
};
|
||||
|
||||
@@ -572,14 +749,15 @@ res.clearCookie = function(name, options){
|
||||
* @param {String} name
|
||||
* @param {String|Object} val
|
||||
* @param {Options} options
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.cookie = function(name, val, options){
|
||||
options = utils.merge({}, options);
|
||||
options = merge({}, options);
|
||||
var secret = this.req.secret;
|
||||
var signed = options.signed;
|
||||
if (signed && !secret) throw new Error('connect.cookieParser("secret") required for signed cookies');
|
||||
if (signed && !secret) throw new Error('cookieParser("secret") required for signed cookies');
|
||||
if ('number' == typeof val) val = val.toString();
|
||||
if ('object' == typeof val) val = 'j:' + JSON.stringify(val);
|
||||
if (signed) val = 's:' + sign(val, secret);
|
||||
@@ -588,7 +766,18 @@ res.cookie = function(name, val, options){
|
||||
options.maxAge /= 1000;
|
||||
}
|
||||
if (null == options.path) options.path = '/';
|
||||
this.set('Set-Cookie', cookie.serialize(name, String(val), options));
|
||||
var headerVal = cookie.serialize(name, String(val), options);
|
||||
|
||||
// supports multiple 'res.cookie' calls by getting previous value
|
||||
var prev = this.get('Set-Cookie');
|
||||
if (prev) {
|
||||
if (Array.isArray(prev)) {
|
||||
headerVal = prev.concat(headerVal);
|
||||
} else {
|
||||
headerVal = [prev, headerVal];
|
||||
}
|
||||
}
|
||||
this.set('Set-Cookie', headerVal);
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -596,57 +785,25 @@ res.cookie = function(name, val, options){
|
||||
/**
|
||||
* Set the location header to `url`.
|
||||
*
|
||||
* The given `url` can also be the name of a mapped url, for
|
||||
* example by default express supports "back" which redirects
|
||||
* The given `url` can also be "back", which redirects
|
||||
* to the _Referrer_ or _Referer_ headers or "/".
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* res.location('/foo/bar').;
|
||||
* res.location('http://example.com');
|
||||
* res.location('../login'); // /blog/post/1 -> /blog/login
|
||||
*
|
||||
* Mounting:
|
||||
*
|
||||
* When an application is mounted and `res.location()`
|
||||
* is given a path that does _not_ lead with "/" it becomes
|
||||
* relative to the mount-point. For example if the application
|
||||
* is mounted at "/blog", the following would become "/blog/login".
|
||||
*
|
||||
* res.location('login');
|
||||
*
|
||||
* While the leading slash would result in a location of "/login":
|
||||
*
|
||||
* res.location('/login');
|
||||
* res.location('../login');
|
||||
*
|
||||
* @param {String} url
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.location = function(url){
|
||||
var app = this.app
|
||||
, req = this.req;
|
||||
var req = this.req;
|
||||
|
||||
// setup redirect map
|
||||
var map = { back: req.get('Referrer') || '/' };
|
||||
|
||||
// perform redirect
|
||||
url = map[url] || url;
|
||||
|
||||
// relative
|
||||
if (!~url.indexOf('://') && 0 != url.indexOf('//')) {
|
||||
var path
|
||||
|
||||
// relative to path
|
||||
if ('.' == url[0]) {
|
||||
path = req.originalUrl.split('?')[0]
|
||||
url = path + ('/' == path[path.length - 1] ? '' : '/') + url;
|
||||
// relative to mount-point
|
||||
} else if ('/' != url[0]) {
|
||||
path = app.path();
|
||||
url = path + '/' + url;
|
||||
}
|
||||
}
|
||||
// "back" is an alias for the referrer
|
||||
if ('back' == url) url = req.get('Referrer') || '/';
|
||||
|
||||
// Respond
|
||||
this.set('Location', url);
|
||||
@@ -666,42 +823,39 @@ res.location = function(url){
|
||||
* res.redirect('/foo/bar');
|
||||
* res.redirect('http://example.com');
|
||||
* res.redirect(301, 'http://example.com');
|
||||
* res.redirect('http://example.com', 301);
|
||||
* res.redirect('../login'); // /blog/post/1 -> /blog/login
|
||||
*
|
||||
* @param {String} url
|
||||
* @param {Number} code
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.redirect = function(url){
|
||||
var app = this.app
|
||||
, head = 'HEAD' == this.req.method
|
||||
, status = 302
|
||||
, body;
|
||||
res.redirect = function redirect(url) {
|
||||
var address = url;
|
||||
var body;
|
||||
var status = 302;
|
||||
|
||||
// allow status / url
|
||||
if (2 == arguments.length) {
|
||||
if ('number' == typeof url) {
|
||||
status = url;
|
||||
url = arguments[1];
|
||||
if (arguments.length === 2) {
|
||||
if (typeof arguments[0] === 'number') {
|
||||
status = arguments[0];
|
||||
address = arguments[1];
|
||||
} else {
|
||||
deprecate('res.redirect(url, status): Use res.redirect(status, url) instead');
|
||||
status = arguments[1];
|
||||
}
|
||||
}
|
||||
|
||||
// Set location header
|
||||
this.location(url);
|
||||
url = this.get('Location');
|
||||
this.location(address);
|
||||
address = this.get('Location');
|
||||
|
||||
// Support text/{plain,html} by default
|
||||
this.format({
|
||||
text: function(){
|
||||
body = statusCodes[status] + '. Redirecting to ' + encodeURI(url);
|
||||
body = statusCodes[status] + '. Redirecting to ' + encodeURI(address);
|
||||
},
|
||||
|
||||
html: function(){
|
||||
var u = utils.escape(url);
|
||||
var u = escapeHtml(address);
|
||||
body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>';
|
||||
},
|
||||
|
||||
@@ -713,7 +867,33 @@ res.redirect = function(url){
|
||||
// Respond
|
||||
this.statusCode = status;
|
||||
this.set('Content-Length', Buffer.byteLength(body));
|
||||
this.end(head ? null : body);
|
||||
|
||||
if (this.req.method === 'HEAD') {
|
||||
this.end();
|
||||
} else {
|
||||
this.end(body);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Add `field` to Vary. If already present in the Vary set, then
|
||||
* this call is simply ignored.
|
||||
*
|
||||
* @param {Array|String} field
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.vary = function(field){
|
||||
// checks for back-compat
|
||||
if (!field || (Array.isArray(field) && !field.length)) {
|
||||
deprecate('res.vary(): Provide a field name');
|
||||
return this;
|
||||
}
|
||||
|
||||
vary(this, field);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -726,17 +906,14 @@ res.redirect = function(url){
|
||||
* - `cache` boolean hinting to the engine it should cache
|
||||
* - `filename` filename of the view being rendered
|
||||
*
|
||||
* @param {String} view
|
||||
* @param {Object|Function} options or callback function
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.render = function(view, options, fn){
|
||||
var self = this
|
||||
, options = options || {}
|
||||
, req = this.req
|
||||
, app = req.app;
|
||||
options = options || {};
|
||||
var self = this;
|
||||
var req = this.req;
|
||||
var app = req.app;
|
||||
|
||||
// support callback function as second arg
|
||||
if ('function' == typeof options) {
|
||||
@@ -755,3 +932,93 @@ res.render = function(view, options, fn){
|
||||
// render
|
||||
app.render(view, options, fn);
|
||||
};
|
||||
|
||||
// pipe the send file stream
|
||||
function sendfile(res, file, options, callback) {
|
||||
var done = false;
|
||||
var streaming;
|
||||
|
||||
// request aborted
|
||||
function onaborted() {
|
||||
if (done) return;
|
||||
done = true;
|
||||
|
||||
var err = new Error('Request aborted');
|
||||
err.code = 'ECONNABORT';
|
||||
callback(err);
|
||||
}
|
||||
|
||||
// directory
|
||||
function ondirectory() {
|
||||
if (done) return;
|
||||
done = true;
|
||||
|
||||
var err = new Error('EISDIR, read');
|
||||
err.code = 'EISDIR';
|
||||
callback(err);
|
||||
}
|
||||
|
||||
// errors
|
||||
function onerror(err) {
|
||||
if (done) return;
|
||||
done = true;
|
||||
callback(err);
|
||||
}
|
||||
|
||||
// ended
|
||||
function onend() {
|
||||
if (done) return;
|
||||
done = true;
|
||||
callback();
|
||||
}
|
||||
|
||||
// file
|
||||
function onfile() {
|
||||
streaming = false;
|
||||
}
|
||||
|
||||
// finished
|
||||
function onfinish(err) {
|
||||
if (err) return onerror(err);
|
||||
if (done) return;
|
||||
|
||||
setImmediate(function () {
|
||||
if (streaming !== false && !done) {
|
||||
onaborted();
|
||||
return;
|
||||
}
|
||||
|
||||
if (done) return;
|
||||
done = true;
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
// streaming
|
||||
function onstream() {
|
||||
streaming = true;
|
||||
}
|
||||
|
||||
file.on('directory', ondirectory);
|
||||
file.on('end', onend);
|
||||
file.on('error', onerror);
|
||||
file.on('file', onfile);
|
||||
file.on('stream', onstream);
|
||||
onFinished(res, onfinish);
|
||||
|
||||
if (options.headers) {
|
||||
// set headers on successful transfer
|
||||
file.on('headers', function headers(res) {
|
||||
var obj = options.headers;
|
||||
var keys = Object.keys(obj);
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var k = keys[i];
|
||||
res.setHeader(k, obj[k]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// pipe
|
||||
file.pipe(res);
|
||||
}
|
||||
|
||||
@@ -1,59 +1,104 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Route = require('./route')
|
||||
, utils = require('../utils')
|
||||
, methods = require('methods')
|
||||
, debug = require('debug')('express:router')
|
||||
, parse = require('connect').utils.parseUrl;
|
||||
var Route = require('./route');
|
||||
var Layer = require('./layer');
|
||||
var methods = require('methods');
|
||||
var mixin = require('utils-merge');
|
||||
var debug = require('debug')('express:router');
|
||||
var deprecate = require('depd')('express');
|
||||
var parseUrl = require('parseurl');
|
||||
var utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Expose `Router` constructor.
|
||||
* Module variables.
|
||||
*/
|
||||
|
||||
exports = module.exports = Router;
|
||||
var objectRegExp = /^\[object (\S+)\]$/;
|
||||
var slice = Array.prototype.slice;
|
||||
var toString = Object.prototype.toString;
|
||||
|
||||
/**
|
||||
* Initialize a new `Router` with the given `options`.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function Router(options) {
|
||||
options = options || {};
|
||||
var self = this;
|
||||
this.map = {};
|
||||
this.params = {};
|
||||
this._params = [];
|
||||
this.caseSensitive = options.caseSensitive;
|
||||
this.strict = options.strict;
|
||||
this.middleware = function router(req, res, next){
|
||||
self._dispatch(req, res, next);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a param callback `fn` for the given `name`.
|
||||
*
|
||||
* @param {String|Function} name
|
||||
* @param {Function} fn
|
||||
* @return {Router} for chaining
|
||||
* @return {Router} which is an callable function
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Router.prototype.param = function(name, fn){
|
||||
var proto = module.exports = function(options) {
|
||||
options = options || {};
|
||||
|
||||
function router(req, res, next) {
|
||||
router.handle(req, res, next);
|
||||
}
|
||||
|
||||
// mixin Router class functions
|
||||
router.__proto__ = proto;
|
||||
|
||||
router.params = {};
|
||||
router._params = [];
|
||||
router.caseSensitive = options.caseSensitive;
|
||||
router.mergeParams = options.mergeParams;
|
||||
router.strict = options.strict;
|
||||
router.stack = [];
|
||||
|
||||
return router;
|
||||
};
|
||||
|
||||
/**
|
||||
* Map the given param placeholder `name`(s) to the given callback.
|
||||
*
|
||||
* 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 same signature as middleware, the only difference
|
||||
* 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.
|
||||
*
|
||||
* Just like in middleware, you must either respond to the request or call next
|
||||
* to avoid stalling the request.
|
||||
*
|
||||
* app.param('user_id', function(req, res, next, id){
|
||||
* User.find(id, function(err, user){
|
||||
* if (err) {
|
||||
* return next(err);
|
||||
* } else if (!user) {
|
||||
* return next(new Error('failed to load user'));
|
||||
* }
|
||||
* req.user = user;
|
||||
* next();
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Function} fn
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
proto.param = function param(name, fn) {
|
||||
// param logic
|
||||
if ('function' == typeof name) {
|
||||
if (typeof name === 'function') {
|
||||
deprecate('router.param(fn): Refactor to use path params');
|
||||
this._params.push(name);
|
||||
return;
|
||||
}
|
||||
|
||||
// apply param functions
|
||||
var params = this._params
|
||||
, len = params.length
|
||||
, ret;
|
||||
var params = this._params;
|
||||
var len = params.length;
|
||||
var ret;
|
||||
|
||||
if (name[0] === ':') {
|
||||
deprecate('router.param(' + JSON.stringify(name) + ', fn): Use router.param(' + JSON.stringify(name.substr(1)) + ', fn) instead');
|
||||
name = name.substr(1);
|
||||
}
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
if (ret = params[i](name, fn)) {
|
||||
@@ -72,202 +117,514 @@ Router.prototype.param = function(name, fn){
|
||||
};
|
||||
|
||||
/**
|
||||
* Route dispatcher aka the route "middleware".
|
||||
* Dispatch a req, res into the router.
|
||||
*
|
||||
* @param {IncomingMessage} req
|
||||
* @param {ServerResponse} res
|
||||
* @param {Function} next
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype._dispatch = function(req, res, next){
|
||||
var params = this.params
|
||||
, self = this;
|
||||
proto.handle = function(req, res, done) {
|
||||
var self = this;
|
||||
|
||||
debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl);
|
||||
debug('dispatching %s %s', req.method, req.url);
|
||||
|
||||
// route dispatch
|
||||
(function pass(i, err){
|
||||
var paramCallbacks
|
||||
, paramIndex = 0
|
||||
, paramVal
|
||||
, route
|
||||
, keys
|
||||
, key;
|
||||
var search = 1 + req.url.indexOf('?');
|
||||
var pathlength = search ? search - 1 : req.url.length;
|
||||
var fqdn = req.url[0] !== '/' && 1 + req.url.substr(0, pathlength).indexOf('://');
|
||||
var protohost = fqdn ? req.url.substr(0, req.url.indexOf('/', 2 + fqdn)) : '';
|
||||
var idx = 0;
|
||||
var removed = '';
|
||||
var slashAdded = false;
|
||||
var paramcalled = {};
|
||||
|
||||
// match next route
|
||||
function nextRoute(err) {
|
||||
pass(req._route_index + 1, err);
|
||||
// store options for OPTIONS request
|
||||
// only used if OPTIONS request
|
||||
var options = [];
|
||||
|
||||
// middleware and routes
|
||||
var stack = self.stack;
|
||||
|
||||
// manage inter-router variables
|
||||
var parentParams = req.params;
|
||||
var parentUrl = req.baseUrl || '';
|
||||
done = restore(done, req, 'baseUrl', 'next', 'params');
|
||||
|
||||
// setup next layer
|
||||
req.next = next;
|
||||
|
||||
// for options requests, respond with a default if nothing else responds
|
||||
if (req.method === 'OPTIONS') {
|
||||
done = wrap(done, function(old, err) {
|
||||
if (err || options.length === 0) return old(err);
|
||||
sendOptionsResponse(res, options, old);
|
||||
});
|
||||
}
|
||||
|
||||
// setup basic req values
|
||||
req.baseUrl = parentUrl;
|
||||
req.originalUrl = req.originalUrl || req.url;
|
||||
|
||||
next();
|
||||
|
||||
function next(err) {
|
||||
var layerError = err === 'route'
|
||||
? null
|
||||
: err;
|
||||
|
||||
// remove added slash
|
||||
if (slashAdded) {
|
||||
req.url = req.url.substr(1);
|
||||
slashAdded = false;
|
||||
}
|
||||
|
||||
// match route
|
||||
req.route = route = self.matchRequest(req, i);
|
||||
// restore altered req.url
|
||||
if (removed.length !== 0) {
|
||||
req.baseUrl = parentUrl;
|
||||
req.url = protohost + removed + req.url.substr(protohost.length);
|
||||
removed = '';
|
||||
}
|
||||
|
||||
// no route
|
||||
if (!route) return next(err);
|
||||
debug('matched %s %s', route.method, route.path);
|
||||
// no more matching layers
|
||||
if (idx >= stack.length) {
|
||||
setImmediate(done, layerError);
|
||||
return;
|
||||
}
|
||||
|
||||
// we have a route
|
||||
// start at param 0
|
||||
req.params = route.params;
|
||||
keys = route.keys;
|
||||
i = 0;
|
||||
// get pathname of request
|
||||
var path = getPathname(req);
|
||||
|
||||
// param callbacks
|
||||
function param(err) {
|
||||
paramIndex = 0;
|
||||
key = keys[i++];
|
||||
paramVal = key && req.params[key.name];
|
||||
paramCallbacks = key && params[key.name];
|
||||
if (path == null) {
|
||||
return done(layerError);
|
||||
}
|
||||
|
||||
try {
|
||||
if ('route' == err) {
|
||||
nextRoute();
|
||||
} else if (err) {
|
||||
i = 0;
|
||||
callbacks(err);
|
||||
} else if (paramCallbacks && undefined !== paramVal) {
|
||||
paramCallback();
|
||||
} else if (key) {
|
||||
param();
|
||||
} else {
|
||||
i = 0;
|
||||
callbacks();
|
||||
}
|
||||
} catch (err) {
|
||||
param(err);
|
||||
// find next matching layer
|
||||
var layer;
|
||||
var match;
|
||||
var route;
|
||||
|
||||
while (match !== true && idx < stack.length) {
|
||||
layer = stack[idx++];
|
||||
match = matchLayer(layer, path);
|
||||
route = layer.route;
|
||||
|
||||
if (typeof match !== 'boolean') {
|
||||
// hold on to layerError
|
||||
layerError = layerError || match;
|
||||
}
|
||||
|
||||
if (match !== true) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!route) {
|
||||
// process non-route handlers normally
|
||||
continue;
|
||||
}
|
||||
|
||||
if (layerError) {
|
||||
// routes do not match with a pending error
|
||||
match = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
var method = req.method;
|
||||
var has_method = route._handles_method(method);
|
||||
|
||||
// build up automatic options response
|
||||
if (!has_method && method === 'OPTIONS') {
|
||||
appendMethods(options, route._options());
|
||||
}
|
||||
|
||||
// don't even bother matching route
|
||||
if (!has_method && method !== 'HEAD') {
|
||||
match = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// no match
|
||||
if (match !== true) {
|
||||
return done(layerError);
|
||||
}
|
||||
|
||||
// store route for dispatch on change
|
||||
if (route) {
|
||||
req.route = route;
|
||||
}
|
||||
|
||||
// Capture one-time layer values
|
||||
req.params = self.mergeParams
|
||||
? mergeParams(layer.params, parentParams)
|
||||
: layer.params;
|
||||
var layerPath = layer.path;
|
||||
|
||||
// this should be done for the layer
|
||||
self.process_params(layer, paramcalled, req, res, function (err) {
|
||||
if (err) {
|
||||
return next(layerError || err);
|
||||
}
|
||||
|
||||
if (route) {
|
||||
return layer.handle_request(req, res, next);
|
||||
}
|
||||
|
||||
trim_prefix(layer, layerError, layerPath, path);
|
||||
});
|
||||
}
|
||||
|
||||
function trim_prefix(layer, layerError, layerPath, path) {
|
||||
var c = path[layerPath.length];
|
||||
if (c && '/' !== c && '.' !== c) return next(layerError);
|
||||
|
||||
// Trim off the part of the url that matches the route
|
||||
// middleware (.use stuff) needs to have the path stripped
|
||||
if (layerPath.length !== 0) {
|
||||
debug('trim prefix (%s) from url %s', layerPath, req.url);
|
||||
removed = layerPath;
|
||||
req.url = protohost + req.url.substr(protohost.length + removed.length);
|
||||
|
||||
// Ensure leading slash
|
||||
if (!fqdn && req.url[0] !== '/') {
|
||||
req.url = '/' + req.url;
|
||||
slashAdded = true;
|
||||
}
|
||||
|
||||
// Setup base URL (no trailing slash)
|
||||
req.baseUrl = parentUrl + (removed[removed.length - 1] === '/'
|
||||
? removed.substring(0, removed.length - 1)
|
||||
: removed);
|
||||
}
|
||||
|
||||
debug('%s %s : %s', layer.name, layerPath, req.originalUrl);
|
||||
|
||||
if (layerError) {
|
||||
layer.handle_error(layerError, req, res, next);
|
||||
} else {
|
||||
layer.handle_request(req, res, next);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Process any parameters for the layer.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
proto.process_params = function(layer, called, req, res, done) {
|
||||
var params = this.params;
|
||||
|
||||
// captured parameters from the layer, keys and values
|
||||
var keys = layer.keys;
|
||||
|
||||
// fast track
|
||||
if (!keys || keys.length === 0) {
|
||||
return done();
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var name;
|
||||
var paramIndex = 0;
|
||||
var key;
|
||||
var paramVal;
|
||||
var paramCallbacks;
|
||||
var paramCalled;
|
||||
|
||||
// process params in order
|
||||
// param callbacks can be async
|
||||
function param(err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
if (i >= keys.length ) {
|
||||
return done();
|
||||
}
|
||||
|
||||
paramIndex = 0;
|
||||
key = keys[i++];
|
||||
|
||||
if (!key) {
|
||||
return done();
|
||||
}
|
||||
|
||||
name = key.name;
|
||||
paramVal = req.params[name];
|
||||
paramCallbacks = params[name];
|
||||
paramCalled = called[name];
|
||||
|
||||
if (paramVal === undefined || !paramCallbacks) {
|
||||
return param();
|
||||
}
|
||||
|
||||
// param previously called with same value or error occurred
|
||||
if (paramCalled && (paramCalled.error || paramCalled.match === paramVal)) {
|
||||
// restore value
|
||||
req.params[name] = paramCalled.value;
|
||||
|
||||
// next param
|
||||
return param(paramCalled.error);
|
||||
}
|
||||
|
||||
called[name] = paramCalled = {
|
||||
error: null,
|
||||
match: paramVal,
|
||||
value: paramVal
|
||||
};
|
||||
|
||||
param(err);
|
||||
paramCallback();
|
||||
}
|
||||
|
||||
// single param callbacks
|
||||
function paramCallback(err) {
|
||||
var fn = paramCallbacks[paramIndex++];
|
||||
if (err || !fn) return param(err);
|
||||
// single param callbacks
|
||||
function paramCallback(err) {
|
||||
var fn = paramCallbacks[paramIndex++];
|
||||
|
||||
// store updated value
|
||||
paramCalled.value = req.params[key.name];
|
||||
|
||||
if (err) {
|
||||
// store error
|
||||
paramCalled.error = err;
|
||||
param(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!fn) return param();
|
||||
|
||||
try {
|
||||
fn(req, res, paramCallback, paramVal, key.name);
|
||||
}
|
||||
|
||||
// invoke route callbacks
|
||||
function callbacks(err) {
|
||||
var fn = route.callbacks[i++];
|
||||
try {
|
||||
if ('route' == err) {
|
||||
nextRoute();
|
||||
} else if (err && fn) {
|
||||
if (fn.length < 4) return callbacks(err);
|
||||
fn(err, req, res, callbacks);
|
||||
} else if (fn) {
|
||||
if (fn.length < 4) return fn(req, res, callbacks);
|
||||
callbacks();
|
||||
} else {
|
||||
nextRoute(err);
|
||||
}
|
||||
} catch (err) {
|
||||
callbacks(err);
|
||||
}
|
||||
}
|
||||
})(0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempt to match a route for `req`
|
||||
* with optional starting index of `i`
|
||||
* defaulting to 0.
|
||||
*
|
||||
* @param {IncomingMessage} req
|
||||
* @param {Number} i
|
||||
* @return {Route}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype.matchRequest = function(req, i, head){
|
||||
var method = req.method.toLowerCase()
|
||||
, url = parse(req)
|
||||
, path = url.pathname
|
||||
, routes = this.map
|
||||
, i = i || 0
|
||||
, route;
|
||||
|
||||
// HEAD support
|
||||
if (!head && 'head' == method) {
|
||||
route = this.matchRequest(req, i, true);
|
||||
if (route) return route;
|
||||
method = 'get';
|
||||
}
|
||||
|
||||
// routes for this method
|
||||
if (routes = routes[method]) {
|
||||
|
||||
// matching routes
|
||||
for (var len = routes.length; i < len; ++i) {
|
||||
route = routes[i];
|
||||
if (route.match(path)) {
|
||||
req._route_index = i;
|
||||
return route;
|
||||
}
|
||||
} catch (e) {
|
||||
paramCallback(e);
|
||||
}
|
||||
}
|
||||
|
||||
param();
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempt to match a route for `method`
|
||||
* and `url` with optional starting
|
||||
* index of `i` defaulting to 0.
|
||||
* Use the given middleware function, with optional path, defaulting to "/".
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} url
|
||||
* @param {Number} i
|
||||
* @return {Route}
|
||||
* @api private
|
||||
* Use (like `.all`) will run for any http METHOD, but it will not add
|
||||
* handlers for those methods so OPTIONS requests will not consider `.use`
|
||||
* functions even if they could respond.
|
||||
*
|
||||
* The other difference is that _route_ path is stripped and not visible
|
||||
* to the handler function. The main effect of this feature is that mounted
|
||||
* handlers can operate without any code changes regardless of the "prefix"
|
||||
* pathname.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Router.prototype.match = function(method, url, i, head){
|
||||
var req = { method: method, url: url };
|
||||
return this.matchRequest(req, i, head);
|
||||
};
|
||||
proto.use = function use(fn) {
|
||||
var offset = 0;
|
||||
var path = '/';
|
||||
|
||||
/**
|
||||
* Route `method`, `path`, and one or more callbacks.
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} path
|
||||
* @param {Function} callback...
|
||||
* @return {Router} for chaining
|
||||
* @api private
|
||||
*/
|
||||
// default path to '/'
|
||||
// disambiguate router.use([fn])
|
||||
if (typeof fn !== 'function') {
|
||||
var arg = fn;
|
||||
|
||||
Router.prototype.route = function(method, path, callbacks){
|
||||
var method = method.toLowerCase()
|
||||
, callbacks = utils.flatten([].slice.call(arguments, 2));
|
||||
while (Array.isArray(arg) && arg.length !== 0) {
|
||||
arg = arg[0];
|
||||
}
|
||||
|
||||
// ensure path was given
|
||||
if (!path) throw new Error('Router#' + method + '() requires a path');
|
||||
// first arg is the path
|
||||
if (typeof arg !== 'function') {
|
||||
offset = 1;
|
||||
path = fn;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure all callbacks are functions
|
||||
callbacks.forEach(function(fn, i){
|
||||
if ('function' == typeof fn) return;
|
||||
var type = {}.toString.call(fn);
|
||||
var msg = '.' + method + '() requires callback functions but got a ' + type;
|
||||
throw new Error(msg);
|
||||
});
|
||||
var callbacks = utils.flatten(slice.call(arguments, offset));
|
||||
|
||||
// create the route
|
||||
debug('defined %s %s', method, path);
|
||||
var route = new Route(method, path, callbacks, {
|
||||
sensitive: this.caseSensitive,
|
||||
strict: this.strict
|
||||
});
|
||||
if (callbacks.length === 0) {
|
||||
throw new TypeError('Router.use() requires middleware functions');
|
||||
}
|
||||
|
||||
callbacks.forEach(function (fn) {
|
||||
if (typeof fn !== 'function') {
|
||||
throw new TypeError('Router.use() requires middleware function but got a ' + gettype(fn));
|
||||
}
|
||||
|
||||
// add the middleware
|
||||
debug('use %s %s', path, fn.name || '<anonymous>');
|
||||
|
||||
var layer = new Layer(path, {
|
||||
sensitive: this.caseSensitive,
|
||||
strict: false,
|
||||
end: false
|
||||
}, fn);
|
||||
|
||||
layer.route = undefined;
|
||||
|
||||
this.stack.push(layer);
|
||||
}, this);
|
||||
|
||||
// add it
|
||||
(this.map[method] = this.map[method] || []).push(route);
|
||||
return this;
|
||||
};
|
||||
|
||||
methods.forEach(function(method){
|
||||
Router.prototype[method] = function(path){
|
||||
var args = [method].concat([].slice.call(arguments));
|
||||
this.route.apply(this, args);
|
||||
/**
|
||||
* Create a new Route for the given path.
|
||||
*
|
||||
* Each route contains a separate middleware stack and VERB handlers.
|
||||
*
|
||||
* See the Route api documentation for details on adding handlers
|
||||
* and middleware to routes.
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {Route}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
proto.route = function(path){
|
||||
var route = new Route(path);
|
||||
|
||||
var layer = new Layer(path, {
|
||||
sensitive: this.caseSensitive,
|
||||
strict: this.strict,
|
||||
end: true
|
||||
}, route.dispatch.bind(route));
|
||||
|
||||
layer.route = route;
|
||||
|
||||
this.stack.push(layer);
|
||||
return route;
|
||||
};
|
||||
|
||||
// create Router#VERB functions
|
||||
methods.concat('all').forEach(function(method){
|
||||
proto[method] = function(path){
|
||||
var route = this.route(path)
|
||||
route[method].apply(route, slice.call(arguments, 1));
|
||||
return this;
|
||||
};
|
||||
});
|
||||
|
||||
// append methods to a list of methods
|
||||
function appendMethods(list, addition) {
|
||||
for (var i = 0; i < addition.length; i++) {
|
||||
var method = addition[i];
|
||||
if (list.indexOf(method) === -1) {
|
||||
list.push(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get pathname of request
|
||||
function getPathname(req) {
|
||||
try {
|
||||
return parseUrl(req).pathname;
|
||||
} catch (err) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// get type for error message
|
||||
function gettype(obj) {
|
||||
var type = typeof obj;
|
||||
|
||||
if (type !== 'object') {
|
||||
return type;
|
||||
}
|
||||
|
||||
// inspect [[Class]] for objects
|
||||
return toString.call(obj)
|
||||
.replace(objectRegExp, '$1');
|
||||
}
|
||||
|
||||
/**
|
||||
* Match path to a layer.
|
||||
*
|
||||
* @param {Layer} layer
|
||||
* @param {string} path
|
||||
* @private
|
||||
*/
|
||||
|
||||
function matchLayer(layer, path) {
|
||||
try {
|
||||
return layer.match(path);
|
||||
} catch (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
// merge params with parent params
|
||||
function mergeParams(params, parent) {
|
||||
if (typeof parent !== 'object' || !parent) {
|
||||
return params;
|
||||
}
|
||||
|
||||
// make copy of parent for base
|
||||
var obj = mixin({}, parent);
|
||||
|
||||
// simple non-numeric merging
|
||||
if (!(0 in params) || !(0 in parent)) {
|
||||
return mixin(obj, params);
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var o = 0;
|
||||
|
||||
// determine numeric gaps
|
||||
while (i === o || o in parent) {
|
||||
if (i in params) i++;
|
||||
if (o in parent) o++;
|
||||
}
|
||||
|
||||
// offset numeric indices in params before merge
|
||||
for (i--; i >= 0; i--) {
|
||||
params[i + o] = params[i];
|
||||
|
||||
// create holes for the merge when necessary
|
||||
if (i < o) {
|
||||
delete params[i];
|
||||
}
|
||||
}
|
||||
|
||||
return mixin(parent, params);
|
||||
}
|
||||
|
||||
// restore obj props after function
|
||||
function restore(fn, obj) {
|
||||
var props = new Array(arguments.length - 2);
|
||||
var vals = new Array(arguments.length - 2);
|
||||
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
props[i] = arguments[i + 2];
|
||||
vals[i] = obj[props[i]];
|
||||
}
|
||||
|
||||
return function(err){
|
||||
// restore vals
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
obj[props[i]] = vals[i];
|
||||
}
|
||||
|
||||
return fn.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
// send an OPTIONS response
|
||||
function sendOptionsResponse(res, options, next) {
|
||||
try {
|
||||
var body = options.join(',');
|
||||
res.set('Allow', body);
|
||||
res.send(body);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
// wrap a function
|
||||
function wrap(old, fn) {
|
||||
return function proxy() {
|
||||
var args = new Array(arguments.length + 1);
|
||||
|
||||
args[0] = old;
|
||||
for (var i = 0, len = arguments.length; i < len; i++) {
|
||||
args[i + 1] = arguments[i];
|
||||
}
|
||||
|
||||
fn.apply(this, args);
|
||||
};
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user