mirror of
https://github.com/expressjs/express.git
synced 2026-02-26 08:45:36 +00:00
Compare commits
206 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
0
.gitmodules
vendored
0
.gitmodules
vendored
@@ -1,9 +1,11 @@
|
||||
.git*
|
||||
benchmarks/
|
||||
docs/
|
||||
examples/
|
||||
support/
|
||||
test/
|
||||
testing.js
|
||||
.DS_Store
|
||||
.travis.yml
|
||||
coverage.html
|
||||
lib-cov
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.8"
|
||||
- "0.10"
|
||||
- "0.11"
|
||||
matrix:
|
||||
allow_failures:
|
||||
- node_js: "0.11"
|
||||
fast_finish: true
|
||||
|
||||
99
History.md
99
History.md
@@ -1,5 +1,100 @@
|
||||
3.4.3 / 2013-100-23
|
||||
===================
|
||||
4.1.0 / 2014-04-24
|
||||
==================
|
||||
|
||||
* pass options from `res.sendfile` to `send`
|
||||
* preserve casing of headers in `res.header` and `res.set`
|
||||
* support unicode file names in `res.attachment` and `res.download`
|
||||
* update accepts to 1.0.1
|
||||
* update cookie to 0.1.2
|
||||
* update send to 0.3.0
|
||||
* update serve-static to 1.1.0
|
||||
* update type-is to 1.1.0
|
||||
|
||||
4.0.0 / 2014-04-09
|
||||
==================
|
||||
|
||||
* remove:
|
||||
- node 0.8 support
|
||||
- connect and connect's patches except for charset handling
|
||||
- express(1) - moved to [express-generator](https://github.com/expressjs/generator)
|
||||
- `express.createServer()` - it has been deprecated for a long time. Use `express()`
|
||||
- `app.configure` - use logic in your own app code
|
||||
- `app.router` - is removed
|
||||
- `req.auth` - use `basic-auth` instead
|
||||
- `req.accepted*` - use `req.accepts*()` instead
|
||||
- `res.location` - relative URL resolution is removed
|
||||
- `res.charset` - include the charset in the content type when using `res.set()`
|
||||
- all bundled middleware except `static`
|
||||
* change:
|
||||
- `app.route` -> `app.mountpath` when mounting an express app in another express app
|
||||
- `json spaces` no longer enabled by default in development
|
||||
- `req.accepts*` -> `req.accepts*s` - i.e. `req.acceptsEncoding` -> `req.acceptsEncodings`
|
||||
- `req.params` is now an object instead of an array
|
||||
- `res.locals` is no longer a function. It is a plain js object. Treat it as such.
|
||||
- `res.headerSent` -> `res.headersSent` to match node.js ServerResponse object
|
||||
* refactor:
|
||||
- `req.accepts*` with [accepts](https://github.com/expressjs/accepts)
|
||||
- `req.is` with [type-is](https://github.com/expressjs/type-is)
|
||||
- [path-to-regexp](https://github.com/component/path-to-regexp)
|
||||
* add:
|
||||
- `app.router()` - returns the app Router instance
|
||||
- `app.route()` - Proxy to the app's `Router#route()` method to create a new route
|
||||
- Router & Route - public API
|
||||
|
||||
3.5.2 / 2014-04-24
|
||||
==================
|
||||
|
||||
* update connect to 2.14.5
|
||||
* update cookie to 0.1.2
|
||||
* update mkdirp to 0.4.0
|
||||
* update send to 0.3.0
|
||||
|
||||
3.5.1 / 2014-03-25
|
||||
==================
|
||||
|
||||
* pin less-middleware in generated app
|
||||
|
||||
3.5.0 / 2014-03-06
|
||||
==================
|
||||
|
||||
* bump deps
|
||||
|
||||
3.4.8 / 2014-01-13
|
||||
==================
|
||||
|
||||
* prevent incorrect automatic OPTIONS responses #1868 @dpatti
|
||||
* update binary and examples for jade 1.0 #1876 @yossi, #1877 @reqshark, #1892 @matheusazzi
|
||||
* throw 400 in case of malformed paths @rlidwka
|
||||
|
||||
3.4.7 / 2013-12-10
|
||||
==================
|
||||
|
||||
* update connect
|
||||
|
||||
3.4.6 / 2013-12-01
|
||||
==================
|
||||
|
||||
* update connect (raw-body)
|
||||
|
||||
3.4.5 / 2013-11-27
|
||||
==================
|
||||
|
||||
* update connect
|
||||
* res.location: remove leading ./ #1802 @kapouer
|
||||
* res.redirect: fix `res.redirect('toString') #1829 @michaelficarra
|
||||
* res.send: always send ETag when content-length > 0
|
||||
* router: add Router.all() method
|
||||
|
||||
3.4.4 / 2013-10-29
|
||||
==================
|
||||
|
||||
* update connect
|
||||
* update supertest
|
||||
* update methods
|
||||
* express(1): replace bodyParser() with urlencoded() and json() #1795 @chirag04
|
||||
|
||||
3.4.3 / 2013-10-23
|
||||
==================
|
||||
|
||||
* update connect
|
||||
|
||||
|
||||
4
LICENSE
4
LICENSE
@@ -1,6 +1,6 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2009-2013 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.
|
||||
|
||||
6
Makefile
6
Makefile
@@ -24,11 +24,11 @@ test-cov: lib-cov
|
||||
lib-cov:
|
||||
@jscoverage lib lib-cov
|
||||
|
||||
benchmark:
|
||||
@./support/bench
|
||||
bench:
|
||||
@$(MAKE) -C benchmarks
|
||||
|
||||
clean:
|
||||
rm -f coverage.html
|
||||
rm -fr lib-cov
|
||||
|
||||
.PHONY: test test-unit test-acceptance benchmark clean
|
||||
.PHONY: test test-unit test-acceptance bench clean
|
||||
|
||||
68
Readme.md
68
Readme.md
@@ -1,8 +1,8 @@
|
||||
[](http://expressjs.com/)
|
||||
[](http://expressjs.com/)
|
||||
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
|
||||
|
||||
[](http://travis-ci.org/visionmedia/express) [](https://www.gittip.com/visionmedia/)
|
||||
[](https://travis-ci.org/visionmedia/express) [](https://www.gittip.com/visionmedia/)
|
||||
|
||||
```js
|
||||
var express = require('express');
|
||||
@@ -15,17 +15,22 @@ app.get('/', function(req, res){
|
||||
app.listen(3000);
|
||||
```
|
||||
|
||||
**PROTIP** Be sure to read [Migrating from 3.x to 4.x](https://github.com/visionmedia/express/wiki/Migrating-from-3.x-to-4.x) as well as [New features in 4.x](https://github.com/visionmedia/express/wiki/New-features-in-4.x).
|
||||
|
||||
## Installation
|
||||
|
||||
$ npm install -g express
|
||||
$ npm install 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:
|
||||
The quickest way to get started with express is to utilize the executable [`express(1)`](http://github.com/expressjs/generator) to generate an application as shown below:
|
||||
|
||||
Install the executable. The executable's major version will match Express's:
|
||||
|
||||
$ npm install -g express-generator@3
|
||||
|
||||
Create the app:
|
||||
|
||||
$ npm install -g express
|
||||
$ express /tmp/foo && cd /tmp/foo
|
||||
|
||||
Install dependencies:
|
||||
@@ -34,30 +39,26 @@ app.listen(3000);
|
||||
|
||||
Start the server:
|
||||
|
||||
$ node app
|
||||
$ npm start
|
||||
|
||||
## Features
|
||||
|
||||
* Built on [Connect](http://github.com/senchalabs/connect)
|
||||
* Robust routing
|
||||
* 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
|
||||
|
||||
## 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](http://github.com/visionmedia/consolidate.js),
|
||||
you can quickly craft your perfect framework.
|
||||
|
||||
## More Information
|
||||
@@ -65,62 +66,43 @@ app.listen(3000);
|
||||
* [Website and Documentation](http://expressjs.com/) stored at [visionmedia/expressjs.com](https://github.com/visionmedia/expressjs.com)
|
||||
* 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
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) and [defunctzombie](https://twitter.com/defunctzombie) 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:
|
||||
Clone the Express repo, then install the dev dependencies to install all the example / test suite dependencies:
|
||||
|
||||
$ git clone git://github.com/visionmedia/express.git --depth 1
|
||||
$ cd express
|
||||
$ npm install
|
||||
|
||||
then run whichever tests you want:
|
||||
Then run whichever tests you want:
|
||||
|
||||
$ node examples/content-negotiation
|
||||
|
||||
You can also view live examples here
|
||||
You can also view live examples here:
|
||||
|
||||
<a href="https://runnable.com/express" target="_blank"><img src="https://runnable.com/external/styles/assets/runnablebtn.png" style="width:67px;height:25px;"></a>
|
||||
|
||||
## Running Tests
|
||||
|
||||
To run the test suite first invoke the following command within the repo, installing the development dependencies:
|
||||
To run the test suite, first invoke the following command within the repo, installing the development dependencies:
|
||||
|
||||
$ npm install
|
||||
|
||||
then run the tests:
|
||||
Then run the tests:
|
||||
|
||||
$ make test
|
||||
|
||||
## Contributors
|
||||
|
||||
https://github.com/visionmedia/express/graphs/contributors
|
||||
|
||||
Author: [TJ Holowaychuk](http://github.com/visionmedia)
|
||||
Lead Maintainer: [Roman Shtylman](https://github.com/defunctzombie)
|
||||
Contributors: https://github.com/visionmedia/express/graphs/contributors
|
||||
|
||||
## License
|
||||
|
||||
(The 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.
|
||||
MIT
|
||||
|
||||
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 program = require('commander')
|
||||
, mkdirp = require('mkdirp')
|
||||
, pkg = require('../package.json')
|
||||
, version = pkg.version
|
||||
, os = require('os')
|
||||
, fs = require('fs');
|
||||
|
||||
// CLI
|
||||
|
||||
program
|
||||
.version(version)
|
||||
.usage('[options] [dir]')
|
||||
.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\');'
|
||||
, 'var routes = require(\'./routes\');'
|
||||
, 'var user = require(\'./routes/user\');'
|
||||
, 'var http = require(\'http\');'
|
||||
, 'var path = require(\'path\');'
|
||||
, ''
|
||||
, 'var app = express();'
|
||||
, ''
|
||||
, '// all environments'
|
||||
, 'app.set(\'port\', process.env.PORT || 3000);'
|
||||
, 'app.set(\'views\', path.join(__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: path.join(__dirname, \'public\') }));');
|
||||
break;
|
||||
case 'stylus':
|
||||
app = app.replace('{css}', eol + 'app.use(require(\'stylus\').middleware(path.join(__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,11 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..')
|
||||
, hash = require('./pass').hash;
|
||||
var express = require('../..');
|
||||
var hash = require('./pass').hash;
|
||||
var bodyParser = require('body-parser');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var session = require('express-session');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
@@ -14,15 +17,15 @@ app.set('views', __dirname + '/views');
|
||||
|
||||
// middleware
|
||||
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.cookieParser('shhhh, very secret'));
|
||||
app.use(express.session());
|
||||
app.use(bodyParser());
|
||||
app.use(cookieParser('shhhh, very secret'));
|
||||
app.use(session());
|
||||
|
||||
// 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 +65,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) {
|
||||
@@ -98,9 +101,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;
|
||||
|
||||
@@ -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,11 @@ 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');
|
||||
console.log('Express listening on port 3000');
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
var users = [];
|
||||
|
||||
users.push({ name: 'Tobi' });
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
var express = require('../../')
|
||||
, app = module.exports = express()
|
||||
, users = require('./db');
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
var users = require('./db');
|
||||
|
||||
// so either you can deal with different types of formatting
|
||||
// so either you can deal with different types of formatting
|
||||
// for expected response in index.js
|
||||
app.get('/', function(req, res){
|
||||
res.format({
|
||||
@@ -21,18 +21,18 @@ app.get('/', function(req, res){
|
||||
json: function(){
|
||||
res.json(users);
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
// or you could write a tiny middleware like
|
||||
// this to add a layer of abstraction
|
||||
// and make things a bit more declarative:
|
||||
|
||||
function format(requestHandlerName) {
|
||||
var requestHandler = require(requestHandlerName);
|
||||
function format(path) {
|
||||
var obj = require(path);
|
||||
return function(req, res){
|
||||
res.format(requestHandler);
|
||||
}
|
||||
res.format(obj);
|
||||
};
|
||||
}
|
||||
|
||||
app.get('/users', format('./users'));
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var favicon = require('static-favicon');
|
||||
var cookie-parser = require('cookie-parser');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// ignore GET /favicon.ico
|
||||
app.use(express.favicon());
|
||||
app.use(favicon());
|
||||
|
||||
// pass a secret to cookieParser() for signed cookies
|
||||
app.use(express.cookieParser('manny is cool'));
|
||||
app.use(cookieParser('manny is cool'));
|
||||
|
||||
// add req.session cookie support
|
||||
app.use(express.cookieSession());
|
||||
app.use(cookieSession());
|
||||
|
||||
// do something with the session
|
||||
app.use(count);
|
||||
@@ -29,4 +30,4 @@ function count(req, res) {
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express server listening on port 3000');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,33 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
var favicon = require('static-favicon');
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
// 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());
|
||||
app.use(favicon());
|
||||
|
||||
// custom log format
|
||||
if ('test' != process.env.NODE_ENV)
|
||||
app.use(express.logger(':method :url'));
|
||||
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());
|
||||
app.use(bodyParser());
|
||||
|
||||
app.get('/', function(req, res){
|
||||
if (req.cookies.remember) {
|
||||
@@ -51,4 +53,4 @@ app.post('/', function(req, res){
|
||||
if (!module.parent){
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..')
|
||||
, app = express()
|
||||
, api = express();
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var app = express();
|
||||
var bodyParser = require('body-parser');
|
||||
var api = express();
|
||||
|
||||
// app middleware
|
||||
|
||||
@@ -12,8 +14,8 @@ app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// api middleware
|
||||
|
||||
api.use(express.logger('dev'));
|
||||
api.use(express.bodyParser());
|
||||
api.use(logger('dev'));
|
||||
api.use(bodyParser());
|
||||
|
||||
/**
|
||||
* CORS support.
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
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>'
|
||||
+ '</ul>');
|
||||
});
|
||||
@@ -16,8 +16,8 @@ app.get('/', function(req, res){
|
||||
// /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;
|
||||
var file = req.params.file;
|
||||
var path = __dirname + '/files/' + file;
|
||||
|
||||
res.download(path);
|
||||
});
|
||||
@@ -41,4 +41,4 @@ app.use(function(err, req, res, next){
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
}
|
||||
|
||||
1
examples/downloads/files/utf-8 한中日.txt
Normal file
1
examples/downloads/files/utf-8 한中日.txt
Normal file
@@ -0,0 +1 @@
|
||||
한中日
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
* 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 favicon = require('static-favicon');
|
||||
var silent = 'test' == process.env.NODE_ENV;
|
||||
|
||||
// general config
|
||||
app.set('views', __dirname + '/views');
|
||||
@@ -21,18 +23,36 @@ if ('production' == app.settings.env) {
|
||||
app.disable('verbose errors');
|
||||
}
|
||||
|
||||
app.use(express.favicon());
|
||||
app.use(favicon());
|
||||
|
||||
silent || app.use(express.logger('dev'));
|
||||
silent || app.use(logger('dev'));
|
||||
|
||||
// "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.
|
||||
// Routes
|
||||
|
||||
app.use(app.router);
|
||||
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!'));
|
||||
});
|
||||
|
||||
// Error handlers
|
||||
|
||||
// Since this is the last non-error-handling
|
||||
// middleware use()d, we assume 404, as nothing else
|
||||
@@ -44,7 +64,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 +101,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!'));
|
||||
});
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
silent || console.log('Express started on port 3000');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
@@ -42,7 +35,12 @@ 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);
|
||||
|
||||
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.
|
||||
@@ -57,4 +58,4 @@ app.get('/user', function(req, res){
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('app listening on port 3000');
|
||||
console.log('app listening on port 3000');
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
var express = require('../../');
|
||||
|
||||
var app = express();
|
||||
|
||||
@@ -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,11 @@ app.get('/', function(req, res){
|
||||
res.render('users', { users: users });
|
||||
});
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app 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,11 +32,11 @@ 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' });
|
||||
})
|
||||
});
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(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,43 @@ 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);
|
||||
});
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
app.listen(4000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
exports.index = function(req, res){
|
||||
res.redirect('/users');
|
||||
};
|
||||
@@ -1,8 +1,9 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
exports.engine = 'jade';
|
||||
|
||||
exports.before = function(req, res, next){
|
||||
var pet = db.pets[req.params.pet_id];
|
||||
if (!pet) return next(new Error('Pet not found'));
|
||||
@@ -20,7 +21,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);
|
||||
};
|
||||
|
||||
@@ -2,6 +2,6 @@ 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)
|
||||
label= 'Name: '
|
||||
input(type='text', name='pet[name]', value=pet.name)
|
||||
input(type='submit', value='Update')
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
@@ -12,7 +15,7 @@ exports.before = function(req, res, next){
|
||||
// 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>
|
||||
12
examples/mvc/controllers/user/views/edit.jade
Normal file
12
examples/mvc/controllers/user/views/edit.jade
Normal file
@@ -0,0 +1,12 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1= user.name
|
||||
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}')
|
||||
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,21 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var session = require('express-session');
|
||||
var cookieParser = require('cookie-parser');
|
||||
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 +32,20 @@ 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(cookieParser('some secret here'));
|
||||
app.use(session());
|
||||
|
||||
// parse request bodies (req.body)
|
||||
app.use(express.bodyParser());
|
||||
app.use(bodyParser());
|
||||
|
||||
// support _method (PUT in forms etc)
|
||||
app.use(express.methodOverride());
|
||||
// override methods (put, delete)
|
||||
app.use(methodOverride());
|
||||
|
||||
// expose the "messages" local variable when views are rendered
|
||||
app.use(function(req, res, next){
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
/**
|
||||
* 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 method;
|
||||
var path;
|
||||
|
||||
// allow specifying the view engine
|
||||
if (obj.engine) app.set('view engine', obj.engine);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
|
||||
// Faux database
|
||||
|
||||
@@ -18,7 +17,7 @@ var users = [
|
||||
|
||||
// Convert :to and :from to integers
|
||||
|
||||
app.param(['to', 'from'], function(req, res, next, num, name){
|
||||
app.param(['to', 'from'], function(req, res, next, num, name){
|
||||
req.params[name] = num = parseInt(num, 10);
|
||||
if( isNaN(num) ){
|
||||
next(new Error('failed to parseInt '+num));
|
||||
@@ -58,9 +57,9 @@ 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(', '));
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -12,9 +11,9 @@ 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);
|
||||
@@ -82,7 +81,7 @@ app.get('/', function(req, res){
|
||||
, '<li>GET /users/1..3.json</li>'
|
||||
, '<li>DELETE /users/4</li>'
|
||||
, '</ul>'
|
||||
].join('\n'));
|
||||
].join('\n'));
|
||||
});
|
||||
|
||||
if (!module.parent) {
|
||||
|
||||
@@ -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 || '';
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
|
||||
/**
|
||||
* 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 site = require('./site');
|
||||
var post = require('./post');
|
||||
var user = require('./user');
|
||||
|
||||
// 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());
|
||||
app.use(logger('dev'));
|
||||
app.use(cookieParser());
|
||||
app.use(bodyParser());
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// General
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
!!! 5
|
||||
doctype
|
||||
html
|
||||
head
|
||||
title Search example
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
|
||||
// first:
|
||||
// $ npm install redis
|
||||
// $ redis-server
|
||||
|
||||
var express = require('../..');
|
||||
var cookieParser = require('cookie-parser');
|
||||
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'));
|
||||
app.use(cookieParser('keyboard cat'));
|
||||
|
||||
// Populates req.session
|
||||
app.use(express.session());
|
||||
app.use(session());
|
||||
|
||||
app.get('/', function(req, res){
|
||||
var body = '';
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..');
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
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'));
|
||||
app.use(logger('dev'));
|
||||
|
||||
// Required by session() middleware
|
||||
// pass the secret for signed cookies
|
||||
// (required by session())
|
||||
app.use(express.cookieParser('keyboard cat'));
|
||||
app.use(cookieParser('keyboard cat'));
|
||||
|
||||
// Populates req.session
|
||||
app.use(express.session({ store: new RedisStore }));
|
||||
app.use(session({ 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,10 +18,10 @@ edit /etc/hosts:
|
||||
|
||||
var main = express();
|
||||
|
||||
main.use(express.logger('dev'));
|
||||
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){
|
||||
@@ -39,8 +41,8 @@ redirect.all('*', function(req, res){
|
||||
|
||||
var app = 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');
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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,12 +15,12 @@ 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');
|
||||
@@ -34,12 +33,12 @@ 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
|
||||
res.render('Readme.md');
|
||||
})
|
||||
});
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(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
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -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
|
||||
@@ -102,14 +78,32 @@ 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.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" });
|
||||
});
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express server listening on port 3000');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
* 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
|
||||
, http = require('http');
|
||||
var mixin = require('utils-merge');
|
||||
var escapeHtml = require('escape-html');
|
||||
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');
|
||||
|
||||
/**
|
||||
* Application prototype.
|
||||
@@ -45,13 +45,11 @@ app.defaultConfiguration = function(){
|
||||
// default settings
|
||||
this.enable('x-powered-by');
|
||||
this.enable('etag');
|
||||
this.set('env', process.env.NODE_ENV || 'development');
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
this.set('env', env);
|
||||
this.set('subdomain offset', 2);
|
||||
debug('booting in %s mode', this.get('env'));
|
||||
|
||||
// implicit middleware
|
||||
this.use(connect.query());
|
||||
this.use(middleware.init(this));
|
||||
debug('booting in %s mode', env);
|
||||
|
||||
// inherit protos
|
||||
this.on('mount', function(parent){
|
||||
@@ -61,18 +59,11 @@ 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;
|
||||
@@ -82,18 +73,94 @@ app.defaultConfiguration = function(){
|
||||
this.set('views', process.cwd() + '/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._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 env = this.get('env');
|
||||
|
||||
this._router.handle(req, res, function(err) {
|
||||
if (done) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
// unhandled error
|
||||
if (err) {
|
||||
// default to 500
|
||||
if (res.statusCode < 400) res.statusCode = 500;
|
||||
debug('default %s', res.statusCode);
|
||||
|
||||
// respect err.status
|
||||
if (err.status) res.statusCode = err.status;
|
||||
|
||||
// production gets a basic error message
|
||||
var msg = 'production' == env
|
||||
? http.STATUS_CODES[res.statusCode]
|
||||
: err.stack || err.toString();
|
||||
msg = escapeHtml(msg);
|
||||
|
||||
// log to stderr in a non-test env
|
||||
if ('test' != env) console.error(err.stack || err.toString());
|
||||
if (res.headersSent) return req.socket.destroy();
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.setHeader('Content-Length', Buffer.byteLength(msg));
|
||||
if ('HEAD' == req.method) return res.end();
|
||||
res.end(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// 404
|
||||
debug('default 404');
|
||||
res.statusCode = 404;
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
if ('HEAD' == req.method) return res.end();
|
||||
res.end('Cannot ' + escapeHtml(req.method) + ' ' + escapeHtml(req.originalUrl) + '\n');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -102,20 +169,21 @@ app.defaultConfiguration = function(){
|
||||
*/
|
||||
|
||||
app.use = function(route, fn){
|
||||
var app;
|
||||
var mount_app;
|
||||
|
||||
// default route to '/'
|
||||
if ('string' != typeof route) fn = route, route = '/';
|
||||
|
||||
// express app
|
||||
if (fn.handle && fn.set) app = fn;
|
||||
if (fn.handle && fn.set) mount_app = fn;
|
||||
|
||||
// restore .app property on req and res
|
||||
if (app) {
|
||||
app.route = route;
|
||||
if (mount_app) {
|
||||
debug('.use app under %s', route);
|
||||
mount_app.mountpath = route;
|
||||
fn = function(req, res, next) {
|
||||
var orig = req.app;
|
||||
app.handle(req, res, function(err){
|
||||
mount_app.handle(req, res, function(err) {
|
||||
req.__proto__ = orig.request;
|
||||
res.__proto__ = orig.response;
|
||||
next(err);
|
||||
@@ -123,17 +191,33 @@ app.use = function(route, fn){
|
||||
};
|
||||
}
|
||||
|
||||
connect.proto.use.call(this, route, fn);
|
||||
this.lazyrouter();
|
||||
this._router.use(route, fn);
|
||||
|
||||
// mounted an app
|
||||
if (app) {
|
||||
app.parent = this;
|
||||
app.emit('mount', this);
|
||||
if (mount_app) {
|
||||
mount_app.parent = this;
|
||||
mount_app.emit('mount', 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`.
|
||||
@@ -176,30 +260,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 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.
|
||||
*
|
||||
* 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
|
||||
@@ -208,27 +272,17 @@ app.engine = function(ext, fn){
|
||||
*/
|
||||
|
||||
app.param = function(name, fn){
|
||||
var self = this
|
||||
, fns = [].slice.call(arguments, 1);
|
||||
var self = this;
|
||||
self.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) {
|
||||
self.param(key, fn);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
self._router.param(name, fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -242,7 +296,7 @@ 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
|
||||
*/
|
||||
@@ -272,7 +326,7 @@ app.set = function(setting, val){
|
||||
|
||||
app.path = function(){
|
||||
return this.parent
|
||||
? this.parent.path() + this.route
|
||||
? this.parent.path() + this.mountpath
|
||||
: '';
|
||||
};
|
||||
|
||||
@@ -338,60 +392,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(...)`.
|
||||
*/
|
||||
@@ -400,16 +400,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;
|
||||
};
|
||||
});
|
||||
@@ -425,10 +419,14 @@ 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;
|
||||
};
|
||||
|
||||
@@ -454,10 +452,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) {
|
||||
@@ -465,13 +463,13 @@ app.render = function(name, options, fn){
|
||||
}
|
||||
|
||||
// merge app.locals
|
||||
utils.merge(opts, this.locals);
|
||||
mixin(opts, this.locals);
|
||||
|
||||
// merge options._locals
|
||||
if (options._locals) utils.merge(opts, options._locals);
|
||||
if (options._locals) mixin(opts, options._locals);
|
||||
|
||||
// merge options
|
||||
utils.merge(opts, options);
|
||||
mixin(opts, options);
|
||||
|
||||
// set .cache unless explicitly provided
|
||||
opts.cache = null == opts.cache
|
||||
@@ -490,7 +488,7 @@ app.render = function(name, options, fn){
|
||||
});
|
||||
|
||||
if (!view.path) {
|
||||
var err = new Error('Failed to lookup view "' + name + '"');
|
||||
var err = new Error('Failed to lookup view "' + name + '" in views directory "' + view.root + '"');
|
||||
err.view = view;
|
||||
return fn(err);
|
||||
}
|
||||
|
||||
@@ -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('utils-merge');
|
||||
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,10 +1,3 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('./utils');
|
||||
|
||||
/**
|
||||
* Initialization middleware, exposing the
|
||||
* request and response to eachother, as well
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
39
lib/middleware/query.js
Normal file
39
lib/middleware/query.js
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var qs = require('qs');
|
||||
var parseUrl = require('parseurl');
|
||||
|
||||
/**
|
||||
* Query:
|
||||
*
|
||||
* Automatically parse the query-string when available,
|
||||
* populating the `req.query` object using
|
||||
* [qs](https://github.com/visionmedia/node-querystring).
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* .use(connect.query())
|
||||
* .use(function(req, res){
|
||||
* res.end(JSON.stringify(req.query));
|
||||
* });
|
||||
*
|
||||
* The `options` passed are provided to qs.parse function.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function query(options){
|
||||
return function query(req, res, next){
|
||||
if (!req.query) {
|
||||
req.query = ~req.url.indexOf('?')
|
||||
? qs.parse(parseUrl(req).query, options)
|
||||
: {};
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
};
|
||||
190
lib/request.js
190
lib/request.js
@@ -1,15 +1,13 @@
|
||||
|
||||
/**
|
||||
* 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 typeis = require('type-is');
|
||||
var http = require('http');
|
||||
var fresh = require('fresh');
|
||||
var parseRange = require('range-parser');
|
||||
var parse = require('parseurl');
|
||||
|
||||
/**
|
||||
* Request prototype.
|
||||
@@ -56,6 +54,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".
|
||||
@@ -99,9 +99,9 @@ req.header = function(name){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.accepts = function(type){
|
||||
var args = arguments.length > 1 ? [].slice.apply(arguments) : type;
|
||||
return utils.accepts(args, this.get('Accept'));
|
||||
req.accepts = function(){
|
||||
var accept = accepts(this);
|
||||
return accept.types.apply(accept, arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -112,11 +112,15 @@ req.accepts = function(type){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.acceptsEncoding = function(encoding){
|
||||
return !! ~this.acceptedEncodings.indexOf(encoding);
|
||||
req.acceptsEncoding = // backwards compatibility
|
||||
req.acceptsEncodings = function(){
|
||||
var accept = accepts(this);
|
||||
return accept.encodings.apply(accept, arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
* To do: update docs.
|
||||
*
|
||||
* Check if the given `charset` is acceptable,
|
||||
* otherwise you should respond with 406 "Not Acceptable".
|
||||
*
|
||||
@@ -125,14 +129,15 @@ req.acceptsEncoding = function(encoding){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.acceptsCharset = function(charset){
|
||||
var accepted = this.acceptedCharsets;
|
||||
return accepted.length
|
||||
? !! ~accepted.indexOf(charset)
|
||||
: true;
|
||||
req.acceptsCharset = // backwards compatibility
|
||||
req.acceptsCharsets = function(){
|
||||
var accept = accepts(this);
|
||||
return accept.charsets.apply(accept, arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
* To do: update docs.
|
||||
*
|
||||
* Check if the given `lang` is acceptable,
|
||||
* otherwise you should respond with 406 "Not Acceptable".
|
||||
*
|
||||
@@ -141,11 +146,10 @@ req.acceptsCharset = function(charset){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.acceptsLanguage = function(lang){
|
||||
var accepted = this.acceptedLanguages;
|
||||
return accepted.length
|
||||
? !! ~accepted.indexOf(lang)
|
||||
: true;
|
||||
req.acceptsLanguage = // backwards compatibility
|
||||
req.acceptsLanguages = function(){
|
||||
var accept = accepts(this);
|
||||
return accept.languages.apply(accept, arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -174,98 +178,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`.
|
||||
*
|
||||
@@ -275,7 +187,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]
|
||||
@@ -319,19 +231,9 @@ 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);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -400,36 +302,6 @@ req.__defineGetter__('ips', function(){
|
||||
: [];
|
||||
});
|
||||
|
||||
/**
|
||||
* 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] };
|
||||
});
|
||||
|
||||
/**
|
||||
* Return subdomains as an array.
|
||||
*
|
||||
|
||||
162
lib/response.js
162
lib/response.js
@@ -2,20 +2,21 @@
|
||||
* 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;
|
||||
var http = require('http');
|
||||
var path = require('path');
|
||||
var mixin = require('utils-merge');
|
||||
var escapeHtml = require('escape-html');
|
||||
var sign = require('cookie-signature').sign;
|
||||
var normalizeType = require('./utils').normalizeType;
|
||||
var normalizeTypes = require('./utils').normalizeTypes;
|
||||
var contentDisposition = require('./utils').contentDisposition;
|
||||
var etag = require('./utils').etag;
|
||||
var statusCodes = http.STATUS_CODES;
|
||||
var cookie = require('cookie');
|
||||
var send = require('send');
|
||||
var basename = path.basename;
|
||||
var extname = path.extname;
|
||||
var mime = send.mime;
|
||||
|
||||
/**
|
||||
* Response prototype.
|
||||
@@ -106,10 +107,7 @@ res.send = function(body){
|
||||
break;
|
||||
// string defaulting to html
|
||||
case 'string':
|
||||
if (!this.get('Content-Type')) {
|
||||
this.charset = this.charset || 'utf-8';
|
||||
this.type('html');
|
||||
}
|
||||
if (!this.get('Content-Type')) this.type('html');
|
||||
break;
|
||||
case 'boolean':
|
||||
case 'object':
|
||||
@@ -132,7 +130,7 @@ res.send = function(body){
|
||||
|
||||
// ETag support
|
||||
// TODO: W/ support
|
||||
if (app.settings.etag && len > 1024 && 'GET' == req.method) {
|
||||
if (app.settings.etag && len && 'GET' == req.method) {
|
||||
if (!this.get('ETag')) {
|
||||
this.set('ETag', etag(body));
|
||||
}
|
||||
@@ -189,7 +187,6 @@ res.json = function(obj){
|
||||
var body = JSON.stringify(obj, replacer, spaces);
|
||||
|
||||
// content-type
|
||||
this.charset = this.charset || 'utf-8';
|
||||
this.get('Content-Type') || this.set('Content-Type', 'application/json');
|
||||
|
||||
return this.send(body);
|
||||
@@ -233,7 +230,6 @@ res.jsonp = function(obj){
|
||||
var callback = this.req.query[app.get('jsonp callback name')];
|
||||
|
||||
// content-type
|
||||
this.charset = this.charset || 'utf-8';
|
||||
this.set('Content-Type', 'application/json');
|
||||
|
||||
// jsonp
|
||||
@@ -260,6 +256,9 @@ res.jsonp = function(obj){
|
||||
*
|
||||
* - `maxAge` defaulting to 0
|
||||
* - `root` root directory for relative filenames
|
||||
* - `hidden` serve hidden files, defaulting to false
|
||||
*
|
||||
* Other options are passed along to `send`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
@@ -288,11 +287,12 @@ res.jsonp = function(obj){
|
||||
*/
|
||||
|
||||
res.sendfile = function(path, options, fn){
|
||||
var self = this
|
||||
, req = self.req
|
||||
, next = this.req.next
|
||||
, options = options || {}
|
||||
, done;
|
||||
options = options || {};
|
||||
var self = this;
|
||||
var req = self.req;
|
||||
var next = this.req.next;
|
||||
var done;
|
||||
|
||||
|
||||
// support function as second arg
|
||||
if ('function' == typeof options) {
|
||||
@@ -310,13 +310,13 @@ res.sendfile = function(path, options, fn){
|
||||
|
||||
// clean up
|
||||
cleanup();
|
||||
if (!self.headerSent) self.removeHeader('Content-Disposition');
|
||||
if (!self.headersSent) self.removeHeader('Content-Disposition');
|
||||
|
||||
// callback available
|
||||
if (fn) return fn(err);
|
||||
|
||||
// list in limbo if there's no callback
|
||||
if (self.headerSent) return;
|
||||
if (self.headersSent) return;
|
||||
|
||||
// delegate
|
||||
next(err);
|
||||
@@ -334,10 +334,11 @@ res.sendfile = function(path, options, fn){
|
||||
req.socket.removeListener('error', error);
|
||||
}
|
||||
|
||||
// Back-compat
|
||||
options.maxage = options.maxage || options.maxAge || 0;
|
||||
|
||||
// transfer
|
||||
var file = send(req, path);
|
||||
if (options.root) file.root(options.root);
|
||||
file.maxage(options.maxAge || 0);
|
||||
var file = send(req, path, options);
|
||||
file.on('error', error);
|
||||
file.on('directory', next);
|
||||
file.on('stream', stream);
|
||||
@@ -351,7 +352,7 @@ res.sendfile = function(path, options, fn){
|
||||
* 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()`.
|
||||
*
|
||||
@@ -369,7 +370,7 @@ res.download = function(path, filename, fn){
|
||||
}
|
||||
|
||||
filename = filename || path;
|
||||
this.set('Content-Disposition', 'attachment; filename="' + basename(filename) + '"');
|
||||
this.set('Content-Disposition', contentDisposition(filename));
|
||||
return this.sendfile(path, fn);
|
||||
};
|
||||
|
||||
@@ -455,8 +456,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;
|
||||
@@ -467,10 +468,7 @@ res.format = function(obj){
|
||||
this.vary("Accept");
|
||||
|
||||
if (key) {
|
||||
var type = normalizeType(key).value;
|
||||
var charset = mime.charsets.lookup(type);
|
||||
if (charset) type += '; charset=' + charset;
|
||||
this.set('Content-Type', type);
|
||||
this.set('Content-Type', normalizeType(key).value);
|
||||
obj[key](req, this, next);
|
||||
} else if (fn) {
|
||||
fn();
|
||||
@@ -494,9 +492,7 @@ res.format = function(obj){
|
||||
|
||||
res.attachment = function(filename){
|
||||
if (filename) this.type(extname(filename));
|
||||
this.set('Content-Disposition', filename
|
||||
? 'attachment; filename="' + basename(filename) + '"'
|
||||
: 'attachment');
|
||||
this.set('Content-Disposition', contentDisposition(filename));
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -523,6 +519,10 @@ res.header = function(field, val){
|
||||
if (2 == arguments.length) {
|
||||
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) {
|
||||
@@ -556,7 +556,7 @@ res.get = function(field){
|
||||
res.clearCookie = function(name, options){
|
||||
var opts = { expires: new Date(1), path: '/' };
|
||||
return this.cookie(name, '', options
|
||||
? utils.merge(opts, options)
|
||||
? mixin(opts, options)
|
||||
: opts);
|
||||
};
|
||||
|
||||
@@ -584,10 +584,10 @@ res.clearCookie = function(name, options){
|
||||
*/
|
||||
|
||||
res.cookie = function(name, val, options){
|
||||
options = utils.merge({}, options);
|
||||
options = mixin({}, 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);
|
||||
@@ -596,7 +596,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;
|
||||
};
|
||||
|
||||
@@ -604,57 +615,24 @@ 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
|
||||
* @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);
|
||||
@@ -683,9 +661,9 @@ res.location = function(url){
|
||||
*/
|
||||
|
||||
res.redirect = function(url){
|
||||
var head = 'HEAD' == this.req.method
|
||||
, status = 302
|
||||
, body;
|
||||
var head = 'HEAD' == this.req.method;
|
||||
var status = 302;
|
||||
var body;
|
||||
|
||||
// allow status / url
|
||||
if (2 == arguments.length) {
|
||||
@@ -708,7 +686,7 @@ res.redirect = function(url){
|
||||
},
|
||||
|
||||
html: function(){
|
||||
var u = utils.escape(url);
|
||||
var u = escapeHtml(url);
|
||||
body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>';
|
||||
},
|
||||
|
||||
@@ -778,10 +756,10 @@ res.vary = function(field){
|
||||
*/
|
||||
|
||||
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) {
|
||||
|
||||
@@ -2,48 +2,74 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Route = require('./route')
|
||||
, utils = require('../utils')
|
||||
, methods = require('methods')
|
||||
, debug = require('debug')('express:router')
|
||||
, parse = require('connect').utils.parseUrl;
|
||||
|
||||
/**
|
||||
* Expose `Router` constructor.
|
||||
*/
|
||||
|
||||
exports = module.exports = Router;
|
||||
var Route = require('./route');
|
||||
var Layer = require('./layer');
|
||||
var methods = require('methods');
|
||||
var debug = require('debug')('express:router');
|
||||
var parseUrl = require('parseurl');
|
||||
|
||||
/**
|
||||
* 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.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(name, fn){
|
||||
// param logic
|
||||
if ('function' == typeof name) {
|
||||
this._params.push(name);
|
||||
@@ -51,9 +77,13 @@ Router.prototype.param = function(name, fn){
|
||||
}
|
||||
|
||||
// apply param functions
|
||||
var params = this._params
|
||||
, len = params.length
|
||||
, ret;
|
||||
var params = this._params;
|
||||
var len = params.length;
|
||||
var ret;
|
||||
|
||||
if (name[0] === ':') {
|
||||
name = name.substr(1);
|
||||
}
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
if (ret = params[i](name, fn)) {
|
||||
@@ -72,240 +102,284 @@ 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;
|
||||
|
||||
debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl);
|
||||
|
||||
// route dispatch
|
||||
(function pass(i, err){
|
||||
var paramCallbacks
|
||||
, paramIndex = 0
|
||||
, paramVal
|
||||
, route
|
||||
, keys
|
||||
, key;
|
||||
|
||||
// match next route
|
||||
function nextRoute(err) {
|
||||
pass(req._route_index + 1, err);
|
||||
}
|
||||
|
||||
// match route
|
||||
req.route = route = self.matchRequest(req, i);
|
||||
|
||||
// implied OPTIONS
|
||||
if (!route && 'OPTIONS' == req.method) return self._options(req, res);
|
||||
|
||||
// no route
|
||||
if (!route) return next(err);
|
||||
debug('matched %s %s', route.method, route.path);
|
||||
|
||||
// we have a route
|
||||
// start at param 0
|
||||
req.params = route.params;
|
||||
keys = route.keys;
|
||||
i = 0;
|
||||
|
||||
// param callbacks
|
||||
function param(err) {
|
||||
paramIndex = 0;
|
||||
key = keys[i++];
|
||||
paramVal = key && req.params[key.name];
|
||||
paramCallbacks = key && params[key.name];
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
param(err);
|
||||
|
||||
// single param callbacks
|
||||
function paramCallback(err) {
|
||||
var fn = paramCallbacks[paramIndex++];
|
||||
if (err || !fn) return param(err);
|
||||
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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond to __OPTIONS__ method.
|
||||
*
|
||||
* @param {IncomingMessage} req
|
||||
* @param {ServerResponse} res
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype._options = function(req, res){
|
||||
var path = parse(req).pathname
|
||||
, body = this._optionsFor(path).join(',');
|
||||
res.set('Allow', body).send(body);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return an array of HTTP verbs or "options" for `path`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype._optionsFor = function(path){
|
||||
proto.handle = function(req, res, done) {
|
||||
var self = this;
|
||||
return methods.filter(function(method){
|
||||
var routes = self.map[method];
|
||||
if (!routes || 'options' == method) return;
|
||||
for (var i = 0, len = routes.length; i < len; ++i) {
|
||||
if (routes[i].match(path)) return true;
|
||||
}
|
||||
}).map(function(method){
|
||||
return method.toUpperCase();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
debug('dispatching %s %s', req.method, req.url);
|
||||
|
||||
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;
|
||||
var method = req.method.toLowerCase();
|
||||
|
||||
// HEAD support
|
||||
if (!head && 'head' == method) {
|
||||
route = this.matchRequest(req, i, true);
|
||||
if (route) return route;
|
||||
method = 'get';
|
||||
var search = 1 + req.url.indexOf('?');
|
||||
var pathlength = search ? search - 1 : req.url.length;
|
||||
var fqdn = 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;
|
||||
|
||||
// store options for OPTIONS request
|
||||
// only used if OPTIONS request
|
||||
var options = [];
|
||||
|
||||
// middleware and routes
|
||||
var stack = self.stack;
|
||||
|
||||
// for options requests, respond with a default if nothing else responds
|
||||
if (method === 'options') {
|
||||
var old = done;
|
||||
done = function(err) {
|
||||
if (err || options.length === 0) return old(err);
|
||||
|
||||
var body = options.join(',');
|
||||
return res.set('Allow', body).send(body);
|
||||
};
|
||||
}
|
||||
|
||||
// routes for this method
|
||||
if (routes = routes[method]) {
|
||||
(function next(err) {
|
||||
if (err === 'route') {
|
||||
err = undefined;
|
||||
}
|
||||
|
||||
// matching routes
|
||||
for (var len = routes.length; i < len; ++i) {
|
||||
route = routes[i];
|
||||
if (route.match(path)) {
|
||||
req._route_index = i;
|
||||
return route;
|
||||
var layer = stack[idx++];
|
||||
if (!layer) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
if (slashAdded) {
|
||||
req.url = req.url.substr(1);
|
||||
slashAdded = false;
|
||||
}
|
||||
|
||||
req.url = protohost + removed + req.url.substr(protohost.length);
|
||||
req.originalUrl = req.originalUrl || req.url;
|
||||
removed = '';
|
||||
|
||||
try {
|
||||
var path = parseUrl(req).pathname;
|
||||
if (undefined == path) path = '/';
|
||||
|
||||
if (!layer.match(path)) return next(err);
|
||||
|
||||
// route object and not middleware
|
||||
var route = layer.route;
|
||||
|
||||
// if final route, then we support options
|
||||
if (route) {
|
||||
// we don't run any routes with error first
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
req.route = route;
|
||||
|
||||
// we can now dispatch to the route
|
||||
if (method === 'options' && !route.methods['options']) {
|
||||
options.push.apply(options, route._options());
|
||||
}
|
||||
}
|
||||
|
||||
req.params = layer.params;
|
||||
|
||||
// this should be done for the layer
|
||||
return self.process_params(layer, req, res, function(err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
if (route) {
|
||||
return layer.handle(req, res, next);
|
||||
}
|
||||
|
||||
trim_prefix();
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
|
||||
function trim_prefix() {
|
||||
var c = path[layer.path.length];
|
||||
if (c && '/' != c && '.' != c) return next(err);
|
||||
|
||||
// Trim off the part of the url that matches the route
|
||||
// middleware (.use stuff) needs to have the path stripped
|
||||
debug('trim prefix (%s) from url %s', removed, req.url);
|
||||
removed = layer.path;
|
||||
req.url = protohost + req.url.substr(protohost.length + removed.length);
|
||||
|
||||
// Ensure leading slash
|
||||
if (!fqdn && '/' != req.url[0]) {
|
||||
req.url = '/' + req.url;
|
||||
slashAdded = true;
|
||||
}
|
||||
|
||||
debug('%s %s : %s', layer.handle.name || 'anonymous', layer.path, req.originalUrl);
|
||||
var arity = layer.handle.length;
|
||||
if (err) {
|
||||
if (arity === 4) {
|
||||
layer.handle(err, req, res, next);
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
} else if (arity < 4) {
|
||||
layer.handle(req, res, next);
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
})();
|
||||
};
|
||||
|
||||
/**
|
||||
* Process any parameters for the route.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
proto.process_params = function(route, req, res, done) {
|
||||
var params = this.params;
|
||||
|
||||
// captured parameters from the route, keys and values
|
||||
var keys = route.keys;
|
||||
|
||||
// fast track
|
||||
if (!keys || keys.length === 0) {
|
||||
return done();
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var paramIndex = 0;
|
||||
var key;
|
||||
var paramVal;
|
||||
var paramCallbacks;
|
||||
|
||||
// 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++];
|
||||
paramVal = key && req.params[key.name];
|
||||
paramCallbacks = key && params[key.name];
|
||||
|
||||
try {
|
||||
if (paramCallbacks && undefined !== paramVal) {
|
||||
return paramCallback();
|
||||
} else if (key) {
|
||||
return param();
|
||||
}
|
||||
} catch (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
// single param callbacks
|
||||
function paramCallback(err) {
|
||||
var fn = paramCallbacks[paramIndex++];
|
||||
if (err || !fn) return param(err);
|
||||
fn(req, res, paramCallback, paramVal, key.name);
|
||||
}
|
||||
|
||||
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.
|
||||
*
|
||||
* @param {String|Function} route
|
||||
* @param {Function} fn
|
||||
* @return {app} for chaining
|
||||
* @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(route, fn){
|
||||
// default route to '/'
|
||||
if ('string' != typeof route) {
|
||||
fn = route;
|
||||
route = '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Route `method`, `path`, and one or more callbacks.
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} path
|
||||
* @param {Function} callback...
|
||||
* @return {Router} for chaining
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype.route = function(method, path, callbacks){
|
||||
var method = method.toLowerCase()
|
||||
, callbacks = utils.flatten([].slice.call(arguments, 2));
|
||||
|
||||
// ensure path was given
|
||||
if (!path) throw new Error('Router#' + method + '() requires a path');
|
||||
|
||||
// ensure all callbacks are functions
|
||||
callbacks.forEach(function(fn){
|
||||
if ('function' == typeof fn) return;
|
||||
if (typeof fn !== 'function') {
|
||||
var type = {}.toString.call(fn);
|
||||
var msg = '.' + method + '() requires callback functions but got a ' + type;
|
||||
var msg = 'Router.use() requires callback functions but got a ' + type;
|
||||
throw new Error(msg);
|
||||
});
|
||||
}
|
||||
|
||||
// create the route
|
||||
debug('defined %s %s', method, path);
|
||||
var route = new Route(method, path, callbacks, {
|
||||
// strip trailing slash
|
||||
if ('/' == route[route.length - 1]) {
|
||||
route = route.slice(0, -1);
|
||||
}
|
||||
|
||||
var layer = new Layer(route, {
|
||||
sensitive: this.caseSensitive,
|
||||
strict: this.strict
|
||||
});
|
||||
strict: this.strict,
|
||||
end: false
|
||||
}, fn);
|
||||
|
||||
// add it
|
||||
(this.map[method] = this.map[method] || []).push(route);
|
||||
// add the middleware
|
||||
debug('use %s %s', route || '/', fn.name || 'anonymous');
|
||||
|
||||
this.stack.push(layer);
|
||||
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;
|
||||
};
|
||||
});
|
||||
|
||||
67
lib/router/layer.js
Normal file
67
lib/router/layer.js
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var pathRegexp = require('path-to-regexp');
|
||||
var debug = require('debug')('express:router:layer');
|
||||
|
||||
/**
|
||||
* Expose `Layer`.
|
||||
*/
|
||||
|
||||
module.exports = Layer;
|
||||
|
||||
function Layer(path, options, fn) {
|
||||
if (!(this instanceof Layer)) {
|
||||
return new Layer(path, options, fn);
|
||||
}
|
||||
|
||||
debug('new %s', path);
|
||||
options = options || {};
|
||||
this.regexp = pathRegexp(path, this.keys = [], options);
|
||||
this.handle = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this route matches `path`, if so
|
||||
* populate `.params`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Layer.prototype.match = function(path){
|
||||
var keys = this.keys;
|
||||
var params = this.params = {};
|
||||
var m = this.regexp.exec(path);
|
||||
var n = 0;
|
||||
var key;
|
||||
var val;
|
||||
|
||||
if (!m) return false;
|
||||
|
||||
this.path = m[0];
|
||||
|
||||
for (var i = 1, len = m.length; i < len; ++i) {
|
||||
key = keys[i - 1];
|
||||
|
||||
try {
|
||||
val = 'string' == typeof m[i]
|
||||
? decodeURIComponent(m[i])
|
||||
: m[i];
|
||||
} catch(e) {
|
||||
var err = new Error("Failed to decode param '" + m[i] + "'");
|
||||
err.status = 400;
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (key) {
|
||||
params[key.name] = val;
|
||||
} else {
|
||||
params[n++] = val;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -1,8 +1,9 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var debug = require('debug')('express:router:route');
|
||||
var methods = require('methods');
|
||||
var utils = require('../utils');
|
||||
|
||||
/**
|
||||
@@ -12,61 +13,179 @@ var utils = require('../utils');
|
||||
module.exports = Route;
|
||||
|
||||
/**
|
||||
* Initialize `Route` with the given HTTP `method`, `path`,
|
||||
* and an array of `callbacks` and `options`.
|
||||
* Initialize `Route` with the given `path`,
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `sensitive` enable case-sensitive routes
|
||||
* - `strict` enable strict matching for trailing slashes
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} path
|
||||
* @param {Array} callbacks
|
||||
* @param {Object} options.
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function Route(method, path, callbacks, options) {
|
||||
options = options || {};
|
||||
function Route(path) {
|
||||
debug('new %s', path);
|
||||
this.path = path;
|
||||
this.method = method;
|
||||
this.callbacks = callbacks;
|
||||
this.regexp = utils.pathRegexp(path
|
||||
, this.keys = []
|
||||
, options.sensitive
|
||||
, options.strict);
|
||||
this.stack = undefined;
|
||||
|
||||
// route handlers for various http methods
|
||||
this.methods = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this route matches `path`, if so
|
||||
* populate `.params`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {Boolean}
|
||||
* @return {Array} supported HTTP methods
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Route.prototype.match = function(path){
|
||||
var keys = this.keys
|
||||
, params = this.params = []
|
||||
, m = this.regexp.exec(path);
|
||||
Route.prototype._options = function(){
|
||||
return Object.keys(this.methods).map(function(method) {
|
||||
return method.toUpperCase();
|
||||
});
|
||||
};
|
||||
|
||||
if (!m) return false;
|
||||
/**
|
||||
* dispatch req, res into this route
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
for (var i = 1, len = m.length; i < len; ++i) {
|
||||
var key = keys[i - 1];
|
||||
Route.prototype.dispatch = function(req, res, done){
|
||||
var self = this;
|
||||
var method = req.method.toLowerCase();
|
||||
|
||||
var val = 'string' == typeof m[i]
|
||||
? utils.decode(m[i])
|
||||
: m[i];
|
||||
|
||||
if (key) {
|
||||
params[key.name] = val;
|
||||
} else {
|
||||
params.push(val);
|
||||
}
|
||||
if (method === 'head' && !this.methods['head']) {
|
||||
method = 'get';
|
||||
}
|
||||
|
||||
return true;
|
||||
req.route = self;
|
||||
|
||||
// single middleware route case
|
||||
if (typeof this.stack === 'function') {
|
||||
this.stack(req, res, done);
|
||||
return;
|
||||
}
|
||||
|
||||
var stack = self.stack;
|
||||
if (!stack) {
|
||||
return done();
|
||||
}
|
||||
|
||||
var idx = 0;
|
||||
(function next_layer(err) {
|
||||
if (err && err === 'route') {
|
||||
return done();
|
||||
}
|
||||
|
||||
var layer = stack[idx++];
|
||||
if (!layer) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
if (layer.method && layer.method !== method) {
|
||||
return next_layer(err);
|
||||
}
|
||||
|
||||
var arity = layer.handle.length;
|
||||
if (err) {
|
||||
if (arity < 4) {
|
||||
return next_layer(err);
|
||||
}
|
||||
|
||||
try {
|
||||
layer.handle(err, req, res, next_layer);
|
||||
} catch (err) {
|
||||
next_layer(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (arity > 3) {
|
||||
return next_layer();
|
||||
}
|
||||
|
||||
try {
|
||||
layer.handle(req, res, next_layer);
|
||||
} catch (err) {
|
||||
next_layer(err);
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a handler for all HTTP verbs to this route.
|
||||
*
|
||||
* Behaves just like middleware and can respond or call `next`
|
||||
* to continue processing.
|
||||
*
|
||||
* You can use multiple `.all` call to add multiple handlers.
|
||||
*
|
||||
* function check_something(req, res, next){
|
||||
* next();
|
||||
* };
|
||||
*
|
||||
* function validate_user(req, res, next){
|
||||
* next();
|
||||
* };
|
||||
*
|
||||
* route
|
||||
* .all(validate_user)
|
||||
* .all(check_something)
|
||||
* .get(function(req, res, next){
|
||||
* res.send('hello world');
|
||||
* });
|
||||
*
|
||||
* @param {function} handler
|
||||
* @return {Route} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Route.prototype.all = function(){
|
||||
var self = this;
|
||||
var callbacks = utils.flatten([].slice.call(arguments));
|
||||
callbacks.forEach(function(fn) {
|
||||
if (typeof fn !== 'function') {
|
||||
var type = {}.toString.call(fn);
|
||||
var msg = 'Route.all() requires callback functions but got a ' + type;
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
if (!self.stack) {
|
||||
self.stack = fn;
|
||||
}
|
||||
else if (typeof self.stack === 'function') {
|
||||
self.stack = [{ handle: self.stack }, { handle: fn }];
|
||||
}
|
||||
else {
|
||||
self.stack.push({ handle: fn });
|
||||
}
|
||||
});
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
methods.forEach(function(method){
|
||||
Route.prototype[method] = function(){
|
||||
var self = this;
|
||||
var callbacks = utils.flatten([].slice.call(arguments));
|
||||
|
||||
callbacks.forEach(function(fn) {
|
||||
if (typeof fn !== 'function') {
|
||||
var type = {}.toString.call(fn);
|
||||
var msg = 'Route.' + method + '() requires callback functions but got a ' + type;
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
debug('%s %s', method, self.path);
|
||||
|
||||
if (!self.methods[method]) {
|
||||
self.methods[method] = true;
|
||||
}
|
||||
|
||||
if (!self.stack) {
|
||||
self.stack = [];
|
||||
}
|
||||
else if (typeof self.stack === 'function') {
|
||||
self.stack = [{ handle: self.stack }];
|
||||
}
|
||||
|
||||
self.stack.push({ method: method, handle: fn });
|
||||
});
|
||||
return self;
|
||||
};
|
||||
});
|
||||
|
||||
233
lib/utils.js
233
lib/utils.js
@@ -1,16 +1,10 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var mime = require('connect').mime
|
||||
, crc32 = require('buffer-crc32');
|
||||
|
||||
/**
|
||||
* toString ref.
|
||||
*/
|
||||
|
||||
var toString = {}.toString;
|
||||
var mime = require('send').mime;
|
||||
var crc32 = require('buffer-crc32');
|
||||
var basename = require('path').basename;
|
||||
|
||||
/**
|
||||
* Return ETag for `body`.
|
||||
@@ -24,25 +18,6 @@ exports.etag = function(body){
|
||||
return '"' + crc32.signed(body) + '"';
|
||||
};
|
||||
|
||||
/**
|
||||
* Make `locals()` bound to the given `obj`.
|
||||
*
|
||||
* This is used for `app.locals` and `res.locals`.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @return {Function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.locals = function(){
|
||||
function locals(obj){
|
||||
for (var key in obj) locals[key] = obj[key];
|
||||
return obj;
|
||||
};
|
||||
|
||||
return locals;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `path` looks absolute.
|
||||
*
|
||||
@@ -66,8 +41,8 @@ exports.isAbsolute = function(path){
|
||||
*/
|
||||
|
||||
exports.flatten = function(arr, ret){
|
||||
var ret = ret || []
|
||||
, len = arr.length;
|
||||
ret = ret || [];
|
||||
var len = arr.length;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
if (Array.isArray(arr[i])) {
|
||||
exports.flatten(arr[i], ret);
|
||||
@@ -111,125 +86,25 @@ exports.normalizeTypes = function(types){
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the acceptable type in `types`, if any.
|
||||
* Generate Content-Disposition header appropriate for the filename.
|
||||
* non-ascii filenames are urlencoded and a filename* parameter is added
|
||||
*
|
||||
* @param {Array} types
|
||||
* @param {String} str
|
||||
* @param {String} filename
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.acceptsArray = function(types, str){
|
||||
// accept anything when Accept is not present
|
||||
if (!str) return types[0];
|
||||
|
||||
// parse
|
||||
var accepted = exports.parseAccept(str)
|
||||
, normalized = exports.normalizeTypes(types)
|
||||
, len = accepted.length;
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
for (var j = 0, jlen = types.length; j < jlen; ++j) {
|
||||
if (exports.accept(normalized[j], accepted[i])) {
|
||||
return types[j];
|
||||
}
|
||||
}
|
||||
exports.contentDisposition = function(filename){
|
||||
var ret = 'attachment';
|
||||
if (filename) {
|
||||
filename = basename(filename);
|
||||
// if filename contains non-ascii characters, add a utf-8 version ala RFC 5987
|
||||
ret = /[^\040-\176]/.test(filename)
|
||||
? 'attachment; filename=' + encodeURI(filename) + '; filename*=UTF-8\'\'' + encodeURI(filename)
|
||||
: 'attachment; filename="' + filename + '"';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `type(s)` are acceptable based on
|
||||
* the given `str`.
|
||||
*
|
||||
* @param {String|Array} type(s)
|
||||
* @param {String} str
|
||||
* @return {Boolean|String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.accepts = function(type, str){
|
||||
if ('string' == typeof type) type = type.split(/ *, */);
|
||||
return exports.acceptsArray(type, str);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `type` array is acceptable for `other`.
|
||||
*
|
||||
* @param {Object} type
|
||||
* @param {Object} other
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.accept = function(type, other){
|
||||
var t = type.value.split('/');
|
||||
return (t[0] == other.type || '*' == other.type)
|
||||
&& (t[1] == other.subtype || '*' == other.subtype)
|
||||
&& paramsEqual(type.params, other.params);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if accept params are equal.
|
||||
*
|
||||
* @param {Object} a
|
||||
* @param {Object} b
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function paramsEqual(a, b){
|
||||
return !Object.keys(a).some(function(k) {
|
||||
return a[k] != b[k];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse accept `str`, returning
|
||||
* an array objects containing
|
||||
* `.type` and `.subtype` along
|
||||
* with the values provided by
|
||||
* `parseQuality()`.
|
||||
*
|
||||
* @param {Type} name
|
||||
* @return {Type}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.parseAccept = function(str){
|
||||
return exports
|
||||
.parseParams(str)
|
||||
.map(function(obj){
|
||||
var parts = obj.value.split('/');
|
||||
obj.type = parts[0];
|
||||
obj.subtype = parts[1];
|
||||
return obj;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse quality `str`, returning an
|
||||
* array of objects with `.value`,
|
||||
* `.quality` and optional `.params`
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.parseParams = function(str){
|
||||
return str
|
||||
.split(/ *, */)
|
||||
.map(acceptParams)
|
||||
.filter(function(obj){
|
||||
return obj.quality;
|
||||
})
|
||||
.sort(function(a, b){
|
||||
if (a.quality === b.quality) {
|
||||
return a.originalIndex - b.originalIndex;
|
||||
} else {
|
||||
return b.quality - a.quality;
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -257,77 +132,3 @@ function acceptParams(str, index) {
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape special characters in the given string of html.
|
||||
*
|
||||
* @param {String} html
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.escape = function(html) {
|
||||
return String(html)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize the given path string,
|
||||
* returning a regular expression.
|
||||
*
|
||||
* An empty array should be passed,
|
||||
* which will contain the placeholder
|
||||
* key names. For example "/user/:id" will
|
||||
* then contain ["id"].
|
||||
*
|
||||
* @param {String|RegExp|Array} path
|
||||
* @param {Array} keys
|
||||
* @param {Boolean} sensitive
|
||||
* @param {Boolean} strict
|
||||
* @return {RegExp}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.pathRegexp = function(path, keys, sensitive, strict) {
|
||||
if (toString.call(path) == '[object RegExp]') return path;
|
||||
if (Array.isArray(path)) path = '(' + path.join('|') + ')';
|
||||
path = path
|
||||
.concat(strict ? '' : '/?')
|
||||
.replace(/\/\(/g, '(?:/')
|
||||
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function(_, slash, format, key, capture, optional, star){
|
||||
keys.push({ name: key, optional: !! optional });
|
||||
slash = slash || '';
|
||||
return ''
|
||||
+ (optional ? '' : slash)
|
||||
+ '(?:'
|
||||
+ (optional ? slash : '')
|
||||
+ (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
|
||||
+ (optional || '')
|
||||
+ (star ? '(/*)?' : '');
|
||||
})
|
||||
.replace(/([\/.])/g, '\\$1')
|
||||
.replace(/\*/g, '(.*)');
|
||||
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decodes a URI component. Returns
|
||||
* the original string if the component
|
||||
* is malformed.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.decode = function(str) {
|
||||
try {
|
||||
return decodeURIComponent(str);
|
||||
} catch (e) {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
16
lib/view.js
16
lib/view.js
@@ -2,14 +2,14 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var path = require('path')
|
||||
, fs = require('fs')
|
||||
, utils = require('./utils')
|
||||
, dirname = path.dirname
|
||||
, basename = path.basename
|
||||
, extname = path.extname
|
||||
, exists = fs.existsSync || path.existsSync
|
||||
, join = path.join;
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var utils = require('./utils');
|
||||
var dirname = path.dirname;
|
||||
var basename = path.basename;
|
||||
var extname = path.extname;
|
||||
var exists = fs.existsSync || path.existsSync;
|
||||
var join = path.join;
|
||||
|
||||
/**
|
||||
* Expose `View`.
|
||||
|
||||
70
package.json
70
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Sinatra inspired web development framework",
|
||||
"version": "3.4.3",
|
||||
"version": "4.1.0",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
{
|
||||
@@ -19,31 +19,52 @@
|
||||
{
|
||||
"name": "Guillermo Rauch",
|
||||
"email": "rauchg@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Jonathan Ong",
|
||||
"email": "me@jongleberry.com"
|
||||
},
|
||||
{
|
||||
"name": "Roman Shtylman",
|
||||
"email": "shtylman+expressjs@gmail.com"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"connect": "2.10.1",
|
||||
"commander": "1.3.2",
|
||||
"range-parser": "0.0.4",
|
||||
"mkdirp": "0.3.5",
|
||||
"cookie": "0.1.0",
|
||||
"parseurl": "1.0.1",
|
||||
"accepts": "1.0.1",
|
||||
"type-is": "1.1.0",
|
||||
"range-parser": "1.0.0",
|
||||
"cookie": "0.1.2",
|
||||
"buffer-crc32": "0.2.1",
|
||||
"fresh": "0.2.0",
|
||||
"methods": "0.0.1",
|
||||
"send": "0.1.4",
|
||||
"cookie-signature": "1.0.1",
|
||||
"debug": "*"
|
||||
"fresh": "0.2.2",
|
||||
"methods": "0.1.0",
|
||||
"send": "0.3.0",
|
||||
"cookie-signature": "1.0.3",
|
||||
"merge-descriptors": "0.0.2",
|
||||
"utils-merge": "1.0.0",
|
||||
"escape-html": "1.0.1",
|
||||
"qs": "0.6.6",
|
||||
"serve-static": "1.1.0",
|
||||
"path-to-regexp": "0.1.2",
|
||||
"debug": ">= 0.7.3 < 1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ejs": "*",
|
||||
"mocha": "*",
|
||||
"jade": "0.30.0",
|
||||
"hjs": "*",
|
||||
"stylus": "*",
|
||||
"should": "2",
|
||||
"connect-redis": "*",
|
||||
"marked": "*",
|
||||
"supertest": "0.6.0"
|
||||
"mocha": "~1.18.2",
|
||||
"body-parser": "1.0.2",
|
||||
"connect-redis": "~2.0.0",
|
||||
"ejs": "~1.0.0",
|
||||
"express-session": "1.0.3",
|
||||
"jade": "~0.35.0",
|
||||
"marked": "0.3.2",
|
||||
"multiparty": "~3.2.4",
|
||||
"static-favicon": "1.0.2",
|
||||
"hjs": "~0.0.6",
|
||||
"should": "~3.3.1",
|
||||
"supertest": "~0.11.0",
|
||||
"method-override": "1.0.0",
|
||||
"cookie-parser": "1.0.1",
|
||||
"morgan": "1.0.0",
|
||||
"vhost": "1.0.0"
|
||||
},
|
||||
"keywords": [
|
||||
"express",
|
||||
@@ -57,15 +78,12 @@
|
||||
"api"
|
||||
],
|
||||
"repository": "git://github.com/visionmedia/express",
|
||||
"main": "index",
|
||||
"bin": {
|
||||
"express": "./bin/express"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublish": "npm prune",
|
||||
"test": "make test"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
"node": ">= 0.8.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ var app = express()
|
||||
, blog = express()
|
||||
, admin = express();
|
||||
|
||||
// app.use(express.logger('dev'))
|
||||
blog.use('/admin', admin);
|
||||
app.use('/blog', blog);
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
NODE_ENV=production node ./support/app &
|
||||
pid=$!
|
||||
|
||||
bench() {
|
||||
ab -n 5000 -c 50 -k -q http://127.0.0.1:8000$1 \
|
||||
| grep "Requests per" \
|
||||
| cut -d ' ' -f 7 \
|
||||
| xargs echo "$2:"
|
||||
}
|
||||
|
||||
bench_conditional() {
|
||||
ab -n 5000 -c 50 -H "If-None-Match: $3" -k -q http://127.0.0.1:8000$1 \
|
||||
| grep "Requests per" \
|
||||
| cut -d ' ' -f 7 \
|
||||
| xargs echo "$2:"
|
||||
}
|
||||
|
||||
sleep .5
|
||||
bench / "Hello World"
|
||||
bench /blog "Mounted Hello World"
|
||||
bench /blog/admin "Mounted 2 Hello World"
|
||||
bench /middleware "Middleware"
|
||||
bench /match "Router"
|
||||
bench /render "Render"
|
||||
bench /json "JSON tiny"
|
||||
bench /json/15 "JSON small"
|
||||
bench /json/50 "JSON medium"
|
||||
bench /json/150 "JSON large"
|
||||
|
||||
kill -9 $pid
|
||||
21
support/docs
21
support/docs
@@ -1,21 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var buf = '';
|
||||
process.stdin.setEncoding('utf8');
|
||||
process.stdin.on('data', function(chunk){
|
||||
buf += chunk;
|
||||
}).on('end', function(){
|
||||
var comments = JSON.parse(buf);
|
||||
comments.forEach(function(comment){
|
||||
if (comment.ignore) return;
|
||||
if (comment.isPrivate) return;
|
||||
if (!comment.ctx) return;
|
||||
if (!comment.description.full.indexOf('Module dep')) return;
|
||||
var ctx = comment.ctx;
|
||||
console.log();
|
||||
console.log('# %s', ctx.string);
|
||||
console.log();
|
||||
console.log(comment.description.full.trim().replace(/^/gm, ' '));
|
||||
});
|
||||
console.log();
|
||||
}).resume();
|
||||
171
test/Route.js
Normal file
171
test/Route.js
Normal file
@@ -0,0 +1,171 @@
|
||||
|
||||
var express = require('../')
|
||||
, Route = express.Route
|
||||
, methods = require('methods')
|
||||
, assert = require('assert');
|
||||
|
||||
describe('Route', function(){
|
||||
|
||||
describe('.all', function(){
|
||||
it('should add handler', function(done){
|
||||
var route = new Route('/foo');
|
||||
|
||||
route.all(function(req, res, next) {
|
||||
assert.equal(req.a, 1);
|
||||
assert.equal(res.b, 2);
|
||||
next();
|
||||
});
|
||||
|
||||
route.dispatch({ a:1, method: 'GET' }, { b:2 }, done);
|
||||
})
|
||||
|
||||
it('should handle VERBS', function(done) {
|
||||
var route = new Route('/foo');
|
||||
|
||||
var count = 0;
|
||||
route.all(function(req, res, next) {
|
||||
count++;
|
||||
});
|
||||
|
||||
methods.forEach(function testMethod(method) {
|
||||
route.dispatch({ method: method }, {});
|
||||
});
|
||||
|
||||
assert.equal(count, methods.length);
|
||||
done();
|
||||
})
|
||||
|
||||
it('should stack', function(done) {
|
||||
var route = new Route('/foo');
|
||||
|
||||
var count = 0;
|
||||
route.all(function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
|
||||
route.all(function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
|
||||
route.dispatch({ method: 'GET' }, {}, function(err) {
|
||||
assert.ifError(err);
|
||||
count++;
|
||||
});
|
||||
|
||||
assert.equal(count, 3);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
describe('.VERB', function(){
|
||||
it('should support .get', function(done){
|
||||
var route = new Route('');
|
||||
|
||||
var count = 0;
|
||||
route.get(function(req, res, next) {
|
||||
count++;
|
||||
})
|
||||
|
||||
route.dispatch({ method: 'GET' }, {});
|
||||
assert(count);
|
||||
done();
|
||||
})
|
||||
|
||||
it('should limit to just .VERB', function(done){
|
||||
var route = new Route('');
|
||||
|
||||
route.get(function(req, res, next) {
|
||||
assert(false);
|
||||
done();
|
||||
})
|
||||
|
||||
route.post(function(req, res, next) {
|
||||
assert(true);
|
||||
})
|
||||
|
||||
route.dispatch({ method: 'post' }, {});
|
||||
done();
|
||||
})
|
||||
|
||||
it('should allow fallthrough', function(done){
|
||||
var route = new Route('');
|
||||
|
||||
var order = '';
|
||||
route.get(function(req, res, next) {
|
||||
order += 'a';
|
||||
next();
|
||||
})
|
||||
|
||||
route.all(function(req, res, next) {
|
||||
order += 'b';
|
||||
next();
|
||||
});
|
||||
|
||||
route.get(function(req, res, next) {
|
||||
order += 'c';
|
||||
})
|
||||
|
||||
route.dispatch({ method: 'get' }, {});
|
||||
assert.equal(order, 'abc');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
describe('errors', function(){
|
||||
it('should handle errors via arity 4 functions', function(done){
|
||||
var route = new Route('');
|
||||
|
||||
var order = '';
|
||||
route.all(function(req, res, next){
|
||||
next(new Error('foobar'));
|
||||
});
|
||||
|
||||
route.all(function(req, res, next){
|
||||
order += '0';
|
||||
next();
|
||||
});
|
||||
|
||||
route.all(function(err, req, res, next){
|
||||
order += 'a';
|
||||
next(err);
|
||||
});
|
||||
|
||||
route.all(function(err, req, res, next){
|
||||
assert.equal(err.message, 'foobar');
|
||||
assert.equal(order, 'a');
|
||||
done();
|
||||
});
|
||||
|
||||
route.dispatch({ method: 'get' }, {});
|
||||
})
|
||||
|
||||
it('should handle throw', function(done) {
|
||||
var route = new Route('');
|
||||
|
||||
var order = '';
|
||||
route.all(function(req, res, next){
|
||||
throw new Error('foobar');
|
||||
});
|
||||
|
||||
route.all(function(req, res, next){
|
||||
order += '0';
|
||||
next();
|
||||
});
|
||||
|
||||
route.all(function(err, req, res, next){
|
||||
order += 'a';
|
||||
next(err);
|
||||
});
|
||||
|
||||
route.all(function(err, req, res, next){
|
||||
assert.equal(err.message, 'foobar');
|
||||
assert.equal(order, 'a');
|
||||
done();
|
||||
});
|
||||
|
||||
route.dispatch({ method: 'get' }, {});
|
||||
});
|
||||
})
|
||||
})
|
||||
206
test/Router.js
206
test/Router.js
@@ -1,103 +1,187 @@
|
||||
|
||||
var express = require('../')
|
||||
, Router = express.Router
|
||||
, request = require('./support/http')
|
||||
, methods = require('methods')
|
||||
, assert = require('assert');
|
||||
|
||||
describe('Router', function(){
|
||||
var router, app;
|
||||
it('should return a function with router methods', function() {
|
||||
var router = Router();
|
||||
assert(typeof router == 'function');
|
||||
|
||||
beforeEach(function(){
|
||||
router = new Router;
|
||||
app = express();
|
||||
})
|
||||
var router = new Router();
|
||||
assert(typeof router == 'function');
|
||||
|
||||
describe('.match(method, url, i)', function(){
|
||||
it('should match based on index', function(){
|
||||
router.route('get', '/foo', function(){});
|
||||
router.route('get', '/foob?', function(){});
|
||||
router.route('get', '/bar', function(){});
|
||||
assert(typeof router.get == 'function');
|
||||
assert(typeof router.handle == 'function');
|
||||
assert(typeof router.use == 'function');
|
||||
});
|
||||
|
||||
var method = 'GET';
|
||||
var url = '/foo?bar=baz';
|
||||
it('should support .use of other routers', function(done){
|
||||
var router = new Router();
|
||||
var another = new Router();
|
||||
|
||||
var route = router.match(method, url, 0);
|
||||
route.constructor.name.should.equal('Route');
|
||||
route.method.should.equal('get');
|
||||
route.path.should.equal('/foo');
|
||||
another.get('/bar', function(req, res){
|
||||
res.end();
|
||||
});
|
||||
router.use('/foo', another);
|
||||
|
||||
var route = router.match(method, url, 1);
|
||||
route.path.should.equal('/foob?');
|
||||
router.handle({ url: '/foo/bar', method: 'GET' }, { end: done });
|
||||
});
|
||||
|
||||
var route = router.match(method, url, 2);
|
||||
assert(!route);
|
||||
it('should support dynamic routes', function(done){
|
||||
var router = new Router();
|
||||
var another = new Router();
|
||||
|
||||
url = '/bar';
|
||||
var route = router.match(method, url);
|
||||
route.path.should.equal('/bar');
|
||||
})
|
||||
})
|
||||
|
||||
describe('.matchRequest(req, i)', function(){
|
||||
it('should match based on index', function(){
|
||||
router.route('get', '/foo', function(){});
|
||||
router.route('get', '/foob?', function(){});
|
||||
router.route('get', '/bar', function(){});
|
||||
var req = { method: 'GET', url: '/foo?bar=baz' };
|
||||
another.get('/:bar', function(req, res){
|
||||
req.params.bar.should.equal('route');
|
||||
res.end();
|
||||
});
|
||||
router.use('/:foo', another);
|
||||
|
||||
var route = router.matchRequest(req, 0);
|
||||
route.constructor.name.should.equal('Route');
|
||||
route.method.should.equal('get');
|
||||
route.path.should.equal('/foo');
|
||||
router.handle({ url: '/test/route', method: 'GET' }, { end: done });
|
||||
});
|
||||
|
||||
var route = router.matchRequest(req, 1);
|
||||
req._route_index.should.equal(1);
|
||||
route.path.should.equal('/foob?');
|
||||
|
||||
var route = router.matchRequest(req, 2);
|
||||
assert(!route);
|
||||
|
||||
req.url = '/bar';
|
||||
var route = router.matchRequest(req);
|
||||
route.path.should.equal('/bar');
|
||||
})
|
||||
})
|
||||
|
||||
describe('.middleware', function(){
|
||||
describe('.handle', function(){
|
||||
it('should dispatch', function(done){
|
||||
router.route('get', '/foo', function(req, res){
|
||||
var router = new Router();
|
||||
|
||||
router.route('/foo').get(function(req, res){
|
||||
res.send('foo');
|
||||
});
|
||||
|
||||
app.use(router.middleware);
|
||||
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.expect('foo', done);
|
||||
var res = {
|
||||
send: function(val) {
|
||||
val.should.equal('foo');
|
||||
done();
|
||||
}
|
||||
}
|
||||
router.handle({ url: '/foo', method: 'GET' }, res);
|
||||
})
|
||||
})
|
||||
|
||||
describe('.multiple callbacks', function(){
|
||||
it('should throw if a callback is null', function(){
|
||||
assert.throws(function () {
|
||||
router.route('get', '/foo', null, function(){});
|
||||
var router = new Router();
|
||||
router.route('/foo').all(null);
|
||||
})
|
||||
})
|
||||
|
||||
it('should throw if a callback is undefined', function(){
|
||||
assert.throws(function () {
|
||||
router.route('get', '/foo', undefined, function(){});
|
||||
var router = new Router();
|
||||
router.route('/foo').all(undefined);
|
||||
})
|
||||
})
|
||||
|
||||
it('should throw if a callback is not a function', function(){
|
||||
assert.throws(function () {
|
||||
router.route('get', '/foo', 'not a function', function(){});
|
||||
var router = new Router();
|
||||
router.route('/foo').all('not a function');
|
||||
})
|
||||
})
|
||||
|
||||
it('should not throw if all callbacks are functions', function(){
|
||||
router.route('get', '/foo', function(){}, function(){});
|
||||
var router = new Router();
|
||||
router.route('/foo').all(function(){}).all(function(){});
|
||||
})
|
||||
})
|
||||
|
||||
describe('error', function(){
|
||||
it('should skip non error middleware', function(done){
|
||||
var router = new Router();
|
||||
|
||||
router.get('/foo', function(req, res, next){
|
||||
next(new Error('foo'));
|
||||
});
|
||||
|
||||
router.get('/bar', function(req, res, next){
|
||||
next(new Error('bar'));
|
||||
});
|
||||
|
||||
router.use(function(req, res, next){
|
||||
assert(false);
|
||||
});
|
||||
|
||||
router.use(function(err, req, res, next){
|
||||
assert.equal(err.message, 'foo');
|
||||
done();
|
||||
});
|
||||
|
||||
router.handle({ url: '/foo', method: 'GET' }, {}, done);
|
||||
});
|
||||
|
||||
it('should handle throwing inside routes with params', function(done) {
|
||||
var router = new Router();
|
||||
|
||||
router.get('/foo/:id', function(req, res, next){
|
||||
throw new Error('foo');
|
||||
});
|
||||
|
||||
router.use(function(req, res, next){
|
||||
assert(false);
|
||||
});
|
||||
|
||||
router.use(function(err, req, res, next){
|
||||
assert.equal(err.message, 'foo');
|
||||
done();
|
||||
});
|
||||
|
||||
router.handle({ url: '/foo/2', method: 'GET' }, {}, done);
|
||||
});
|
||||
})
|
||||
|
||||
describe('.all', function() {
|
||||
it('should support using .all to capture all http verbs', function(done){
|
||||
var router = new Router();
|
||||
|
||||
var count = 0;
|
||||
router.all('/foo', function(){ count++; });
|
||||
|
||||
var url = '/foo?bar=baz';
|
||||
|
||||
methods.forEach(function testMethod(method) {
|
||||
router.handle({ url: url, method: method }, {}, function() {});
|
||||
});
|
||||
|
||||
assert.equal(count, methods.length);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
describe('.param', function() {
|
||||
it('should call param function when routing VERBS', function(done) {
|
||||
var router = new Router();
|
||||
|
||||
router.param('id', function(req, res, next, id) {
|
||||
assert.equal(id, '123');
|
||||
next();
|
||||
});
|
||||
|
||||
router.get('/foo/:id/bar', function(req, res, next) {
|
||||
assert.equal(req.params.id, '123');
|
||||
next();
|
||||
});
|
||||
|
||||
router.handle({ url: '/foo/123/bar', method: 'get' }, {}, done);
|
||||
});
|
||||
|
||||
it('should call param function when routing middleware', function(done) {
|
||||
var router = new Router();
|
||||
|
||||
router.param('id', function(req, res, next, id) {
|
||||
assert.equal(id, '123');
|
||||
next();
|
||||
});
|
||||
|
||||
router.use('/foo/:id/bar', function(req, res, next) {
|
||||
assert.equal(req.params.id, '123');
|
||||
assert.equal(req.url, '/baz');
|
||||
next();
|
||||
});
|
||||
|
||||
router.handle({ url: '/foo/123/bar/baz', method: 'get' }, {}, done);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var app = require('../../examples/auth/app')
|
||||
, request = require('../support/http');
|
||||
, request = require('supertest');
|
||||
|
||||
function redirects(to, fn){
|
||||
return function(err, res){
|
||||
@@ -18,7 +18,7 @@ describe('auth', function(){
|
||||
it('should redirect to /login', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(redirects(/\/login$/, done))
|
||||
.end(redirects(/login$/, done))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -26,7 +26,7 @@ describe('auth', function(){
|
||||
it('should redirect to /login', function(done){
|
||||
request(app)
|
||||
.get('/restricted')
|
||||
.end(redirects(/\/login$/,done))
|
||||
.end(redirects(/login$/,done))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('auth', function(){
|
||||
.post('/login')
|
||||
.type('urlencoded')
|
||||
.send('username=not-tj&password=foobar')
|
||||
.end(redirects(/\/login$/, done))
|
||||
.end(redirects(/login$/, done))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
var request = require('../support/http')
|
||||
var request = require('supertest')
|
||||
, app = require('../../examples/content-negotiation');
|
||||
|
||||
describe('content-negotiation', function(){
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
var app = require('../../examples/cookies/app')
|
||||
, request = require('../support/http');
|
||||
, request = require('supertest');
|
||||
|
||||
describe('cookies', function(){
|
||||
describe('GET /', function(){
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
var app = require('../../examples/downloads/app')
|
||||
, request = require('../support/http');
|
||||
, request = require('supertest');
|
||||
|
||||
describe('downloads', function(){
|
||||
describe('GET /', function(){
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
var request = require('../support/http')
|
||||
var request = require('supertest')
|
||||
, app = require('../../examples/ejs');
|
||||
|
||||
describe('ejs', function(){
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
var app = require('../../examples/error-pages')
|
||||
, request = require('../support/http');
|
||||
, request = require('supertest');
|
||||
|
||||
describe('error-pages', function(){
|
||||
describe('GET /', function(){
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
var app = require('../../examples/error')
|
||||
, request = require('../support/http');
|
||||
, request = require('supertest');
|
||||
|
||||
describe('error', function(){
|
||||
describe('GET /', function(){
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
|
||||
var app = require('../../examples/markdown')
|
||||
, request = require('../support/http');
|
||||
, request = require('supertest');
|
||||
|
||||
describe('markdown', function(){
|
||||
describe('GET /', function(){
|
||||
it('should respond with html', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(/<h1>Markdown Example<\/h1>/,done)
|
||||
.expect(/<h1[^>]*>Markdown Example<\/h1>/,done)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
var request = require('../support/http')
|
||||
var request = require('supertest')
|
||||
, app = require('../../examples/mvc');
|
||||
|
||||
describe('mvc', function(){
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var app = require('../../examples/params/app')
|
||||
, request = require('../support/http');
|
||||
, request = require('supertest');
|
||||
|
||||
describe('params', function(){
|
||||
describe('GET /', function(){
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var app = require('../../examples/resource/app')
|
||||
, request = require('../support/http');
|
||||
, request = require('supertest');
|
||||
|
||||
describe('resource', function(){
|
||||
describe('GET /', function(){
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
var request = require('../support/http')
|
||||
var request = require('supertest')
|
||||
, app = require('../../examples/web-service');
|
||||
|
||||
describe('web-service', function(){
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('./support/http');
|
||||
, request = require('supertest');
|
||||
|
||||
describe('app.all()', function(){
|
||||
it('should add a router per method', function(done){
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('./support/http');
|
||||
, request = require('supertest');
|
||||
|
||||
describe('app.del()', function(){
|
||||
it('should alias app.delete()', function(done){
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user