mirror of
https://github.com/expressjs/express.git
synced 2026-02-26 18:57:43 +00:00
Compare commits
229 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7daae1912b | ||
|
|
3205f68510 | ||
|
|
898dcfac8b | ||
|
|
b1efa19f97 | ||
|
|
b45fd70f99 | ||
|
|
7f6c7a19c6 | ||
|
|
142462d539 | ||
|
|
f881784e9b | ||
|
|
5af625903f | ||
|
|
dc94f305cc | ||
|
|
8060a49c6c | ||
|
|
4d3e0d88a2 | ||
|
|
253ce4837a | ||
|
|
ad05eb8222 | ||
|
|
21393c244c | ||
|
|
4279e6ef45 | ||
|
|
3db6dd752f | ||
|
|
fcbe68eeb5 | ||
|
|
5019f38e29 | ||
|
|
9bf1247716 | ||
|
|
2fd31f6ea6 | ||
|
|
9cf7bba8f0 | ||
|
|
2e257d1cf7 | ||
|
|
980a15d847 | ||
|
|
56831d7799 | ||
|
|
6d65ae5ba6 | ||
|
|
c919b4a573 | ||
|
|
fe6f392c2d | ||
|
|
bffb71d4c8 | ||
|
|
3b34a537ee | ||
|
|
ad79ce9c4b | ||
|
|
721f6388c3 | ||
|
|
402ec83157 | ||
|
|
298ac11018 | ||
|
|
bb6e207336 | ||
|
|
f433b7c7cf | ||
|
|
a94278abd1 | ||
|
|
f9ec70edd0 | ||
|
|
9e5a758e7c | ||
|
|
492e933796 | ||
|
|
8ccd9d0eb5 | ||
|
|
16fdc11ccb | ||
|
|
a7cd5a2553 | ||
|
|
9e6b881f85 | ||
|
|
95fa49147b | ||
|
|
f665c57c5c | ||
|
|
9024d24e81 | ||
|
|
f92a7ad0a3 | ||
|
|
db4448dda8 | ||
|
|
311e83e591 | ||
|
|
fb2d918056 | ||
|
|
dfefea5e9d | ||
|
|
3fbab91231 | ||
|
|
87e02c30e7 | ||
|
|
c3470c9c96 | ||
|
|
7f049164b7 | ||
|
|
4e12a72873 | ||
|
|
91e0c27252 | ||
|
|
db4a061ed6 | ||
|
|
e7e2592357 | ||
|
|
739586f96a | ||
|
|
4c0f1f53d3 | ||
|
|
9354ab62dd | ||
|
|
23ff74bb3f | ||
|
|
98d17e2293 | ||
|
|
ababa6ae5b | ||
|
|
097cd0c242 | ||
|
|
31b2e2d7b4 | ||
|
|
8fe8d74056 | ||
|
|
fcc4742056 | ||
|
|
d37ffa1149 | ||
|
|
cf709f3021 | ||
|
|
7515ee6a78 | ||
|
|
b8d6d258b0 | ||
|
|
35c50601bd | ||
|
|
bc9bcb0317 | ||
|
|
e4302b2120 | ||
|
|
3d6b4ba013 | ||
|
|
555ffe37b2 | ||
|
|
165578a1da | ||
|
|
0bbbc84959 | ||
|
|
92d37671c5 | ||
|
|
2901bd6916 | ||
|
|
be997fd654 | ||
|
|
5c3852b91c | ||
|
|
3c7310ebcb | ||
|
|
61f2929a35 | ||
|
|
c054581370 | ||
|
|
dbea8312bb | ||
|
|
1536a2196e | ||
|
|
374e6c3789 | ||
|
|
203fb05d3e | ||
|
|
ce8555c690 | ||
|
|
9863fa0903 | ||
|
|
2fd3e72a19 | ||
|
|
8ccceacf91 | ||
|
|
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 | ||
|
|
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 | ||
|
|
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 | ||
|
|
4bf9cfd477 | ||
|
|
08cbc442f5 | ||
|
|
a02dd201e6 | ||
|
|
a5f7dcee04 | ||
|
|
0ddd761904 | ||
|
|
991c2a9d05 | ||
|
|
4983c38298 | ||
|
|
337ab24899 | ||
|
|
63c6a9c5ad | ||
|
|
718e68ffae | ||
|
|
f56a5f01c4 | ||
|
|
b77ffe0228 | ||
|
|
fd6439bb36 | ||
|
|
121f8d02f3 | ||
|
|
5ddbb6965f | ||
|
|
a3b5f6d07f | ||
|
|
ac2cbef8be | ||
|
|
dff22e9d09 | ||
|
|
7282b50ad0 | ||
|
|
8c059469fd | ||
|
|
8c3f153dd4 | ||
|
|
185b526e60 | ||
|
|
38996b30b1 | ||
|
|
827dfed7c2 | ||
|
|
28af21baeb | ||
|
|
951c70496b | ||
|
|
a36eeb96f3 | ||
|
|
7018d3d0e6 | ||
|
|
3f14b4de1f | ||
|
|
26c0be4c4e | ||
|
|
cec0c06a70 | ||
|
|
476f8deb07 | ||
|
|
c6c71abf4d |
@@ -8,3 +8,4 @@ test/
|
||||
testing.js
|
||||
.DS_Store
|
||||
.travis.yml
|
||||
Contributing.md
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.8"
|
||||
- "0.10"
|
||||
- "0.11"
|
||||
matrix:
|
||||
|
||||
25
Contributing.md
Normal file
25
Contributing.md
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
## Website Issues
|
||||
|
||||
Issues for the expressjs.com website go here https://github.com/visionmedia/expressjs.com
|
||||
|
||||
## PRs and Code contributions
|
||||
|
||||
* Tests must pass.
|
||||
* Follow existing coding style.
|
||||
* If you fix a bug, add a test.
|
||||
|
||||
|
||||
## Issues which are questions
|
||||
|
||||
We will typically close any vague issues or questions that are specific to some app you are writing. Please double check the docs and other references before being trigger happy with posting a question issue.
|
||||
|
||||
Things that will help get your question issue looked at:
|
||||
|
||||
* Full and runnable JS code.
|
||||
* Clear description of the problem or unexpected behavior.
|
||||
* Clear description of the expected result.
|
||||
* Steps you have taken to debug it yourself.
|
||||
|
||||
If you post a question and do not outline the above items or make it easy for us to understand and reproduce your issue, it will be closed.
|
||||
|
||||
225
History.md
225
History.md
@@ -1,3 +1,228 @@
|
||||
4.4.2 / 2014-06-09
|
||||
==================
|
||||
|
||||
* fix catching errors from top-level handlers
|
||||
* use `vary` module for `res.vary`
|
||||
* deps: debug@1.0.1
|
||||
* deps: proxy-addr@1.0.1
|
||||
* deps: send@0.4.2
|
||||
- fix "event emitter leak" warnings
|
||||
- deps: debug@1.0.1
|
||||
- deps: finished@1.2.1
|
||||
* deps: serve-static@1.2.2
|
||||
- fix "event emitter leak" warnings
|
||||
- deps: send@0.4.2
|
||||
* deps: type-is@1.2.1
|
||||
|
||||
4.4.1 / 2014-06-02
|
||||
==================
|
||||
|
||||
* deps: methods@1.0.1
|
||||
* deps: send@0.4.1
|
||||
- Send `max-age` in `Cache-Control` in correct format
|
||||
* deps: serve-static@1.2.1
|
||||
- use `escape-html` for escaping
|
||||
- deps: send@0.4.1
|
||||
|
||||
4.4.0 / 2014-05-30
|
||||
==================
|
||||
|
||||
* custom etag control with `app.set('etag', val)`
|
||||
- `app.set('etag', function(body, encoding){ return '"etag"' })` custom etag generation
|
||||
- `app.set('etag', 'weak')` weak tag
|
||||
- `app.set('etag', 'strong')` strong etag
|
||||
- `app.set('etag', false)` turn off
|
||||
- `app.set('etag', true)` standard etag
|
||||
* mark `res.send` ETag as weak and reduce collisions
|
||||
* update accepts to 1.0.2
|
||||
- Fix interpretation when header not in request
|
||||
* update send to 0.4.0
|
||||
- Calculate ETag with md5 for reduced collisions
|
||||
- Ignore stream errors after request ends
|
||||
- deps: debug@0.8.1
|
||||
* update serve-static to 1.2.0
|
||||
- Calculate ETag with md5 for reduced collisions
|
||||
- Ignore stream errors after request ends
|
||||
- deps: send@0.4.0
|
||||
|
||||
4.3.2 / 2014-05-28
|
||||
==================
|
||||
|
||||
* fix handling of errors from `router.param()` callbacks
|
||||
|
||||
4.3.1 / 2014-05-23
|
||||
==================
|
||||
|
||||
* revert "fix behavior of multiple `app.VERB` for the same path"
|
||||
- this caused a regression in the order of route execution
|
||||
|
||||
4.3.0 / 2014-05-21
|
||||
==================
|
||||
|
||||
* add `req.baseUrl` to access the path stripped from `req.url` in routes
|
||||
* fix behavior of multiple `app.VERB` for the same path
|
||||
* fix issue routing requests among sub routers
|
||||
* invoke `router.param()` only when necessary instead of every match
|
||||
* proper proxy trust with `app.set('trust proxy', trust)`
|
||||
- `app.set('trust proxy', 1)` trust first hop
|
||||
- `app.set('trust proxy', 'loopback')` trust loopback addresses
|
||||
- `app.set('trust proxy', '10.0.0.1')` trust single IP
|
||||
- `app.set('trust proxy', '10.0.0.1/16')` trust subnet
|
||||
- `app.set('trust proxy', '10.0.0.1, 10.0.0.2')` trust list
|
||||
- `app.set('trust proxy', false)` turn off
|
||||
- `app.set('trust proxy', true)` trust everything
|
||||
* set proper `charset` in `Content-Type` for `res.send`
|
||||
* update type-is to 1.2.0
|
||||
- support suffix matching
|
||||
|
||||
4.2.0 / 2014-05-11
|
||||
==================
|
||||
|
||||
* deprecate `app.del()` -- use `app.delete()` instead
|
||||
* deprecate `res.json(obj, status)` -- use `res.json(status, obj)` instead
|
||||
- the edge-case `res.json(status, num)` requires `res.status(status).json(num)`
|
||||
* deprecate `res.jsonp(obj, status)` -- use `res.jsonp(status, obj)` instead
|
||||
- the edge-case `res.jsonp(status, num)` requires `res.status(status).jsonp(num)`
|
||||
* fix `req.next` when inside router instance
|
||||
* include `ETag` header in `HEAD` requests
|
||||
* keep previous `Content-Type` for `res.jsonp`
|
||||
* support PURGE method
|
||||
- add `app.purge`
|
||||
- add `router.purge`
|
||||
- include PURGE in `app.all`
|
||||
* update debug to 0.8.0
|
||||
- add `enable()` method
|
||||
- change from stderr to stdout
|
||||
* update methods to 1.0.0
|
||||
- add PURGE
|
||||
|
||||
4.1.2 / 2014-05-08
|
||||
==================
|
||||
|
||||
* fix `req.host` for IPv6 literals
|
||||
* fix `res.jsonp` error if callback param is object
|
||||
|
||||
4.1.1 / 2014-04-27
|
||||
==================
|
||||
|
||||
* fix package.json to reflect supported node version
|
||||
|
||||
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
|
||||
- deps: negotiator@0.4.0
|
||||
* update cookie to 0.1.2
|
||||
- Fix for maxAge == 0
|
||||
- made compat with expires field
|
||||
* update send to 0.3.0
|
||||
- Accept API options in options object
|
||||
- Coerce option types
|
||||
- Control whether to generate etags
|
||||
- Default directory access to 403 when index disabled
|
||||
- Fix sending files with dots without root set
|
||||
- Include file path in etag
|
||||
- Make "Can't set headers after they are sent." catchable
|
||||
- Send full entity-body for multi range requests
|
||||
- Set etags to "weak"
|
||||
- Support "If-Range" header
|
||||
- Support multiple index paths
|
||||
- deps: mime@1.2.11
|
||||
* update serve-static to 1.1.0
|
||||
- Accept options directly to `send` module
|
||||
- Resolve relative paths at middleware setup
|
||||
- Use parseurl to parse the URL from request
|
||||
- deps: send@0.3.0
|
||||
* update type-is to 1.1.0
|
||||
- add non-array values support
|
||||
- add `multipart` as a shorthand
|
||||
|
||||
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.10.4 / 2014-06-09
|
||||
===================
|
||||
|
||||
* deps: connect@2.19.5
|
||||
- fix "event emitter leak" warnings
|
||||
- deps: csurf@1.2.1
|
||||
- deps: debug@1.0.1
|
||||
- deps: serve-static@1.2.2
|
||||
- deps: type-is@1.2.1
|
||||
* deps: debug@1.0.1
|
||||
* deps: send@0.4.2
|
||||
- fix "event emitter leak" warnings
|
||||
- deps: finished@1.2.1
|
||||
- deps: debug@1.0.1
|
||||
|
||||
3.10.3 / 2014-06-05
|
||||
===================
|
||||
|
||||
* use `vary` module for `res.vary`
|
||||
* deps: connect@2.19.4
|
||||
- deps: errorhandler@1.0.2
|
||||
- deps: method-override@2.0.2
|
||||
- deps: serve-favicon@2.0.1
|
||||
* deps: debug@1.0.0
|
||||
|
||||
3.10.2 / 2014-06-03
|
||||
===================
|
||||
|
||||
* deps: connect@2.19.3
|
||||
- deps: compression@1.0.6
|
||||
|
||||
3.10.1 / 2014-06-03
|
||||
===================
|
||||
|
||||
* deps: connect@2.19.2
|
||||
- deps: compression@1.0.4
|
||||
* deps: proxy-addr@1.0.1
|
||||
|
||||
3.10.0 / 2014-06-02
|
||||
===================
|
||||
|
||||
* deps: connect@2.19.1
|
||||
- deprecate `methodOverride()` -- use `method-override` module directly
|
||||
- deps: body-parser@1.3.0
|
||||
- deps: method-override@2.0.1
|
||||
- deps: multiparty@3.2.8
|
||||
- deps: response-time@2.0.0
|
||||
- deps: serve-static@1.2.1
|
||||
* deps: methods@1.0.1
|
||||
* deps: send@0.4.1
|
||||
- Send `max-age` in `Cache-Control` in correct format
|
||||
|
||||
3.9.0 / 2014-05-30
|
||||
==================
|
||||
|
||||
|
||||
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.
|
||||
|
||||
57
Readme.md
57
Readme.md
@@ -1,8 +1,11 @@
|
||||
[](http://expressjs.com/)
|
||||
[](http://expressjs.com/)
|
||||
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
|
||||
|
||||
[](http://badge.fury.io/js/express) [](https://travis-ci.org/visionmedia/express) [](https://coveralls.io/r/visionmedia/express) [](https://www.gittip.com/visionmedia/)
|
||||
[](http://badge.fury.io/js/express)
|
||||
[](https://travis-ci.org/visionmedia/express)
|
||||
[](https://coveralls.io/r/visionmedia/express)
|
||||
[](https://www.gittip.com/visionmedia/)
|
||||
|
||||
```js
|
||||
var express = require('express');
|
||||
@@ -15,17 +18,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,17 +42,15 @@ 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
|
||||
|
||||
@@ -54,9 +60,7 @@ app.listen(3000);
|
||||
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
|
||||
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.
|
||||
|
||||
@@ -65,7 +69,7 @@ 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)
|
||||
@@ -99,30 +103,11 @@ $ npm 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
|
||||
|
||||
423
bin/express
423
bin/express
@@ -1,423 +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 html'
|
||||
, '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.json());'
|
||||
, 'app.use(express.urlencoded());'
|
||||
, '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'] = '~0.1.15';
|
||||
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) {
|
||||
@@ -75,7 +78,7 @@ function restrict(req, res, next) {
|
||||
}
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('login');
|
||||
res.redirect('/login');
|
||||
});
|
||||
|
||||
app.get('/restricted', restrict, function(req, res){
|
||||
@@ -98,9 +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;
|
||||
@@ -113,7 +116,7 @@ app.post('/login', function(req, res){
|
||||
req.session.error = 'Authentication failed, please check your '
|
||||
+ ' username and password.'
|
||||
+ ' (use "tj" and "foobar")';
|
||||
res.redirect('login');
|
||||
res.redirect('/login');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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,7 +18,7 @@ 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 });
|
||||
|
||||
@@ -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,7 +21,7 @@ app.get('/', function(req, res){
|
||||
json: function(){
|
||||
res.json(users);
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
// or you could write a tiny middleware like
|
||||
@@ -32,7 +32,7 @@ function format(path) {
|
||||
var obj = require(path);
|
||||
return function(req, res){
|
||||
res.format(obj);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
app.get('/users', format('./users'));
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
var cookie-parser = require('cookie-parser');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// ignore GET /favicon.ico
|
||||
app.use(express.favicon());
|
||||
|
||||
// pass a secret to cookieParser() for signed cookies
|
||||
app.use(express.cookieParser('manny is cool'));
|
||||
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);
|
||||
|
||||
@@ -1,30 +1,24 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
|
||||
|
||||
// add favicon() before logger() so
|
||||
// GET /favicon.ico requests are not
|
||||
// logged, because this middleware
|
||||
// reponds to /favicon.ico and does not
|
||||
// call next()
|
||||
app.use(express.favicon());
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
var logger = require('morgan');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
// custom log format
|
||||
if ('test' != process.env.NODE_ENV) app.use(express.logger(':method :url'));
|
||||
if ('test' != process.env.NODE_ENV) app.use(logger(':method :url'));
|
||||
|
||||
// parses request cookies, populating
|
||||
// req.cookies and req.signedCookies
|
||||
// when the secret is passed, used
|
||||
// when the secret is passed, used
|
||||
// for signing the cookies.
|
||||
app.use(express.cookieParser('my secret here'));
|
||||
app.use(cookieParser('my secret here'));
|
||||
|
||||
// parses json, x-www-form-urlencoded, and multipart/form-data
|
||||
app.use(express.bodyParser());
|
||||
app.use(bodyParser());
|
||||
|
||||
app.get('/', function(req, res){
|
||||
if (req.cookies.remember) {
|
||||
|
||||
@@ -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,26 +16,16 @@ 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);
|
||||
});
|
||||
|
||||
// error handling middleware. Because it's
|
||||
// below our routes, you will be able to
|
||||
// "intercept" errors, otherwise Connect
|
||||
// will respond with 500 "Internal Server Error".
|
||||
app.use(function(err, req, res, next){
|
||||
// special-case 404s,
|
||||
// remember you could
|
||||
// render a 404 template here
|
||||
if (404 == err.status) {
|
||||
res.download(path, function(err){
|
||||
if (!err) return; // file sent
|
||||
if (err && err.status !== 404) return next(err); // non-404 error
|
||||
// file for download not found
|
||||
res.statusCode = 404;
|
||||
res.send('Cant find that file, sorry!');
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
|
||||
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,10 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express()
|
||||
, silent = 'test' == process.env.NODE_ENV;
|
||||
var express = require('../../');
|
||||
var app = module.exports = express();
|
||||
var logger = require('morgan');
|
||||
var silent = 'test' == process.env.NODE_ENV;
|
||||
|
||||
// general config
|
||||
app.set('views', __dirname + '/views');
|
||||
@@ -19,18 +20,34 @@ app.enable('verbose errors');
|
||||
// use $ NODE_ENV=production node examples/error-pages
|
||||
if ('production' == app.settings.env) app.disable('verbose errors');
|
||||
|
||||
app.use(express.favicon());
|
||||
silent || app.use(logger('dev'));
|
||||
|
||||
silent || app.use(express.logger('dev'));
|
||||
// Routes
|
||||
|
||||
// "app.router" positions our routes
|
||||
// above the middleware defined below,
|
||||
// this means that Express will attempt
|
||||
// to match & call routes _before_ continuing
|
||||
// on, at which point we assume it's a 404 because
|
||||
// no route has handled the request.
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.jade');
|
||||
});
|
||||
|
||||
app.use(app.router);
|
||||
app.get('/404', function(req, res, next){
|
||||
// trigger a 404 since no other middleware
|
||||
// will match /404 after this one, and we're not
|
||||
// responding here
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/403', function(req, res, next){
|
||||
// trigger a 403 error
|
||||
var err = new Error('not allowed!');
|
||||
err.status = 403;
|
||||
next(err);
|
||||
});
|
||||
|
||||
app.get('/500', function(req, res, next){
|
||||
// trigger a generic (500) error
|
||||
next(new Error('keyboard cat!'));
|
||||
});
|
||||
|
||||
// Error handlers
|
||||
|
||||
// Since this is the last non-error-handling
|
||||
// middleware use()d, we assume 404, as nothing else
|
||||
@@ -42,7 +59,7 @@ app.use(app.router);
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.status(404);
|
||||
|
||||
|
||||
// respond with html page
|
||||
if (req.accepts('html')) {
|
||||
res.render('404', { url: req.url });
|
||||
@@ -79,30 +96,6 @@ app.use(function(err, req, res, next){
|
||||
res.render('500', { error: err });
|
||||
});
|
||||
|
||||
// Routes
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.jade');
|
||||
});
|
||||
|
||||
app.get('/404', function(req, res, next){
|
||||
// trigger a 404 since no other middleware
|
||||
// will match /404 after this one, and we're not
|
||||
// responding here
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/403', function(req, res, next){
|
||||
// trigger a 403 error
|
||||
var err = new Error('not allowed!');
|
||||
err.status = 403;
|
||||
next(err);
|
||||
});
|
||||
|
||||
app.get('/500', function(req, res, next){
|
||||
// trigger a generic (500) error
|
||||
next(new Error('keyboard cat!'));
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
|
||||
@@ -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,6 +35,11 @@ app.get('/next', function(req, res, next){
|
||||
});
|
||||
});
|
||||
|
||||
// the error handler is placed after routes
|
||||
// if it were above it would not receive errors
|
||||
// from app.get() etc
|
||||
app.use(error);
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,6 +38,12 @@ 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);
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
|
||||
@@ -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' });
|
||||
})
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
|
||||
@@ -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,17 +17,44 @@ app.get('/', function(req, res){
|
||||
});
|
||||
|
||||
app.post('/', function(req, res, next){
|
||||
// the uploaded file can be found as `req.files.image` and the
|
||||
// title field as `req.body.title`
|
||||
res.send(format('\nuploaded %s (%d Kb) to %s as %s'
|
||||
, req.files.image.name
|
||||
, req.files.image.size / 1024 | 0
|
||||
, req.files.image.path
|
||||
, req.body.title));
|
||||
// create a form to begin parsing
|
||||
var form = new multiparty.Form();
|
||||
var image;
|
||||
var title;
|
||||
|
||||
form.on('error', next);
|
||||
form.on('close', function(){
|
||||
res.send(format('\nuploaded %s (%d Kb) as %s'
|
||||
, image.filename
|
||||
, image.size / 1024 | 0
|
||||
, title));
|
||||
});
|
||||
|
||||
// listen on field event for title
|
||||
form.on('field', function(name, val){
|
||||
if (name !== 'title') return;
|
||||
title = val;
|
||||
});
|
||||
|
||||
// listen on part event for image file
|
||||
form.on('part', function(part){
|
||||
if (!part.filename) return;
|
||||
if (part.name !== 'image') return part.resume();
|
||||
image = {};
|
||||
image.filename = part.filename;
|
||||
image.size = 0;
|
||||
part.on('data', function(buf){
|
||||
image.size += buf.length;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// parse the form
|
||||
form.parse(req);
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
app.listen(4000);
|
||||
console.log('Express started on port 4000');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
exports.index = function(req, res){
|
||||
res.redirect('/users');
|
||||
};
|
||||
@@ -1,11 +1,12 @@
|
||||
/**
|
||||
* 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'));
|
||||
if (!pet) return next('route');
|
||||
req.pet = pet;
|
||||
next();
|
||||
};
|
||||
|
||||
@@ -1,7 +1,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)
|
||||
form(action='/pet/#{pet.id}?_method=put', method='post')
|
||||
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');
|
||||
|
||||
@@ -8,7 +11,7 @@ exports.create = function(req, res, next){
|
||||
var id = req.params.user_id;
|
||||
var user = db.users[id];
|
||||
var body = req.body;
|
||||
if (!user) return next(new Error('User not found'));
|
||||
if (!user) return next('route');
|
||||
var pet = { name: body.pet.name };
|
||||
pet.id = db.pets.push(pet) - 1;
|
||||
user.pets.push(pet);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
@@ -8,11 +11,11 @@ exports.before = function(req, res, next){
|
||||
process.nextTick(function(){
|
||||
req.user = db.users[id];
|
||||
// cant find that user
|
||||
if (!req.user) return next(new Error('User not found'));
|
||||
if (!req.user) return next('route');
|
||||
// found it, move on to the routes
|
||||
next();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.list = function(req, res, next){
|
||||
res.render('list', { users: db.users });
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1><%= user.name %></h1>
|
||||
<form action='/user/<%= user.id %>' method='post'>
|
||||
<input type="hidden" name="_method" value="put" />
|
||||
<label>Name: <input type="text" name="user[name]" value="<%= user.name %>" /></label>
|
||||
<input type="submit" value="Update" />
|
||||
</form>
|
||||
|
||||
<form action='/user/<%= user.id %>/pet' method='post'>
|
||||
<label>Pet: <input type="text" name="pet[name]" placeholder="name" /></label>
|
||||
<input type="submit" value="Add" />
|
||||
</form>
|
||||
11
examples/mvc/controllers/user/views/edit.jade
Normal file
11
examples/mvc/controllers/user/views/edit.jade
Normal file
@@ -0,0 +1,11 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1= user.name
|
||||
form(action='/user/#{user.id}?_method=put', method='post')
|
||||
label= 'Name: '
|
||||
input(type='text', name='user[name]', value='#{user.name}')
|
||||
input(type='submit', value='Update')
|
||||
|
||||
form(action='/user/#{user.id}/pet', method='post')
|
||||
label= 'Pet: '
|
||||
input(type='text', name='pet[name]', placeholder='Name')
|
||||
input(type='submit', value='Add')
|
||||
@@ -1,8 +0,0 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1>Users</h1>
|
||||
<p>Click a user below to view their pets.</p>
|
||||
<ul>
|
||||
<% users.forEach(function(user){ %>
|
||||
<li><a href="/user/<%= user.id %>"><%= user.name %></a></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
7
examples/mvc/controllers/user/views/list.jade
Normal file
7
examples/mvc/controllers/user/views/list.jade
Normal file
@@ -0,0 +1,7 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1 Users
|
||||
p Click a user below to view their pets.
|
||||
ul
|
||||
each user in users
|
||||
li
|
||||
a(href='/user/#{user.id}')= user.name
|
||||
@@ -1,21 +0,0 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1><%= user.name %> <a href="/user/<%= user.id %>/edit">edit</a></h1>
|
||||
|
||||
<% if (hasMessages) { %>
|
||||
<ul id="messages">
|
||||
<% messages.forEach(function(msg){ %>
|
||||
<li><%= msg %></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
<% } %>
|
||||
|
||||
<% if (user.pets.length) { %>
|
||||
<p>View <%= user.name %>s pets:</p>
|
||||
<ul>
|
||||
<% user.pets.forEach(function(pet){ %>
|
||||
<li><a href="/pet/<%= pet.id %>"><%= pet.name %></a></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
<% } else { %>
|
||||
<p>No pets!</p>
|
||||
<% } %>
|
||||
17
examples/mvc/controllers/user/views/show.jade
Normal file
17
examples/mvc/controllers/user/views/show.jade
Normal file
@@ -0,0 +1,17 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1= user.name + ' '
|
||||
a(href='/user/#{user.id}/edit') edit
|
||||
|
||||
if (hasMessages)
|
||||
ul#messages
|
||||
each msg in messages
|
||||
li= msg
|
||||
|
||||
if (user.pets.length)
|
||||
p View #{user.name}'s pets:
|
||||
ul
|
||||
each pet in user.pets
|
||||
li
|
||||
a(href='/pet/#{pet.id}')= pet.name
|
||||
else
|
||||
p No pets!
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
// faux database
|
||||
|
||||
var pets = exports.pets = [];
|
||||
|
||||
@@ -1,14 +1,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());
|
||||
// allow overriding methods in query (?_method=put)
|
||||
app.use(methodOverride('_method'));
|
||||
|
||||
// expose the "messages" local variable when views are rendered
|
||||
app.use(function(req, res, next){
|
||||
@@ -66,16 +73,9 @@ app.use(function(req, res, next){
|
||||
// load controllers
|
||||
require('./lib/boot')(app, { verbose: !module.parent });
|
||||
|
||||
// assume "not found" in the error msgs
|
||||
// is a 404. this is somewhat silly, but
|
||||
// valid, you can do whatever you like, set
|
||||
// properties, use instanceof etc.
|
||||
app.use(function(err, req, res, next){
|
||||
// treat as 404
|
||||
if (~err.message.indexOf('not found')) return next();
|
||||
|
||||
// log it
|
||||
console.error(err.stack);
|
||||
if (!module.parent) console.error(err.stack);
|
||||
|
||||
// error page
|
||||
res.status(500).render('5xx');
|
||||
|
||||
@@ -1,32 +1,26 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../..')
|
||||
, fs = require('fs');
|
||||
var express = require('../../..');
|
||||
var fs = require('fs');
|
||||
|
||||
module.exports = function(parent, options){
|
||||
var verbose = options.verbose;
|
||||
fs.readdirSync(__dirname + '/../controllers').forEach(function(name){
|
||||
verbose && console.log('\n %s:', name);
|
||||
var obj = require('./../controllers/' + name)
|
||||
, name = obj.name || name
|
||||
, prefix = obj.prefix || ''
|
||||
, app = express()
|
||||
, method
|
||||
, path;
|
||||
var obj = require('./../controllers/' + name);
|
||||
var name = obj.name || name;
|
||||
var prefix = obj.prefix || '';
|
||||
var app = express();
|
||||
var handler;
|
||||
var method;
|
||||
var path;
|
||||
|
||||
// allow specifying the view engine
|
||||
if (obj.engine) app.set('view engine', obj.engine);
|
||||
app.set('views', __dirname + '/../controllers/' + name + '/views');
|
||||
|
||||
// before middleware support
|
||||
if (obj.before) {
|
||||
path = '/' + name + '/:' + name + '_id';
|
||||
app.all(path, obj.before);
|
||||
verbose && console.log(' ALL %s -> before', path);
|
||||
path = '/' + name + '/:' + name + '_id/*';
|
||||
app.all(path, obj.before);
|
||||
verbose && console.log(' ALL %s -> before', path);
|
||||
}
|
||||
|
||||
// generate routes based
|
||||
// on the exported methods
|
||||
for (var key in obj) {
|
||||
@@ -59,15 +53,25 @@ module.exports = function(parent, options){
|
||||
path = '/';
|
||||
break;
|
||||
default:
|
||||
/* istanbul ignore next */
|
||||
throw new Error('unrecognized route: ' + name + '.' + key);
|
||||
}
|
||||
|
||||
// setup
|
||||
handler = obj[key];
|
||||
path = prefix + path;
|
||||
app[method](path, obj[key]);
|
||||
verbose && console.log(' %s %s -> %s', method.toUpperCase(), path, key);
|
||||
|
||||
// before middleware support
|
||||
if (obj.before) {
|
||||
app[method](path, obj.before, handler);
|
||||
verbose && console.log(' %s %s -> before -> %s', method.toUpperCase(), path, key);
|
||||
} else {
|
||||
app[method](path, obj[key]);
|
||||
verbose && console.log(' %s %s -> %s', method.toUpperCase(), path, key);
|
||||
}
|
||||
}
|
||||
|
||||
// mount the app
|
||||
parent.use(app);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1>404: Not Found</h1>
|
||||
<p>Sorry we can't find <%= url %></p>
|
||||
3
examples/mvc/views/404.jade
Normal file
3
examples/mvc/views/404.jade
Normal file
@@ -0,0 +1,3 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1 404: Not Found
|
||||
p Sorry we can't find #{url}
|
||||
@@ -1,3 +0,0 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1>500: Internal Server Error</h1>
|
||||
<p>Looks like something blew up!</p>
|
||||
3
examples/mvc/views/5xx.jade
Normal file
3
examples/mvc/views/5xx.jade
Normal file
@@ -0,0 +1,3 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1 500: Internal Server Error
|
||||
p Looks like something blew up!
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
// first:
|
||||
// $ npm install redis online
|
||||
// $ redis-server
|
||||
@@ -7,10 +6,10 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..')
|
||||
, online = require('online')
|
||||
, redis = require('redis')
|
||||
, db = redis.createClient();
|
||||
var express = require('../..');
|
||||
var online = require('online');
|
||||
var redis = require('redis');
|
||||
var db = redis.createClient();
|
||||
|
||||
// online
|
||||
|
||||
|
||||
@@ -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] = parseInt(num, 10);
|
||||
if( isNaN(req.params[name]) ){
|
||||
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);
|
||||
@@ -84,7 +83,7 @@ app.get('/', function(req, res){
|
||||
, '<li>GET /users/1..3.json</li>'
|
||||
, '<li>DELETE /users/4</li>'
|
||||
, '</ul>'
|
||||
].join('\n'));
|
||||
].join('\n'));
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
|
||||
@@ -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,24 @@
|
||||
|
||||
/**
|
||||
* 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(methodOverride('_method'));
|
||||
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 = [
|
||||
|
||||
@@ -3,8 +3,7 @@ extends ../layout
|
||||
block content
|
||||
h1 Editing #{user.name}
|
||||
#user
|
||||
form(method="post")
|
||||
input(type="hidden", value="put", name="_method")
|
||||
form(action="?_method=put", method="post")
|
||||
p Name:
|
||||
input(type="text", value= user.name, name="user[name]")
|
||||
p Email:
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
var search = document.querySelector('[type=search]');
|
||||
var code = document.querySelector('pre');
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
// first:
|
||||
// $ npm install redis
|
||||
// $ redis-server
|
||||
@@ -7,9 +6,10 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../..')
|
||||
, redis = require('redis')
|
||||
, db = redis.createClient();
|
||||
var express = require('../..');
|
||||
var redis = require('redis');
|
||||
|
||||
var db = redis.createClient();
|
||||
|
||||
// npm install redis
|
||||
|
||||
|
||||
@@ -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'));
|
||||
if (!module.parent) main.use(logger('dev'));
|
||||
|
||||
main.get('/', function(req, res){
|
||||
res.send('Hello from main app!')
|
||||
res.send('Hello from main app!');
|
||||
});
|
||||
|
||||
main.get('/:sub', function(req, res){
|
||||
@@ -30,17 +32,17 @@ main.get('/:sub', function(req, res){
|
||||
|
||||
var redirect = express();
|
||||
|
||||
redirect.all('*', function(req, res){
|
||||
console.log(req.subdomains);
|
||||
res.redirect('http://example.com:3000/' + req.subdomains[0]);
|
||||
redirect.use(function(req, res){
|
||||
if (!module.parent) console.log(req.vhost);
|
||||
res.redirect('http://example.com:3000/' + req.vhost[0]);
|
||||
});
|
||||
|
||||
// Vhost app
|
||||
|
||||
var app = express();
|
||||
var app = module.exports = express();
|
||||
|
||||
app.use(express.vhost('*.example.com', redirect)) // Serves all subdomains via Redirect app
|
||||
app.use(express.vhost('example.com', main)); // Serves top level domain via Main server app
|
||||
app.use(vhost('*.example.com', redirect)); // Serves all subdomains via Redirect app
|
||||
app.use(vhost('example.com', main)); // Serves top level domain via Main server app
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
|
||||
@@ -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');
|
||||
})
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
|
||||
@@ -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,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,13 +78,31 @@ 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" });
|
||||
});
|
||||
|
||||
/* istanbul ignore next */
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
|
||||
@@ -2,18 +2,19 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var connect = require('connect')
|
||||
, Router = require('./router')
|
||||
, methods = require('methods')
|
||||
, middleware = require('./middleware')
|
||||
, debug = require('debug')('express:application')
|
||||
, locals = require('./utils').locals
|
||||
, compileETag = require('./utils').compileETag
|
||||
, compileTrust = require('./utils').compileTrust
|
||||
, View = require('./view')
|
||||
, utils = connect.utils
|
||||
, deprecate = require('./utils').deprecate
|
||||
, 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');
|
||||
var compileETag = require('./utils').compileETag;
|
||||
var compileTrust = require('./utils').compileTrust;
|
||||
var deprecate = require('./utils').deprecate;
|
||||
var resolve = require('path').resolve;
|
||||
|
||||
/**
|
||||
* Application prototype.
|
||||
@@ -48,14 +49,12 @@ app.defaultConfiguration = function(){
|
||||
// default settings
|
||||
this.enable('x-powered-by');
|
||||
this.set('etag', 'weak');
|
||||
this.set('env', process.env.NODE_ENV || 'development');
|
||||
var env = process.env.NODE_ENV || 'development';
|
||||
this.set('env', env);
|
||||
this.set('subdomain offset', 2);
|
||||
this.set('trust proxy', false);
|
||||
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){
|
||||
@@ -65,61 +64,128 @@ app.defaultConfiguration = function(){
|
||||
this.settings.__proto__ = parent.settings;
|
||||
});
|
||||
|
||||
// router
|
||||
this._router = new Router(this);
|
||||
this.routes = this._router.map;
|
||||
this.__defineGetter__('router', function(){
|
||||
this._usedRouter = true;
|
||||
this._router.caseSensitive = this.enabled('case sensitive routing');
|
||||
this._router.strict = this.enabled('strict routing');
|
||||
return this._router.middleware;
|
||||
});
|
||||
|
||||
// setup locals
|
||||
this.locals = locals(this);
|
||||
this.locals = Object.create(null);
|
||||
|
||||
// top-most app is mounted at /
|
||||
this.mountpath = '/';
|
||||
|
||||
// default locals
|
||||
this.locals.settings = this.settings;
|
||||
|
||||
// default configuration
|
||||
this.set('view', View);
|
||||
this.set('views', process.cwd() + '/views');
|
||||
this.set('views', resolve('views'));
|
||||
this.set('jsonp callback name', 'callback');
|
||||
|
||||
this.configure('development', function(){
|
||||
this.set('json spaces', 2);
|
||||
});
|
||||
|
||||
this.configure('production', function(){
|
||||
if (env === 'production') {
|
||||
this.enable('view cache');
|
||||
}
|
||||
|
||||
Object.defineProperty(this, 'router', {
|
||||
get: function() {
|
||||
throw new Error('\'app.router\' is deprecated!\nPlease see the 3.x to 4.x migration guide for details on how to update your app.');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Proxy `connect#use()` to apply settings to
|
||||
* mounted applications.
|
||||
* lazily adds the base router if it has not yet been added.
|
||||
*
|
||||
* We cannot add the base router in the defaultConfiguration because
|
||||
* it reads app settings which might be set after that has run.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
app.lazyrouter = function() {
|
||||
if (!this._router) {
|
||||
this._router = new Router({
|
||||
caseSensitive: this.enabled('case sensitive routing'),
|
||||
strict: this.enabled('strict routing')
|
||||
});
|
||||
|
||||
this._router.use(query());
|
||||
this._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
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
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);
|
||||
@@ -127,17 +193,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`.
|
||||
@@ -180,30 +262,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
|
||||
@@ -212,27 +274,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;
|
||||
};
|
||||
|
||||
@@ -291,7 +343,7 @@ app.set = function(setting, val){
|
||||
|
||||
app.path = function(){
|
||||
return this.parent
|
||||
? this.parent.path() + this.route
|
||||
? this.parent.path() + this.mountpath
|
||||
: '';
|
||||
};
|
||||
|
||||
@@ -357,60 +409,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(...)`.
|
||||
*/
|
||||
@@ -419,11 +417,10 @@ methods.forEach(function(method){
|
||||
app[method] = function(path){
|
||||
if ('get' == method && 1 == arguments.length) return this.set(path);
|
||||
|
||||
// if no router attached yet, attach the router
|
||||
if (!this._usedRouter) this.use(this.router);
|
||||
this.lazyrouter();
|
||||
|
||||
// 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;
|
||||
};
|
||||
});
|
||||
@@ -439,10 +436,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;
|
||||
};
|
||||
|
||||
@@ -468,10 +469,10 @@ app.del = deprecate(app.delete, 'app.del: Use app.delete instead');
|
||||
*/
|
||||
|
||||
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) {
|
||||
@@ -479,13 +480,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
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var merge = require('merge-descriptors');
|
||||
var connect = require('connect')
|
||||
, proto = require('./application')
|
||||
, Route = require('./router/route')
|
||||
, Router = require('./router')
|
||||
, req = require('./request')
|
||||
, res = require('./response')
|
||||
, deprecate = require('./utils').deprecate
|
||||
, 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()`.
|
||||
@@ -18,12 +16,6 @@ var connect = require('connect')
|
||||
|
||||
exports = module.exports = createApplication;
|
||||
|
||||
/**
|
||||
* Expose mime.
|
||||
*/
|
||||
|
||||
exports.mime = connect.mime;
|
||||
|
||||
/**
|
||||
* Create an express application.
|
||||
*
|
||||
@@ -32,35 +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.
|
||||
*/
|
||||
|
||||
merge(exports, connect.middleware);
|
||||
|
||||
/**
|
||||
* Deprecated createServer().
|
||||
*/
|
||||
|
||||
exports.createServer = deprecate(createApplication,
|
||||
'createServer() is deprecated\n' +
|
||||
'express applications no longer inherit from http.Server\n' +
|
||||
'please use:\n' +
|
||||
'\n' +
|
||||
' var express = require("express");\n' +
|
||||
' var app = express();\n' +
|
||||
'\n'
|
||||
);
|
||||
|
||||
/**
|
||||
* Expose the prototypes.
|
||||
*/
|
||||
@@ -76,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();
|
||||
};
|
||||
};
|
||||
192
lib/request.js
192
lib/request.js
@@ -1,16 +1,14 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var http = require('http')
|
||||
, utils = require('./utils')
|
||||
, connect = require('connect')
|
||||
, fresh = require('fresh')
|
||||
, parseRange = require('range-parser')
|
||||
, parse = require('parseurl')
|
||||
, proxyaddr = require('proxy-addr')
|
||||
, 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');
|
||||
var proxyaddr = require('proxy-addr');
|
||||
|
||||
/**
|
||||
* Request prototype.
|
||||
@@ -57,6 +55,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".
|
||||
@@ -100,9 +100,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);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -113,11 +113,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".
|
||||
*
|
||||
@@ -126,14 +130,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".
|
||||
*
|
||||
@@ -142,11 +147,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);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -175,98 +179,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`.
|
||||
*
|
||||
@@ -276,7 +188,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]
|
||||
@@ -320,19 +232,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);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -408,36 +310,6 @@ req.__defineGetter__('ips', function(){
|
||||
return addrs.slice(1).reverse();
|
||||
});
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
||||
191
lib/response.js
191
lib/response.js
@@ -2,22 +2,23 @@
|
||||
* 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
|
||||
, setCharset = require('./utils').setCharset
|
||||
, deprecate = require('./utils').deprecate
|
||||
, statusCodes = http.STATUS_CODES
|
||||
, cookie = require('cookie')
|
||||
, send = require('send')
|
||||
, mime = connect.mime
|
||||
, resolve = require('url').resolve
|
||||
, basename = path.basename
|
||||
, extname = path.extname;
|
||||
var escapeHtml = require('escape-html');
|
||||
var http = require('http');
|
||||
var path = require('path');
|
||||
var mixin = require('utils-merge');
|
||||
var sign = require('cookie-signature').sign;
|
||||
var normalizeType = require('./utils').normalizeType;
|
||||
var normalizeTypes = require('./utils').normalizeTypes;
|
||||
var setCharset = require('./utils').setCharset;
|
||||
var contentDisposition = require('./utils').contentDisposition;
|
||||
var deprecate = require('./utils').deprecate;
|
||||
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;
|
||||
var vary = require('vary');
|
||||
|
||||
/**
|
||||
* Response prototype.
|
||||
@@ -74,9 +75,6 @@ res.links = function(links){
|
||||
* res.send(404, 'Sorry, cant find that');
|
||||
* res.send(404);
|
||||
*
|
||||
* @param {Mixed} body or status
|
||||
* @param {Mixed} body
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
@@ -110,10 +108,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':
|
||||
@@ -182,9 +177,6 @@ res.send = function(body){
|
||||
* res.json(500, 'oh noes!');
|
||||
* res.json(404, 'I dont have that');
|
||||
*
|
||||
* @param {Mixed} obj or status
|
||||
* @param {Mixed} obj
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
@@ -210,7 +202,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);
|
||||
@@ -232,9 +223,6 @@ var jsonNumDeprecated = deprecate(res.json,
|
||||
* res.jsonp(500, 'oh noes!');
|
||||
* res.jsonp(404, 'I dont have that');
|
||||
*
|
||||
* @param {Mixed} obj or status
|
||||
* @param {Mixed} obj
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
@@ -263,7 +251,6 @@ res.jsonp = function(obj){
|
||||
var callback = this.req.query[app.get('jsonp callback name')];
|
||||
|
||||
// content-type
|
||||
this.charset = this.charset || 'utf-8';
|
||||
this.get('Content-Type') || this.set('Content-Type', 'application/json');
|
||||
|
||||
// fixup callback
|
||||
@@ -300,6 +287,9 @@ var jsonpNumDeprecated = deprecate(res.json,
|
||||
*
|
||||
* - `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:
|
||||
*
|
||||
@@ -321,18 +311,16 @@ var jsonpNumDeprecated = deprecate(res.json,
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Object|Function} options or fn
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.sendfile = function(path, options, fn){
|
||||
var self = this
|
||||
, req = self.req
|
||||
, next = this.req.next
|
||||
, options = options || {}
|
||||
, done;
|
||||
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) {
|
||||
@@ -374,10 +362,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);
|
||||
@@ -395,9 +384,6 @@ res.sendfile = function(path, options, fn){
|
||||
*
|
||||
* This method uses `res.sendfile()`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {String|Function} filename or fn
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
@@ -409,7 +395,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);
|
||||
};
|
||||
|
||||
@@ -495,8 +481,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;
|
||||
@@ -507,10 +493,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();
|
||||
@@ -534,9 +517,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;
|
||||
};
|
||||
|
||||
@@ -563,6 +544,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) {
|
||||
@@ -596,7 +581,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);
|
||||
};
|
||||
|
||||
@@ -624,10 +609,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);
|
||||
@@ -636,7 +621,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;
|
||||
};
|
||||
|
||||
@@ -651,47 +647,18 @@ res.cookie = function(name, val, options){
|
||||
*
|
||||
* 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
|
||||
, path;
|
||||
var req = this.req;
|
||||
|
||||
// "back" is an alias for the referrer
|
||||
if ('back' == url) url = req.get('Referrer') || '/';
|
||||
|
||||
// relative
|
||||
if (!~url.indexOf('://') && 0 != url.indexOf('//')) {
|
||||
// relative to path
|
||||
if ('.' == url[0]) {
|
||||
path = req.originalUrl.split('?')[0];
|
||||
path = path + ('/' == path[path.length - 1] ? '' : '/');
|
||||
url = resolve(path, url);
|
||||
// relative to mount-point
|
||||
} else if ('/' != url[0]) {
|
||||
path = app.path();
|
||||
url = path + '/' + url;
|
||||
}
|
||||
}
|
||||
|
||||
// Respond
|
||||
this.set('Location', url);
|
||||
return this;
|
||||
@@ -719,9 +686,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) {
|
||||
@@ -744,7 +711,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>';
|
||||
},
|
||||
|
||||
@@ -769,31 +736,12 @@ res.redirect = function(url){
|
||||
*/
|
||||
|
||||
res.vary = function(field){
|
||||
var self = this;
|
||||
|
||||
// nothing
|
||||
// checks for back-compat
|
||||
if (!field) return this;
|
||||
if (Array.isArray(field) && !field.length) return this;
|
||||
|
||||
// array
|
||||
if (Array.isArray(field)) {
|
||||
field.forEach(function(field){
|
||||
self.vary(field);
|
||||
});
|
||||
return;
|
||||
}
|
||||
vary(this, field);
|
||||
|
||||
var vary = this.get('Vary');
|
||||
|
||||
// append
|
||||
if (vary) {
|
||||
vary = vary.split(/ *, */);
|
||||
if (!~vary.indexOf(field)) vary.push(field);
|
||||
this.set('Vary', vary.join(', '));
|
||||
return this;
|
||||
}
|
||||
|
||||
// set
|
||||
this.set('Vary', field);
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -807,17 +755,14 @@ res.vary = function(field){
|
||||
* - `cache` boolean hinting to the engine it should cache
|
||||
* - `filename` filename of the view being rendered
|
||||
*
|
||||
* @param {String} view
|
||||
* @param {Object|Function} options or callback function
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.render = function(view, options, fn){
|
||||
var self = this
|
||||
, options = options || {}
|
||||
, req = this.req
|
||||
, app = req.app;
|
||||
options = options || {};
|
||||
var self = this;
|
||||
var req = this.req;
|
||||
var app = req.app;
|
||||
|
||||
// support callback function as second arg
|
||||
if ('function' == typeof options) {
|
||||
|
||||
@@ -3,47 +3,74 @@
|
||||
*/
|
||||
|
||||
var Route = require('./route');
|
||||
var utils = require('../utils');
|
||||
var Layer = require('./layer');
|
||||
var methods = require('methods');
|
||||
var debug = require('debug')('express:router');
|
||||
var parseUrl = require('parseurl');
|
||||
|
||||
/**
|
||||
* Expose `Router` constructor.
|
||||
*/
|
||||
|
||||
exports = module.exports = Router;
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
/**
|
||||
* 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 +78,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,250 +103,333 @@ Router.prototype.param = function(name, fn){
|
||||
};
|
||||
|
||||
/**
|
||||
* Route dispatcher aka the route "middleware".
|
||||
* Dispatch a req, res into the router.
|
||||
*
|
||||
* @param {IncomingMessage} req
|
||||
* @param {ServerResponse} res
|
||||
* @param {Function} next
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype._dispatch = function(req, res, next){
|
||||
var params = this.params
|
||||
, self = this;
|
||||
proto.handle = function(req, res, done) {
|
||||
var self = this;
|
||||
|
||||
debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl);
|
||||
debug('dispatching %s %s', req.method, req.url);
|
||||
|
||||
// route dispatch
|
||||
(function pass(i, err){
|
||||
var paramCallbacks
|
||||
, paramIndex = 0
|
||||
, paramVal
|
||||
, route
|
||||
, keys
|
||||
, key;
|
||||
var method = req.method.toLowerCase();
|
||||
|
||||
// match next route
|
||||
function nextRoute(err) {
|
||||
pass(req._route_index + 1, err);
|
||||
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;
|
||||
var paramcalled = {};
|
||||
|
||||
// store options for OPTIONS request
|
||||
// only used if OPTIONS request
|
||||
var options = [];
|
||||
|
||||
// middleware and routes
|
||||
var stack = self.stack;
|
||||
|
||||
// manage inter-router variables
|
||||
var parent = req.next;
|
||||
var parentUrl = req.baseUrl || '';
|
||||
done = wrap(done, function(old, err) {
|
||||
req.baseUrl = parentUrl;
|
||||
req.next = parent;
|
||||
old(err);
|
||||
});
|
||||
req.next = next;
|
||||
|
||||
// for options requests, respond with a default if nothing else responds
|
||||
if (method === 'options') {
|
||||
done = wrap(done, function(old, err) {
|
||||
if (err || options.length === 0) return old(err);
|
||||
|
||||
var body = options.join(',');
|
||||
return res.set('Allow', body).send(body);
|
||||
});
|
||||
}
|
||||
|
||||
next();
|
||||
|
||||
function next(err) {
|
||||
if (err === 'route') {
|
||||
err = undefined;
|
||||
}
|
||||
|
||||
// match route
|
||||
req.route = route = self.matchRequest(req, i);
|
||||
var layer = stack[idx++];
|
||||
var layerPath;
|
||||
|
||||
// implied OPTIONS
|
||||
if (!route && 'OPTIONS' == req.method) return self._options(req, res, next);
|
||||
if (!layer) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
// no route
|
||||
if (!route) return next(err);
|
||||
debug('matched %s %s', route.method, route.path);
|
||||
if (slashAdded) {
|
||||
req.url = req.url.substr(1);
|
||||
slashAdded = false;
|
||||
}
|
||||
|
||||
// we have a route
|
||||
// start at param 0
|
||||
req.params = route.params;
|
||||
keys = route.keys;
|
||||
i = 0;
|
||||
req.baseUrl = parentUrl;
|
||||
req.url = protohost + removed + req.url.substr(protohost.length);
|
||||
req.originalUrl = req.originalUrl || req.url;
|
||||
removed = '';
|
||||
|
||||
// param callbacks
|
||||
function param(err) {
|
||||
paramIndex = 0;
|
||||
key = keys[i++];
|
||||
paramVal = key && req.params[key.name];
|
||||
paramCallbacks = key && params[key.name];
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
// Capture one-time layer values
|
||||
req.params = layer.params;
|
||||
layerPath = layer.path;
|
||||
|
||||
// this should be done for the layer
|
||||
return self.process_params(layer, paramcalled, 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[layerPath.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 = layerPath;
|
||||
req.url = protohost + req.url.substr(protohost.length + removed.length);
|
||||
|
||||
// Ensure leading slash
|
||||
if (!fqdn && req.url[0] !== '/') {
|
||||
req.url = '/' + req.url;
|
||||
slashAdded = true;
|
||||
}
|
||||
|
||||
// Setup base URL (no trailing slash)
|
||||
if (removed.length && removed.substr(-1) === '/') {
|
||||
req.baseUrl = parentUrl + removed.substring(0, removed.length - 1);
|
||||
} else {
|
||||
req.baseUrl = parentUrl + removed;
|
||||
}
|
||||
|
||||
debug('%s %s : %s', layer.handle.name || 'anonymous', layerPath, req.originalUrl);
|
||||
var arity = layer.handle.length;
|
||||
try {
|
||||
if ('route' == err) {
|
||||
nextRoute();
|
||||
} else if (err) {
|
||||
i = 0;
|
||||
callbacks(err);
|
||||
} else if (paramCallbacks && undefined !== paramVal) {
|
||||
paramCallback();
|
||||
} else if (key) {
|
||||
param();
|
||||
if (err && arity === 4) {
|
||||
layer.handle(err, req, res, next);
|
||||
} else if (!err && arity < 4) {
|
||||
layer.handle(req, res, next);
|
||||
} else {
|
||||
i = 0;
|
||||
callbacks();
|
||||
next(err);
|
||||
}
|
||||
} catch (err) {
|
||||
param(err);
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function wrap(old, fn) {
|
||||
return function () {
|
||||
var args = [old].concat(slice.call(arguments));
|
||||
fn.apply(this, args);
|
||||
};
|
||||
|
||||
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, next){
|
||||
var path = parseUrl(req).pathname
|
||||
, body = this._optionsFor(path).join(',');
|
||||
if (!body) return next();
|
||||
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){
|
||||
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
|
||||
*/
|
||||
|
||||
Router.prototype.matchRequest = function(req, i, head){
|
||||
var method = req.method.toLowerCase()
|
||||
, url = parseUrl(req)
|
||||
, path = url.pathname
|
||||
, routes = this.map
|
||||
, i = i || 0
|
||||
, route;
|
||||
|
||||
// HEAD support
|
||||
if (!head && 'head' == method) {
|
||||
route = this.matchRequest(req, i, true);
|
||||
if (route) return route;
|
||||
method = 'get';
|
||||
}
|
||||
|
||||
// routes for this method
|
||||
if (routes = routes[method]) {
|
||||
|
||||
// matching routes
|
||||
for (var len = routes.length; i < len; ++i) {
|
||||
route = routes[i];
|
||||
if (route.match(path)) {
|
||||
req._route_index = i;
|
||||
return route;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempt to match a route for `method`
|
||||
* and `url` with optional starting
|
||||
* index of `i` defaulting to 0.
|
||||
* Process any parameters for the layer.
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} url
|
||||
* @param {Number} i
|
||||
* @return {Route}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype.match = function(method, url, i, head){
|
||||
var req = { method: method, url: url };
|
||||
return this.matchRequest(req, i, head);
|
||||
proto.process_params = function(layer, called, req, res, done) {
|
||||
var params = this.params;
|
||||
|
||||
// captured parameters from the layer, keys and values
|
||||
var keys = layer.keys;
|
||||
|
||||
// fast track
|
||||
if (!keys || keys.length === 0) {
|
||||
return done();
|
||||
}
|
||||
|
||||
var i = 0;
|
||||
var name;
|
||||
var paramIndex = 0;
|
||||
var key;
|
||||
var paramVal;
|
||||
var paramCallbacks;
|
||||
var paramCalled;
|
||||
|
||||
// process params in order
|
||||
// param callbacks can be async
|
||||
function param(err) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
if (i >= keys.length ) {
|
||||
return done();
|
||||
}
|
||||
|
||||
paramIndex = 0;
|
||||
key = keys[i++];
|
||||
|
||||
if (!key) {
|
||||
return done();
|
||||
}
|
||||
|
||||
name = key.name;
|
||||
paramVal = req.params[name];
|
||||
paramCallbacks = params[name];
|
||||
paramCalled = called[name];
|
||||
|
||||
if (paramVal === undefined || !paramCallbacks) {
|
||||
return param();
|
||||
}
|
||||
|
||||
// param previously called with same value or error occurred
|
||||
if (paramCalled && (paramCalled.err || paramCalled.val === paramVal)) {
|
||||
return param(paramCalled.err);
|
||||
}
|
||||
|
||||
called[name] = paramCalled = { val: paramVal };
|
||||
|
||||
try {
|
||||
return paramCallback();
|
||||
} catch (err) {
|
||||
return done(err);
|
||||
}
|
||||
}
|
||||
|
||||
// single param callbacks
|
||||
function paramCallback(err) {
|
||||
if (err && paramCalled) {
|
||||
// store error
|
||||
paramCalled.err = err;
|
||||
}
|
||||
|
||||
var fn = paramCallbacks[paramIndex++];
|
||||
if (err || !fn) return param(err);
|
||||
fn(req, res, paramCallback, paramVal, key.name);
|
||||
}
|
||||
|
||||
param();
|
||||
};
|
||||
|
||||
/**
|
||||
* Route `method`, `path`, and one or more callbacks.
|
||||
* Use the given middleware function, with optional path, defaulting to "/".
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} path
|
||||
* @param {Function} callback...
|
||||
* @return {Router} for chaining
|
||||
* @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.route = function(method, path, callbacks){
|
||||
var method = method.toLowerCase()
|
||||
, callbacks = utils.flatten([].slice.call(arguments, 2));
|
||||
proto.use = function(route, fn){
|
||||
// default route to '/'
|
||||
if ('string' != typeof route) {
|
||||
fn = route;
|
||||
route = '/';
|
||||
}
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
Router.prototype.all = function(path) {
|
||||
var self = this;
|
||||
var args = [].slice.call(arguments);
|
||||
methods.forEach(function(method){
|
||||
self.route.apply(self, [method].concat(args));
|
||||
});
|
||||
return this;
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
methods.forEach(function(method){
|
||||
Router.prototype[method] = function(path){
|
||||
var args = [method].concat([].slice.call(arguments));
|
||||
this.route.apply(this, args);
|
||||
// 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,67 +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();
|
||||
|
||||
try {
|
||||
var 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.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;
|
||||
};
|
||||
});
|
||||
|
||||
220
lib/utils.js
220
lib/utils.js
@@ -1,19 +1,13 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var mime = require('connect').mime
|
||||
, deprecate = require('util').deprecate
|
||||
, proxyaddr = require('proxy-addr')
|
||||
, crc32 = require('buffer-crc32')
|
||||
, crypto = require('crypto');
|
||||
|
||||
/**
|
||||
* toString ref.
|
||||
*/
|
||||
|
||||
var toString = {}.toString;
|
||||
var mime = require('send').mime;
|
||||
var crc32 = require('buffer-crc32');
|
||||
var crypto = require('crypto');
|
||||
var basename = require('path').basename;
|
||||
var deprecate = require('util').deprecate;
|
||||
var proxyaddr = require('proxy-addr');
|
||||
|
||||
/**
|
||||
* Simple detection of charset parameter in content-type
|
||||
@@ -88,25 +82,6 @@ exports.wetag = function wetag(body, encoding){
|
||||
return 'W/"' + len.toString(16) + '-' + crc32.unsigned(buf) + '"'
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@@ -130,8 +105,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);
|
||||
@@ -175,125 +150,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;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -322,61 +197,6 @@ 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');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile "etag" value to function.
|
||||
*
|
||||
|
||||
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`.
|
||||
|
||||
80
package.json
80
package.json
@@ -1,37 +1,15 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Sinatra inspired web development framework",
|
||||
"version": "3.9.0",
|
||||
"version": "4.4.2",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "TJ Holowaychuk",
|
||||
"email": "tj@vision-media.ca"
|
||||
},
|
||||
{
|
||||
"name": "Aaron Heckmann",
|
||||
"email": "aaron.heckmann+github@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Ciaran Jessup",
|
||||
"email": "ciaranj@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Douglas Christopher Wilson",
|
||||
"email": "doug@somethingdoug.com"
|
||||
},
|
||||
{
|
||||
"name": "Guillermo Rauch",
|
||||
"email": "rauchg@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Jonathan Ong",
|
||||
"email": "me@jongleberry.com"
|
||||
},
|
||||
{
|
||||
"name": "Roman Shtylman",
|
||||
"email": "shtylman+expressjs@gmail.com"
|
||||
}
|
||||
"Aaron Heckmann <aaron.heckmann+github@gmail.com>",
|
||||
"Ciaran Jessup <ciaranj@gmail.com>",
|
||||
"Douglas Christopher Wilson <doug@somethingdoug.com>",
|
||||
"Guillermo Rauch <rauchg@gmail.com>",
|
||||
"Jonathan Ong <me@jongleberry.com>",
|
||||
"Roman Shtylman <shtylman+expressjs@gmail.com"
|
||||
],
|
||||
"keywords": [
|
||||
"express",
|
||||
@@ -44,40 +22,50 @@
|
||||
"app",
|
||||
"api"
|
||||
],
|
||||
"repository": "git://github.com/visionmedia/express",
|
||||
"repository": "visionmedia/express",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "1.0.2",
|
||||
"buffer-crc32": "0.2.1",
|
||||
"connect": "2.18.0",
|
||||
"commander": "1.3.2",
|
||||
"methods": "1.0.0",
|
||||
"mkdirp": "0.5.0",
|
||||
"debug": "1.0.1",
|
||||
"escape-html": "1.0.1",
|
||||
"methods": "1.0.1",
|
||||
"parseurl": "1.0.1",
|
||||
"proxy-addr": "1.0.0",
|
||||
"proxy-addr": "1.0.1",
|
||||
"range-parser": "1.0.0",
|
||||
"send": "0.4.0",
|
||||
"send": "0.4.2",
|
||||
"serve-static": "1.2.2",
|
||||
"type-is": "1.2.1",
|
||||
"vary": "0.1.0",
|
||||
"cookie": "0.1.2",
|
||||
"fresh": "0.2.2",
|
||||
"cookie-signature": "1.0.3",
|
||||
"merge-descriptors": "0.0.2",
|
||||
"debug": ">= 0.8.0 < 1"
|
||||
"utils-merge": "1.0.0",
|
||||
"qs": "0.6.6",
|
||||
"path-to-regexp": "0.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"after": "0.8.1",
|
||||
"istanbul": "0.2.10",
|
||||
"mocha": "~1.20.0",
|
||||
"should": "~4.0.0",
|
||||
"mocha": "~1.20.1",
|
||||
"should": "~4.0.4",
|
||||
"supertest": "~0.13.0",
|
||||
"connect-redis": "~2.0.0",
|
||||
"ejs": "~1.0.0",
|
||||
"jade": "~1.3.1",
|
||||
"hjs": "~0.0.6",
|
||||
"marked": "0.3.2",
|
||||
"connect-redis": "~1.4.5",
|
||||
"supertest": "~0.13.0"
|
||||
"multiparty": "~3.2.4",
|
||||
"hjs": "~0.0.6",
|
||||
"body-parser": "1.3.0",
|
||||
"cookie-parser": "1.1.0",
|
||||
"express-session": "1.2.1",
|
||||
"method-override": "2.0.2",
|
||||
"morgan": "1.1.1",
|
||||
"vhost": "2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
},
|
||||
"bin": {
|
||||
"express": "./bin/express"
|
||||
"node": ">= 0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublish": "npm prune",
|
||||
|
||||
@@ -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');
|
||||
|
||||
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();
|
||||
190
test/Route.js
Normal file
190
test/Route.js
Normal file
@@ -0,0 +1,190 @@
|
||||
|
||||
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' }, {});
|
||||
});
|
||||
|
||||
it('should handle throwing inside error handlers', function(done) {
|
||||
var route = new Route('');
|
||||
|
||||
route.get(function(req, res, next){
|
||||
throw new Error('boom!');
|
||||
});
|
||||
|
||||
route.get(function(err, req, res, next){
|
||||
throw new Error('oops');
|
||||
});
|
||||
|
||||
route.get(function(err, req, res, next){
|
||||
assert.equal(err.message, 'oops');
|
||||
done();
|
||||
});
|
||||
|
||||
route.dispatch({ url: '/', method: 'GET' }, {}, done);
|
||||
});
|
||||
})
|
||||
})
|
||||
336
test/Router.js
336
test/Router.js
@@ -1,121 +1,321 @@
|
||||
|
||||
var after = require('after');
|
||||
var express = require('../')
|
||||
, Router = express.Router
|
||||
, request = require('supertest')
|
||||
, 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('.all', function() {
|
||||
it('should support using .all to capture all http verbs', function() {
|
||||
describe('error', function(){
|
||||
it('should skip non error middleware', function(done){
|
||||
var router = new Router();
|
||||
|
||||
router.all('/foo', function(){});
|
||||
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' }, {}, function() {});
|
||||
});
|
||||
|
||||
it('should handle throwing in handler after async param', function(done) {
|
||||
var router = new Router();
|
||||
|
||||
router.param('user', function(req, res, next, val){
|
||||
process.nextTick(function(){
|
||||
req.user = val;
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
router.use('/:user', function(req, res, next){
|
||||
throw new Error('oh no!');
|
||||
});
|
||||
|
||||
router.use(function(err, req, res, next){
|
||||
assert.equal(err.message, 'oh no!');
|
||||
done();
|
||||
});
|
||||
|
||||
router.handle({ url: '/bob', method: 'GET' }, {}, function() {});
|
||||
});
|
||||
|
||||
it('should handle throwing inside error handlers', function(done) {
|
||||
var router = new Router();
|
||||
|
||||
router.use(function(req, res, next){
|
||||
throw new Error('boom!');
|
||||
});
|
||||
|
||||
router.use(function(err, req, res, next){
|
||||
throw new Error('oops');
|
||||
});
|
||||
|
||||
router.use(function(err, req, res, next){
|
||||
assert.equal(err.message, 'oops');
|
||||
done();
|
||||
});
|
||||
|
||||
router.handle({ url: '/', 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) {
|
||||
var route = router.match(method, url);
|
||||
route.constructor.name.should.equal('Route');
|
||||
route.method.should.equal(method);
|
||||
route.path.should.equal('/foo');
|
||||
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);
|
||||
});
|
||||
|
||||
it('should only call once per request', function(done) {
|
||||
var count = 0;
|
||||
var req = { url: '/foo/bob/bar', method: 'get' };
|
||||
var router = new Router();
|
||||
var sub = new Router();
|
||||
|
||||
sub.get('/bar', function(req, res, next) {
|
||||
next();
|
||||
});
|
||||
|
||||
router.param('user', function(req, res, next, user) {
|
||||
count++;
|
||||
req.user = user;
|
||||
next();
|
||||
});
|
||||
|
||||
router.use('/foo/:user/', new Router());
|
||||
router.use('/foo/:user/', sub);
|
||||
|
||||
router.handle(req, {}, function(err) {
|
||||
if (err) return done(err);
|
||||
assert.equal(count, 1);
|
||||
assert.equal(req.user, 'bob');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should call when values differ', function(done) {
|
||||
var count = 0;
|
||||
var req = { url: '/foo/bob/bar', method: 'get' };
|
||||
var router = new Router();
|
||||
var sub = new Router();
|
||||
|
||||
sub.get('/bar', function(req, res, next) {
|
||||
next();
|
||||
});
|
||||
|
||||
router.param('user', function(req, res, next, user) {
|
||||
count++;
|
||||
req.user = user;
|
||||
next();
|
||||
});
|
||||
|
||||
router.use('/foo/:user/', new Router());
|
||||
router.use('/:user/bob/', sub);
|
||||
|
||||
router.handle(req, {}, function(err) {
|
||||
if (err) return done(err);
|
||||
assert.equal(count, 2);
|
||||
assert.equal(req.user, 'foo');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('parallel requests', function() {
|
||||
it('should not mix requests', function(done) {
|
||||
var req1 = { url: '/foo/50/bar', method: 'get' };
|
||||
var req2 = { url: '/foo/10/bar', method: 'get' };
|
||||
var router = new Router();
|
||||
var sub = new Router();
|
||||
|
||||
done = after(2, done);
|
||||
|
||||
sub.get('/bar', function(req, res, next) {
|
||||
next();
|
||||
});
|
||||
|
||||
router.param('ms', function(req, res, next, ms) {
|
||||
ms = parseInt(ms, 10);
|
||||
req.ms = ms;
|
||||
setTimeout(next, ms);
|
||||
});
|
||||
|
||||
router.use('/foo/:ms/', new Router());
|
||||
router.use('/foo/:ms/', sub);
|
||||
|
||||
router.handle(req1, {}, function(err) {
|
||||
assert.ifError(err);
|
||||
assert.equal(req1.ms, 50);
|
||||
assert.equal(req1.originalUrl, '/foo/50/bar');
|
||||
done();
|
||||
});
|
||||
|
||||
router.handle(req2, {}, function(err) {
|
||||
assert.ifError(err);
|
||||
assert.equal(req2.ms, 10);
|
||||
assert.equal(req2.originalUrl, '/foo/10/bar');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
@@ -30,4 +30,4 @@ describe('downloads', function(){
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 120 KiB |
@@ -92,6 +92,13 @@ describe('mvc', function(){
|
||||
})
|
||||
|
||||
describe('PUT /user/:id', function(){
|
||||
it('should 500 on error', function(done){
|
||||
request(app)
|
||||
.put('/user/1')
|
||||
.send({})
|
||||
.expect(500, done)
|
||||
})
|
||||
|
||||
it('should update the user', function(done){
|
||||
request(app)
|
||||
.put('/user/1')
|
||||
|
||||
46
test/acceptance/vhost.js
Normal file
46
test/acceptance/vhost.js
Normal file
@@ -0,0 +1,46 @@
|
||||
var app = require('../../examples/vhost')
|
||||
var request = require('supertest')
|
||||
|
||||
describe('vhost', function(){
|
||||
describe('example.com', function(){
|
||||
describe('GET /', function(){
|
||||
it('should say hello', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'example.com')
|
||||
.expect(200, /hello/i, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /foo', function(){
|
||||
it('should say foo', function(done){
|
||||
request(app)
|
||||
.get('/foo')
|
||||
.set('Host', 'example.com')
|
||||
.expect(200, 'requested foo', done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('foo.example.com', function(){
|
||||
describe('GET /', function(){
|
||||
it('should redirect to /foo', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'foo.example.com')
|
||||
.expect(302, /Redirecting to http:\/\/example.com:3000\/foo/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('bar.example.com', function(){
|
||||
describe('GET /', function(){
|
||||
it('should redirect to /bar', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Host', 'bar.example.com')
|
||||
.expect(302, /Redirecting to http:\/\/example.com:3000\/bar/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
20
test/app.js
20
test/app.js
@@ -25,7 +25,7 @@ describe('app.parent', function(){
|
||||
})
|
||||
})
|
||||
|
||||
describe('app.route', function(){
|
||||
describe('app.mountpath', function(){
|
||||
it('should return the mounted path', function(){
|
||||
var app = express()
|
||||
, blog = express()
|
||||
@@ -34,9 +34,21 @@ describe('app.route', function(){
|
||||
app.use('/blog', blog);
|
||||
blog.use('/admin', blogAdmin);
|
||||
|
||||
app.route.should.equal('/');
|
||||
blog.route.should.equal('/blog');
|
||||
blogAdmin.route.should.equal('/admin');
|
||||
app.mountpath.should.equal('/');
|
||||
blog.mountpath.should.equal('/blog');
|
||||
blogAdmin.mountpath.should.equal('/admin');
|
||||
})
|
||||
})
|
||||
|
||||
describe('app.router', function(){
|
||||
it('should throw with notice', function(done){
|
||||
var app = express()
|
||||
|
||||
try {
|
||||
app.router;
|
||||
} catch(err) {
|
||||
done();
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ describe('app', function(){
|
||||
it('should merge locals', function(){
|
||||
var app = express();
|
||||
Object.keys(app.locals).should.eql(['settings']);
|
||||
app.locals({ user: 'tobi', age: 1 });
|
||||
app.locals({ age: 2 });
|
||||
app.locals.user = 'tobi';
|
||||
app.locals.age = 2;
|
||||
Object.keys(app.locals).should.eql(['settings', 'user', 'age']);
|
||||
app.locals.user.should.equal('tobi');
|
||||
app.locals.age.should.equal(2);
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('OPTIONS', function(){
|
||||
var router = new express.Router();
|
||||
|
||||
router.get('/users', function(req, res){});
|
||||
app.use(router.middleware);
|
||||
app.use(router);
|
||||
app.get('/other', function(req, res){});
|
||||
|
||||
request(app)
|
||||
|
||||
@@ -101,6 +101,62 @@ describe('app', function(){
|
||||
.expect('123', done);
|
||||
})
|
||||
|
||||
it('should only call once per request', function(done) {
|
||||
var app = express();
|
||||
var called = 0;
|
||||
var count = 0;
|
||||
|
||||
app.param('user', function(req, res, next, user) {
|
||||
called++;
|
||||
req.user = user;
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/foo/:user', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.get('/foo/:user', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.use(function(req, res) {
|
||||
res.end([count, called, req.user].join(' '));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/foo/bob')
|
||||
.expect('2 1 bob', done);
|
||||
})
|
||||
|
||||
it('should call when values differ', function(done) {
|
||||
var app = express();
|
||||
var called = 0;
|
||||
var count = 0;
|
||||
|
||||
app.param('user', function(req, res, next, user) {
|
||||
called++;
|
||||
req.users = (req.users || []).concat(user);
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/:user/bob', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.get('/foo/:user', function(req, res, next) {
|
||||
count++;
|
||||
next();
|
||||
});
|
||||
app.use(function(req, res) {
|
||||
res.end([count, called, req.users.join(',')].join(' '));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/foo/bob')
|
||||
.expect('2 2 foo,bob', done);
|
||||
})
|
||||
|
||||
it('should work with encoded values', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -156,5 +212,30 @@ describe('app', function(){
|
||||
.get('/user/123')
|
||||
.expect('name', done);
|
||||
})
|
||||
|
||||
it('should defer all the param routes', function(done){
|
||||
var app = express();
|
||||
|
||||
app.param('id', function(req, res, next, val){
|
||||
if (val === 'new') return next('route');
|
||||
return next();
|
||||
});
|
||||
|
||||
app.all('/user/:id', function(req, res){
|
||||
res.send('all.id');
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res){
|
||||
res.send('get.id');
|
||||
});
|
||||
|
||||
app.get('/user/new', function(req, res){
|
||||
res.send('get.new');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/new')
|
||||
.expect('get.new', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -95,7 +95,7 @@ describe('app', function(){
|
||||
app.render('user.jade', function(err, str){
|
||||
// nextTick to prevent cyclic
|
||||
process.nextTick(function(){
|
||||
err.message.should.match(/Cannot read property 'name' of undefined/);
|
||||
err.message.should.match(/Cannot read property '[^']+' of undefined/);
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
62
test/app.route.js
Normal file
62
test/app.route.js
Normal file
@@ -0,0 +1,62 @@
|
||||
var express = require('../');
|
||||
var request = require('supertest');
|
||||
|
||||
describe('app.route', function(){
|
||||
it('should return a new route', function(done){
|
||||
var app = express();
|
||||
|
||||
app.route('/foo')
|
||||
.get(function(req, res) {
|
||||
res.send('get');
|
||||
})
|
||||
.post(function(req, res) {
|
||||
res.send('post');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.post('/foo')
|
||||
.expect('post', done);
|
||||
});
|
||||
|
||||
it('should all .VERB after .all', function(done){
|
||||
var app = express();
|
||||
|
||||
app.route('/foo')
|
||||
.all(function(req, res, next) {
|
||||
next();
|
||||
})
|
||||
.get(function(req, res) {
|
||||
res.send('get');
|
||||
})
|
||||
.post(function(req, res) {
|
||||
res.send('post');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.post('/foo')
|
||||
.expect('post', done);
|
||||
});
|
||||
|
||||
it('should support dynamic routes', function(done){
|
||||
var app = express();
|
||||
|
||||
app.route('/:foo')
|
||||
.get(function(req, res) {
|
||||
res.send(req.params.foo);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/test')
|
||||
.expect('test', done);
|
||||
});
|
||||
|
||||
it('should not error on empty routes', function(done){
|
||||
var app = express();
|
||||
|
||||
app.route('/:foo');
|
||||
|
||||
request(app)
|
||||
.get('/test')
|
||||
.expect(404, done);
|
||||
});
|
||||
});
|
||||
@@ -25,6 +25,11 @@ describe('app.router', function(){
|
||||
[method]('/foo')
|
||||
.expect('head' == method ? '' : method, done);
|
||||
})
|
||||
|
||||
it('should reject numbers for app.' + method, function(){
|
||||
var app = express();
|
||||
app[method].bind(app, '/', 3).should.throw(/Number/);
|
||||
})
|
||||
});
|
||||
})
|
||||
|
||||
@@ -88,36 +93,6 @@ describe('app.router', function(){
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(app.router);
|
||||
|
||||
app.use(function(req, res, next){
|
||||
calls.push('after');
|
||||
res.end();
|
||||
});
|
||||
|
||||
app.get('/', function(req, res, next){
|
||||
calls.push('GET /')
|
||||
next();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
calls.should.eql(['before', 'GET /', 'after'])
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should be auto .use()d on the first app.VERB() call', function(done){
|
||||
var app = express();
|
||||
|
||||
var calls = [];
|
||||
|
||||
app.use(function(req, res, next){
|
||||
calls.push('before');
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/', function(req, res, next){
|
||||
calls.push('GET /')
|
||||
next();
|
||||
@@ -642,6 +617,45 @@ describe('app.router', function(){
|
||||
.expect('editing user 12', done);
|
||||
})
|
||||
|
||||
it('should run in order added', function(done){
|
||||
var app = express();
|
||||
var path = [];
|
||||
|
||||
app.get('*', function(req, res, next){
|
||||
path.push(0);
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res, next){
|
||||
path.push(1);
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(function(req, res, next){
|
||||
path.push(2);
|
||||
next();
|
||||
});
|
||||
|
||||
app.all('/user/:id', function(req, res, next){
|
||||
path.push(3);
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('*', function(req, res, next){
|
||||
path.push(4);
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(function(req, res, next){
|
||||
path.push(5);
|
||||
res.end(path.join(','))
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/1')
|
||||
.expect(200, '0,1,2,3,4,5', done);
|
||||
})
|
||||
|
||||
it('should be chainable', function(){
|
||||
var app = express();
|
||||
app.get('/', function(){}).should.equal(app);
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
|
||||
var express = require('../')
|
||||
, assert = require('assert')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('app.routes', function(){
|
||||
it('should be initialized', function(){
|
||||
var app = express();
|
||||
app.routes.should.eql({});
|
||||
})
|
||||
|
||||
it('should be populated with routes', function(){
|
||||
var app = express();
|
||||
|
||||
app.get('/', function(req, res){});
|
||||
app.get('/user/:id', function(req, res){});
|
||||
|
||||
var get = app.routes.get;
|
||||
get.should.have.length(2);
|
||||
|
||||
get[0].path.should.equal('/');
|
||||
get[0].method.should.equal('get');
|
||||
get[0].regexp.toString().should.equal('/^\\/\\/?$/i');
|
||||
|
||||
get[1].path.should.equal('/user/:id');
|
||||
get[1].method.should.equal('get');
|
||||
})
|
||||
|
||||
it('should be mutable', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/', function(req, res){});
|
||||
app.get('/user/:id', function(req, res){});
|
||||
|
||||
var get = app.routes.get;
|
||||
get.should.have.length(2);
|
||||
|
||||
get[0].path.should.equal('/');
|
||||
get[0].method.should.equal('get');
|
||||
get[0].regexp.toString().should.equal('/^\\/\\/?$/i');
|
||||
|
||||
get.splice(1);
|
||||
|
||||
request(app)
|
||||
.get('/user/12')
|
||||
.expect(404, done);
|
||||
})
|
||||
})
|
||||
@@ -15,6 +15,11 @@ describe('app', function(){
|
||||
app.use(blog);
|
||||
})
|
||||
|
||||
it('should reject numbers', function(){
|
||||
var app = express();
|
||||
app.use.bind(app, 3).should.throw(/Number/);
|
||||
})
|
||||
|
||||
describe('.use(app)', function(){
|
||||
it('should mount the app', function(done){
|
||||
var blog = express()
|
||||
@@ -63,5 +68,20 @@ describe('app', function(){
|
||||
app.use('/blog', blog);
|
||||
blog.parent.should.equal(app);
|
||||
})
|
||||
|
||||
it('should support dynamic routes', function(done){
|
||||
var blog = express()
|
||||
, app = express();
|
||||
|
||||
blog.get('/', function(req, res){
|
||||
res.end('success');
|
||||
});
|
||||
|
||||
app.use('/post/:article', blog);
|
||||
|
||||
request(app)
|
||||
.get('/post/once-upon-a-time')
|
||||
.expect('success', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
|
||||
var express = require('../');
|
||||
|
||||
describe('config', function(){
|
||||
describe('.configure()', function(){
|
||||
describe('when no env is given', function(){
|
||||
it('should always execute', function(){
|
||||
var app = express();
|
||||
var calls = [];
|
||||
|
||||
app.configure(function(){
|
||||
calls.push('all');
|
||||
});
|
||||
|
||||
app.configure('test', function(){
|
||||
calls.push('test');
|
||||
});
|
||||
|
||||
app.configure('test', function(){
|
||||
calls.push('test 2');
|
||||
});
|
||||
|
||||
calls.should.eql(['all', 'test', 'test 2'])
|
||||
})
|
||||
})
|
||||
|
||||
describe('when an env is given', function(){
|
||||
it('should only execute the matching env', function(){
|
||||
var app = express();
|
||||
var calls = [];
|
||||
|
||||
app.set('env', 'development');
|
||||
|
||||
app.configure('development', function(){
|
||||
calls.push('dev');
|
||||
});
|
||||
|
||||
app.configure('test', function(){
|
||||
calls.push('test');
|
||||
});
|
||||
|
||||
calls.should.eql(['dev']);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when several envs are given', function(){
|
||||
it('should execute when matching one', function(){
|
||||
var app = express();
|
||||
var calls = [];
|
||||
|
||||
app.set('env', 'development');
|
||||
|
||||
app.configure('development', function(){
|
||||
calls.push('dev');
|
||||
});
|
||||
|
||||
app.configure('test', 'development', function(){
|
||||
calls.push('dev 2');
|
||||
});
|
||||
|
||||
app.configure('development', 'test', function(){
|
||||
calls.push('dev 3');
|
||||
});
|
||||
|
||||
app.configure('test', function(){
|
||||
calls.push('dev 3');
|
||||
});
|
||||
|
||||
calls.should.eql(['dev', 'dev 2', 'dev 3']);
|
||||
})
|
||||
})
|
||||
|
||||
it('should execute in order as defined', function(){
|
||||
var app = express();
|
||||
var calls = [];
|
||||
|
||||
app.configure(function(){
|
||||
calls.push('all');
|
||||
});
|
||||
|
||||
app.configure('test', function(){
|
||||
calls.push('test');
|
||||
});
|
||||
|
||||
app.configure(function(){
|
||||
calls.push('all 2');
|
||||
});
|
||||
|
||||
app.configure('test', function(){
|
||||
calls.push('test 2');
|
||||
});
|
||||
|
||||
calls.should.eql(['all', 'test', 'all 2', 'test 2'])
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,19 +1,9 @@
|
||||
|
||||
var express = require('../');
|
||||
var request = require('supertest');
|
||||
var assert = require('assert');
|
||||
var should = require('should');
|
||||
|
||||
describe('exports', function(){
|
||||
it('should expose connect middleware', function(){
|
||||
express.should.have.property('bodyParser');
|
||||
express.should.have.property('session');
|
||||
express.should.have.property('static');
|
||||
})
|
||||
|
||||
it('should expose .mime', function(){
|
||||
assert(express.mime == require('connect').mime, 'express.mime should be connect.mime');
|
||||
})
|
||||
|
||||
it('should expose Router', function(){
|
||||
express.Router.should.be.a.Function;
|
||||
})
|
||||
@@ -60,4 +50,12 @@ describe('exports', function(){
|
||||
.get('/')
|
||||
.expect('bar', done);
|
||||
})
|
||||
|
||||
it('should throw on old middlewares', function(){
|
||||
var error;
|
||||
try { express.bodyParser; } catch (e) { error = e; }
|
||||
should(error).have.property('message');
|
||||
error.message.should.containEql('middleware');
|
||||
error.message.should.containEql('bodyParser');
|
||||
})
|
||||
})
|
||||
|
||||
1
test/fixtures/.name
vendored
Normal file
1
test/fixtures/.name
vendored
Normal file
@@ -0,0 +1 @@
|
||||
tobi
|
||||
1
test/fixtures/name.jade
vendored
Normal file
1
test/fixtures/name.jade
vendored
Normal file
@@ -0,0 +1 @@
|
||||
p= name
|
||||
@@ -1,43 +1,44 @@
|
||||
//
|
||||
// var express = require('../')
|
||||
// , request = require('supertest');
|
||||
//
|
||||
// describe('middleware', function(){
|
||||
// describe('.next()', function(){
|
||||
// it('should behave like connect', function(done){
|
||||
// var app = express()
|
||||
// , calls = [];
|
||||
//
|
||||
// app.use(function(req, res, next){
|
||||
// calls.push('one');
|
||||
// next();
|
||||
// });
|
||||
//
|
||||
// app.use(function(req, res, next){
|
||||
// calls.push('two');
|
||||
// next();
|
||||
// });
|
||||
//
|
||||
// app.use(function(req, res){
|
||||
// var buf = '';
|
||||
// res.setHeader('Content-Type', 'application/json');
|
||||
// req.setEncoding('utf8');
|
||||
// req.on('data', function(chunk){ buf += chunk });
|
||||
// req.on('end', function(){
|
||||
// res.end(buf);
|
||||
// });
|
||||
// });
|
||||
//
|
||||
// request(app)
|
||||
// .get('/')
|
||||
// .set('Content-Type', 'application/json')
|
||||
// .write('{"foo":"bar"}')
|
||||
// .end(function(res){
|
||||
// res.headers.should.have.property('content-type', 'application/json');
|
||||
// res.statusCode.should.equal(200);
|
||||
// res.body.should.equal('{"foo":"bar"}');
|
||||
// done();
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
// })
|
||||
|
||||
var express = require('../');
|
||||
var request = require('supertest');
|
||||
|
||||
describe('middleware', function(){
|
||||
describe('.next()', function(){
|
||||
it('should behave like connect', function(done){
|
||||
var app = express()
|
||||
, calls = [];
|
||||
|
||||
app.use(function(req, res, next){
|
||||
calls.push('one');
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(function(req, res, next){
|
||||
calls.push('two');
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(function(req, res){
|
||||
var buf = '';
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
req.setEncoding('utf8');
|
||||
req.on('data', function(chunk){ buf += chunk });
|
||||
req.on('end', function(){
|
||||
res.end(buf);
|
||||
});
|
||||
});
|
||||
|
||||
request(app.listen())
|
||||
.get('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.send('{"foo":"bar"}')
|
||||
.end(function(err, res){
|
||||
if (err) return done(err);
|
||||
res.headers.should.have.property('content-type', 'application/json');
|
||||
res.statusCode.should.equal(200);
|
||||
res.text.should.equal('{"foo":"bar"}');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.accepted', function(){
|
||||
it('should return an array of accepted media types', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.accepted[0].value.should.equal('application/json');
|
||||
req.accepted[1].value.should.equal('text/html');
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept', 'text/html;q=.5, application/json')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
describe('when Accept is not present', function(){
|
||||
it('should default to []', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.accepted.should.have.length(0);
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.acceptedCharsets', function(){
|
||||
it('should return an array of accepted charsets', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptedCharsets[0].should.equal('unicode-1-1');
|
||||
req.acceptedCharsets[1].should.equal('iso-8859-5');
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Charset', 'iso-8859-5;q=.2, unicode-1-1;q=0.8')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
describe('when Accept-Charset is not present', function(){
|
||||
it('should default to []', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptedCharsets.should.have.length(0);
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200, done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,37 +0,0 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('supertest');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.acceptedEncodings', function(){
|
||||
it('should return an array of accepted encodings', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptedEncodings.should.eql(['gzip', 'deflate']);
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Encoding', ' gzip, deflate')
|
||||
.expect(200, done);
|
||||
})
|
||||
|
||||
describe('when Accept-Encoding is not present', function(){
|
||||
it('should default to []', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
req.acceptedEncodings.should.have.length(0);
|
||||
res.end();
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept-Encoding', '')
|
||||
.expect(200, done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user