mirror of
https://github.com/expressjs/express.git
synced 2026-02-27 19:20:15 +00:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb1f536eb5 | ||
|
|
7791169810 | ||
|
|
1b854f4297 | ||
|
|
52353da08b | ||
|
|
dc56b9b603 | ||
|
|
1446135545 | ||
|
|
cdc46307d7 | ||
|
|
dbf02c231a | ||
|
|
4732185e6f | ||
|
|
84a95b3101 | ||
|
|
09e8fe280b | ||
|
|
0e4ea9c16b | ||
|
|
c7542aef95 | ||
|
|
6873bce6c6 | ||
|
|
6ddacb6302 | ||
|
|
e1d33992a7 | ||
|
|
569c5139a6 | ||
|
|
6f5f5787e2 | ||
|
|
94cf769dd7 | ||
|
|
6d00b45eed | ||
|
|
6a4c6933aa | ||
|
|
c35ee2427e | ||
|
|
3d7216935a | ||
|
|
fc15c3d02b | ||
|
|
9eaec5b34e | ||
|
|
30712fa9d9 | ||
|
|
b817579d8a | ||
|
|
cbcaba3cec | ||
|
|
4e11fef43b | ||
|
|
067fdd5c4c | ||
|
|
fff815f666 | ||
|
|
609c18aa5b | ||
|
|
73c108ce90 | ||
|
|
1e9da205a7 | ||
|
|
d5539c7beb | ||
|
|
f87bd8c38f | ||
|
|
05515fa09e | ||
|
|
35c91ed6f5 | ||
|
|
26238c429d | ||
|
|
1b28ad16d4 | ||
|
|
e29f3aa5dd | ||
|
|
5c94603787 | ||
|
|
9409107f77 | ||
|
|
7f11aa25ea | ||
|
|
1994f24d82 | ||
|
|
b5b30a3f20 | ||
|
|
4fcbb961eb | ||
|
|
11cfad755a | ||
|
|
0b24bd08c9 | ||
|
|
b377839538 | ||
|
|
698d82f799 | ||
|
|
462a291eb8 | ||
|
|
79dc2467f7 | ||
|
|
0a0c86813d | ||
|
|
dfdc939816 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -6,7 +6,4 @@ lib-cov
|
||||
*.dat
|
||||
*.out
|
||||
*.pid
|
||||
*.swp
|
||||
*.swo
|
||||
benchmarks/graphs
|
||||
testing.js
|
||||
benchmarks/graphs
|
||||
12
.gitmodules
vendored
12
.gitmodules
vendored
@@ -13,18 +13,6 @@
|
||||
[submodule "support/connect"]
|
||||
path = support/connect
|
||||
url = git://github.com/senchalabs/connect.git
|
||||
[submodule "support/should"]
|
||||
path = support/should
|
||||
url = git://github.com/visionmedia/should.js.git
|
||||
[submodule "support/formidable"]
|
||||
path = support/formidable
|
||||
url = git://github.com/felixge/node-formidable.git
|
||||
[submodule "support/jade"]
|
||||
path = support/jade
|
||||
url = git://github.com/visionmedia/jade.git
|
||||
[submodule "support/qs"]
|
||||
path = support/qs
|
||||
url = git://github.com/visionmedia/node-querystring.git
|
||||
[submodule "support/mime"]
|
||||
path = support/mime
|
||||
url = https://github.com/bentomas/node-mime.git
|
||||
|
||||
11
.npmignore
11
.npmignore
@@ -1,7 +1,4 @@
|
||||
.git*
|
||||
docs/
|
||||
examples/
|
||||
support/
|
||||
test/
|
||||
testing.js
|
||||
.DS_Store
|
||||
test
|
||||
support
|
||||
examples
|
||||
docs
|
||||
|
||||
141
History.md
141
History.md
@@ -1,145 +1,4 @@
|
||||
|
||||
2.2.0 / 2011-03-30
|
||||
==================
|
||||
|
||||
* Added `app.lookup.VERB()`, ex `app.lookup.put('/user/:id')`. Closes #606
|
||||
* Added `app.match.VERB()`, ex `app.match.put('/user/12')`. Closes #606
|
||||
* Added `app.VERB(path)` as alias of `app.lookup.VERB()`.
|
||||
* Dependency `connect >= 1.1.6`
|
||||
|
||||
2.1.1 / 2011-03-29
|
||||
==================
|
||||
|
||||
* Added; expose `err.view` object when failing to locate a view
|
||||
* Fixed `res.partial()` call `next(err)` when no callback is given [reported by aheckmann]
|
||||
* Fixed; `res.send(undefined)` responds with 204 [aheckmann]
|
||||
|
||||
2.1.0 / 2011-03-24
|
||||
==================
|
||||
|
||||
* Added `<root>/_?<name>` partial lookup support. Closes #447
|
||||
* Added `request`, `response`, and `app` local variables
|
||||
* Added `settings` local variable, containing the app's settings
|
||||
* Added `req.flash()` exception if `req.session` is not available
|
||||
* Added `res.send(bool)` support (json response)
|
||||
* Fixed stylus example for latest version
|
||||
* Fixed; wrap try/catch around `res.render()`
|
||||
|
||||
2.0.0 / 2011-03-17
|
||||
==================
|
||||
|
||||
* Fixed up index view path alternative.
|
||||
* Changed; `res.locals()` without object returns the locals
|
||||
|
||||
2.0.0rc3 / 2011-03-17
|
||||
==================
|
||||
|
||||
* Added `res.locals(obj)` to compliment `res.local(key, val)`
|
||||
* Added `res.partial()` callback support
|
||||
* Fixed recursive error reporting issue in `res.render()`
|
||||
|
||||
2.0.0rc2 / 2011-03-17
|
||||
==================
|
||||
|
||||
* Changed; `partial()` "locals" are now optional
|
||||
* Fixed `SlowBuffer` support. Closes #584 [reported by tyrda01]
|
||||
* Fixed .filename view engine option [reported by drudge]
|
||||
* Fixed blog example
|
||||
* Fixed `{req,res}.app` reference when mounting [Ben Weaver]
|
||||
|
||||
2.0.0rc / 2011-03-14
|
||||
==================
|
||||
|
||||
* Fixed; expose `HTTPSServer` constructor
|
||||
* Fixed express(1) default test charset. Closes #579 [reported by secoif]
|
||||
* Fixed; default charset to utf-8 instead of utf8 for lame IE [reported by NickP]
|
||||
|
||||
2.0.0beta3 / 2011-03-09
|
||||
==================
|
||||
|
||||
* Added support for `res.contentType()` literal
|
||||
The original `res.contentType('.json')`,
|
||||
`res.contentType('application/json')`, and `res.contentType('json')`
|
||||
will work now.
|
||||
* Added `res.render()` status option support back
|
||||
* Added charset option for `res.render()`
|
||||
* Added `.charset` support (via connect 1.0.4)
|
||||
* Added view resolution hints when in development and a lookup fails
|
||||
* Added layout lookup support relative to the page view.
|
||||
For example while rendering `./views/user/index.jade` if you create
|
||||
`./views/user/layout.jade` it will be used in favour of the root layout.
|
||||
* Fixed `res.redirect()`. RFC states absolute url [reported by unlink]
|
||||
* Fixed; default `res.send()` string charset to utf8
|
||||
* Removed `Partial` constructor (not currently used)
|
||||
|
||||
2.0.0beta2 / 2011-03-07
|
||||
==================
|
||||
|
||||
* Added res.render() `.locals` support back to aid in migration process
|
||||
* Fixed flash example
|
||||
|
||||
2.0.0beta / 2011-03-03
|
||||
==================
|
||||
|
||||
* Added HTTPS support
|
||||
* Added `res.cookie()` maxAge support
|
||||
* Added `req.header()` _Referrer_ / _Referer_ special-case, either works
|
||||
* Added mount support for `res.redirect()`, now respects the mount-point
|
||||
* Added `union()` util, taking place of `merge(clone())` combo
|
||||
* Added stylus support to express(1) generated app
|
||||
* Added secret to session middleware used in examples and generated app
|
||||
* Added `res.local(name, val)` for progressive view locals
|
||||
* Added default param support to `req.param(name, default)`
|
||||
* Added `app.disabled()` and `app.enabled()`
|
||||
* Added `app.register()` support for omitting leading ".", either works
|
||||
* Added `res.partial()`, using the same interface as `partial()` within a view. Closes #539
|
||||
* Added `app.param()` to map route params to async/sync logic
|
||||
* Added; aliased `app.helpers()` as `app.locals()`. Closes #481
|
||||
* Added extname with no leading "." support to `res.contentType()`
|
||||
* Added `cache views` setting, defaulting to enabled in "production" env
|
||||
* Added index file partial resolution, eg: partial('user') may try _views/user/index.jade_.
|
||||
* Added `req.accepts()` support for extensions
|
||||
* Changed; `res.download()` and `res.sendfile()` now utilize Connect's
|
||||
static file server `connect.static.send()`.
|
||||
* Changed; replaced `connect.utils.mime()` with npm _mime_ module
|
||||
* Changed; allow `req.query` to be pre-defined (via middleware or other parent
|
||||
* Changed view partial resolution, now relative to parent view
|
||||
* Changed view engine signature. no longer `engine.render(str, options, callback)`, now `engine.compile(str, options) -> Function`, the returned function accepts `fn(locals)`.
|
||||
* Fixed `req.param()` bug returning Array.prototype methods. Closes #552
|
||||
* Fixed; using `Stream#pipe()` instead of `sys.pump()` in `res.sendfile()`
|
||||
* Fixed; using _qs_ module instead of _querystring_
|
||||
* Fixed; strip unsafe chars from jsonp callbacks
|
||||
* Removed "stream threshold" setting
|
||||
|
||||
1.0.8 / 2011-03-01
|
||||
==================
|
||||
|
||||
* Allow `req.query` to be pre-defined (via middleware or other parent app)
|
||||
* "connect": ">= 0.5.0 < 1.0.0". Closes #547
|
||||
* Removed the long deprecated __EXPRESS_ENV__ support
|
||||
|
||||
1.0.7 / 2011-02-07
|
||||
==================
|
||||
|
||||
* Fixed `render()` setting inheritance.
|
||||
Mounted apps would not inherit "view engine"
|
||||
|
||||
1.0.6 / 2011-02-07
|
||||
==================
|
||||
|
||||
* Fixed `view engine` setting bug when period is in dirname
|
||||
|
||||
1.0.5 / 2011-02-05
|
||||
==================
|
||||
|
||||
* Added secret to generated app `session()` call
|
||||
|
||||
1.0.4 / 2011-02-05
|
||||
==================
|
||||
|
||||
* Added `qs` dependency to _package.json_
|
||||
* Fixed namespaced `require()`s for latest connect support
|
||||
|
||||
1.0.3 / 2011-01-13
|
||||
==================
|
||||
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2009-2011 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2009-2010 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
|
||||
|
||||
61
Makefile
61
Makefile
@@ -1,11 +1,43 @@
|
||||
|
||||
DOCS = $(shell find docs/*.md)
|
||||
PREFIX ?= /usr/local
|
||||
LIB_PREFIX = ~/.node_libraries
|
||||
|
||||
DOCS = docs/index.md \
|
||||
docs/executable.md \
|
||||
docs/contrib.md \
|
||||
docs/guide.md \
|
||||
docs/migrate.md \
|
||||
docs/applications.md
|
||||
|
||||
MANPAGES =$(DOCS:.md=.1)
|
||||
HTMLDOCS =$(DOCS:.md=.html)
|
||||
|
||||
install: install-docs
|
||||
@mkdir -p $(PREFIX)/bin
|
||||
@mkdir -p $(LIB_PREFIX)
|
||||
cp -f bin/express $(PREFIX)/bin/express
|
||||
cp -fr lib/express $(LIB_PREFIX)/express
|
||||
|
||||
uninstall: uninstall-docs
|
||||
rm -f $(PREFIX)/bin/express
|
||||
rm -fr $(LIB_PREFIX)/express
|
||||
|
||||
install-support:
|
||||
cd support/connect && $(MAKE) install
|
||||
|
||||
uninstall-support:
|
||||
cd support/connect && $(MAKE) uninstall
|
||||
|
||||
install-docs:
|
||||
@mkdir -p $(PREFIX)/share/man/man1
|
||||
cp -f docs/executable.1 $(PREFIX)/share/man/man1/express.1
|
||||
|
||||
uninstall-docs:
|
||||
rm -f $(PREFIX)/share/man/man1/express.1
|
||||
|
||||
test:
|
||||
@NODE_ENV=test ./support/expresso/bin/expresso \
|
||||
-I lib \
|
||||
-I support \
|
||||
-I support/connect/lib \
|
||||
-I support/haml/lib \
|
||||
-I support/jade/lib \
|
||||
@@ -16,24 +48,29 @@ test:
|
||||
test-cov:
|
||||
@TESTFLAGS=--cov $(MAKE) test
|
||||
|
||||
docs: $(HTMLDOCS)
|
||||
docs: docs/api.html $(MANPAGES) $(HTMLDOCS)
|
||||
@ echo "... generating TOC"
|
||||
@./support/toc.js docs/guide.html
|
||||
|
||||
docs/api.html: lib/express/*.js
|
||||
dox \
|
||||
--private \
|
||||
--title Express \
|
||||
--desc "High performance web framework for [node](http://nodejs.org)." \
|
||||
$(shell find lib/express/* -type f) > $@
|
||||
|
||||
%.1: %.md
|
||||
@echo "... $< -> $@"
|
||||
@ronn -r --pipe $< > $@
|
||||
|
||||
%.html: %.md
|
||||
@echo "... $< -> $@"
|
||||
@markdown $< \
|
||||
@ronn -5 --pipe --fragment $< \
|
||||
| cat docs/layout/head.html - docs/layout/foot.html \
|
||||
| sed 's/NAME/Express/g' \
|
||||
> $@
|
||||
|
||||
site:
|
||||
rm -fr /tmp/docs \
|
||||
&& cp -fr docs /tmp/docs \
|
||||
&& git checkout gh-pages \
|
||||
&& cp -fr /tmp/docs/* . \
|
||||
&& echo "done"
|
||||
|
||||
docclean:
|
||||
rm -f docs/*.{1,html}
|
||||
|
||||
.PHONY: site test test-cov docs docclean
|
||||
.PHONY: install uninstall install-docs install-support uninstall-support install-docs uninstall-docs test test-cov docs docclean
|
||||
34
Readme.md
34
Readme.md
@@ -5,17 +5,23 @@
|
||||
built on [node](http://nodejs.org) and [Connect](http://github.com/senchalabs/connect).
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
res.send('Hello World');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
## Installation
|
||||
|
||||
npm:
|
||||
|
||||
$ npm install express
|
||||
|
||||
curl:
|
||||
|
||||
$ curl -# http://expressjs.com/install.sh | sh
|
||||
|
||||
## Features
|
||||
|
||||
* Robust routing
|
||||
@@ -54,28 +60,28 @@ The following are the major contributors of Express (in no specific order).
|
||||
|
||||
## More Information
|
||||
|
||||
* [express-configure](http://github.com/visionmedia/express-configuration) async configuration support
|
||||
* [express-messages](http://github.com/visionmedia/express-messages) flash notification rendering helper
|
||||
* [express-namespace](http://github.com/visionmedia/express-namespace) namespaced route support
|
||||
* Express [Contrib](http://github.com/visionmedia/express-contrib) repo for additional functionality
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
|
||||
* [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
* Visit the [Wiki](http://github.com/visionmedia/express/wiki)
|
||||
* Screencast - [Introduction](http://bit.ly/eRYu0O)
|
||||
* Screencast - [View Partials](http://bit.ly/dU13Fx)
|
||||
* Screencast - [Route Specific Middleware](http://bit.ly/hX4IaH)
|
||||
* Screencast - [Route Path Placeholder Preconditions](http://bit.ly/eNqmVs)
|
||||
|
||||
## Node Compatibility
|
||||
|
||||
The latest release of Express is compatible with node --version:
|
||||
|
||||
Express 1.x is compatible with node 0.2.x and connect < 1.0.
|
||||
v0.2.5
|
||||
|
||||
Express 2.x is compatible with node 0.4.x and connect 1.x
|
||||
and connect --version:
|
||||
|
||||
0.3.0
|
||||
|
||||
Express 1.x is maintained in the _1.x_ branch.
|
||||
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2009-2011 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2009-2010 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
|
||||
|
||||
99
bin/express
99
bin/express
@@ -5,13 +5,20 @@
|
||||
*/
|
||||
|
||||
var fs = require('fs')
|
||||
, sys = require('sys')
|
||||
, exec = require('child_process').exec;
|
||||
|
||||
/**
|
||||
* Framework version.
|
||||
*/
|
||||
|
||||
var version = '2.2.0';
|
||||
var version = '1.0.3';
|
||||
|
||||
/**
|
||||
* stdin stream.
|
||||
*/
|
||||
|
||||
var stdin;
|
||||
|
||||
/**
|
||||
* Add session support.
|
||||
@@ -36,15 +43,14 @@ var templateEngine = 'jade';
|
||||
*/
|
||||
|
||||
var usage = ''
|
||||
+ '\x1b[1mUsage\x1b[0m: express [options] [PATH]\n'
|
||||
+ '\n'
|
||||
+ ' Usage: express [options] [path]\n'
|
||||
+ '\n'
|
||||
+ ' Options:\n'
|
||||
+ ' -s, --sessions add session support\n'
|
||||
+ ' -t, --template <engine> add template <engine> support (jade|ejs). default=jade\n'
|
||||
+ ' -c, --css <engine> add stylesheet <engine> support (less|sass|stylus). default=plain css\n'
|
||||
+ ' -v, --version output framework version\n'
|
||||
+ ' -h, --help output help information\n'
|
||||
+ '\x1b[1mOptions\x1b[0m:\n'
|
||||
+ ' -s, --sessions Add session support\n'
|
||||
+ ' -t, --template ENGINE Add template ENGINE support (jade|ejs). Defaults to jade\n'
|
||||
+ ' -c, --css ENGINE Add stylesheet ENGINE support (less|sass). Defaults to plain css\n'
|
||||
+ ' -v, --version Output framework version\n'
|
||||
+ ' -h, --help Output help information\n'
|
||||
;
|
||||
|
||||
/**
|
||||
@@ -104,10 +110,6 @@ var css = [
|
||||
, ' padding: 50px;'
|
||||
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
|
||||
, '}'
|
||||
, ''
|
||||
, 'a {'
|
||||
, ' color: #00B7FF;'
|
||||
, '}'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
@@ -119,10 +121,6 @@ var less = [
|
||||
, ' padding: 50px;'
|
||||
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
|
||||
, '}'
|
||||
, ''
|
||||
, 'a {'
|
||||
, ' color: #00B7FF;'
|
||||
, '}'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
@@ -133,20 +131,6 @@ var sass = [
|
||||
'body'
|
||||
, ' :padding 50px'
|
||||
, ' :font 14px "Lucida Grande", Helvetica, Arial, sans-serif'
|
||||
, 'a'
|
||||
, ' :color #00B7FF'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* Default stylus template.
|
||||
*/
|
||||
|
||||
var stylus = [
|
||||
'body'
|
||||
, ' padding 50px'
|
||||
, ' font 14px "Lucida Grande", Helvetica, Arial, sans-serif'
|
||||
, 'a'
|
||||
, ' color #00B7FF'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
@@ -195,10 +179,10 @@ var app = [
|
||||
, 'app.configure(function(){'
|
||||
, ' app.set(\'views\', __dirname + \'/views\');'
|
||||
, ' app.set(\'view engine\', \':TEMPLATE\');'
|
||||
, ' app.use(express.bodyParser());'
|
||||
, ' app.use(express.methodOverride());{sess}{css}'
|
||||
, ' app.use(express.bodyDecoder());'
|
||||
, ' app.use(express.methodOverride());:SESS:CSS'
|
||||
, ' app.use(app.router);'
|
||||
, ' app.use(express.static(__dirname + \'/public\'));'
|
||||
, ' app.use(express.staticProvider(__dirname + \'/public\'));'
|
||||
, '});'
|
||||
, ''
|
||||
, 'app.configure(\'development\', function(){'
|
||||
@@ -213,7 +197,9 @@ var app = [
|
||||
, ''
|
||||
, 'app.get(\'/\', function(req, res){'
|
||||
, ' res.render(\'index\', {'
|
||||
, ' title: \'Express\''
|
||||
, ' locals: {'
|
||||
, ' title: \'Express\''
|
||||
, ' }'
|
||||
, ' });'
|
||||
, '});'
|
||||
, ''
|
||||
@@ -221,7 +207,7 @@ var app = [
|
||||
, ''
|
||||
, 'if (!module.parent) {'
|
||||
, ' app.listen(3000);'
|
||||
, ' console.log("Express server listening on port %d", app.address().port);'
|
||||
, ' console.log("Express server listening on port %d", app.address().port)'
|
||||
, '}'
|
||||
, ''
|
||||
].join('\n');
|
||||
@@ -273,7 +259,7 @@ while (args.length) {
|
||||
} else {
|
||||
confirm('destination is not empty, continue? ', function(ok){
|
||||
if (ok) {
|
||||
process.stdin.destroy();
|
||||
stdin.destroy();
|
||||
createApplicationAt(path);
|
||||
} else {
|
||||
abort('aborting');
|
||||
@@ -297,9 +283,6 @@ function createApplicationAt(path) {
|
||||
mkdir(path + '/public/images');
|
||||
mkdir(path + '/public/stylesheets', function(){
|
||||
switch (cssEngine) {
|
||||
case 'stylus':
|
||||
write(path + '/public/stylesheets/style.styl', stylus);
|
||||
break;
|
||||
case 'less':
|
||||
write(path + '/public/stylesheets/style.less', less);
|
||||
break;
|
||||
@@ -310,7 +293,7 @@ function createApplicationAt(path) {
|
||||
write(path + '/public/stylesheets/style.css', css);
|
||||
}
|
||||
});
|
||||
mkdir(path + '/views', function(){
|
||||
mkdir(path + '/views/partials', function(){
|
||||
switch (templateEngine) {
|
||||
case 'ejs':
|
||||
write(path + '/views/layout.ejs', ejsLayout);
|
||||
@@ -330,18 +313,15 @@ function createApplicationAt(path) {
|
||||
switch (cssEngine) {
|
||||
case 'sass':
|
||||
case 'less':
|
||||
app = app.replace('{css}', '\n app.use(express.compiler({ src: __dirname + \'/public\', enable: [\'' + cssEngine + '\'] }));');
|
||||
break;
|
||||
case 'stylus':
|
||||
app = app.replace('{css}', '\n app.use(require(\'stylus\').middleware({ src: __dirname + \'/public\' }));');
|
||||
app = app.replace(':CSS', '\n app.use(express.compiler({ src: __dirname + \'/public\', enable: [\'' + cssEngine + '\'] }));');
|
||||
break;
|
||||
default:
|
||||
app = app.replace('{css}', '');
|
||||
app = app.replace(':CSS', '');
|
||||
}
|
||||
|
||||
// Session support
|
||||
app = app.replace('{sess}', sessions
|
||||
? '\n app.use(express.cookieParser());\n app.use(express.session({ secret: \'your secret here\' }));'
|
||||
app = app.replace(':SESS', sessions
|
||||
? '\n app.use(express.cookieDecoder());\n app.use(express.session());'
|
||||
: '');
|
||||
|
||||
// Template support
|
||||
@@ -372,7 +352,7 @@ function createApplicationAt(path) {
|
||||
|
||||
function emptyDirectory(path, fn) {
|
||||
fs.readdir(path, function(err, files){
|
||||
if (err && 'ENOENT' != err.code) throw err;
|
||||
if (err && err.errno !== process.ENOENT) throw err;
|
||||
fn(!files || !files.length);
|
||||
});
|
||||
}
|
||||
@@ -386,7 +366,7 @@ function emptyDirectory(path, fn) {
|
||||
|
||||
function write(path, str) {
|
||||
fs.writeFile(path, str);
|
||||
console.log(' \x1b[36mcreate\x1b[0m : ' + path);
|
||||
console.log(' \x1b[33mcreate\x1b[0m : ' + path);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -410,18 +390,13 @@ function confirm(msg, fn) {
|
||||
*/
|
||||
|
||||
function prompt(msg, fn) {
|
||||
// prompt
|
||||
if (' ' == msg[msg.length - 1]) {
|
||||
process.stdout.write(msg);
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
// stdin
|
||||
process.stdin.setEncoding('ascii');
|
||||
process.stdin.once('data', function(data){
|
||||
stdin = stdin || process.openStdin();
|
||||
sys[msg[msg.length - 1] == ' ' ? 'print' : 'puts'](msg);
|
||||
stdin.setEncoding('ascii');
|
||||
stdin.addListener('data', function(data){
|
||||
fn(data);
|
||||
}).resume();
|
||||
stdin.removeListener('data', arguments.callee);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -434,7 +409,7 @@ function prompt(msg, fn) {
|
||||
function mkdir(path, fn) {
|
||||
exec('mkdir -p ' + path, function(err){
|
||||
if (err) throw err;
|
||||
console.log(' \x1b[36mcreate\x1b[0m : ' + path);
|
||||
console.log(' \x1b[33mcreate\x1b[0m : ' + path);
|
||||
fn && fn();
|
||||
});
|
||||
}
|
||||
|
||||
1676
docs/api.html
Normal file
1676
docs/api.html
Normal file
File diff suppressed because it is too large
Load Diff
70
docs/applications.1
Normal file
70
docs/applications.1
Normal file
@@ -0,0 +1,70 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "APPLICATIONS" "" "October 2010" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBapplications\fR
|
||||
.
|
||||
.P
|
||||
Learnboost \fIhttp://learnboost\.com\fR is a free online gradebook application, aimed to crush the competition with innovative, realtime, enjoyable features\.
|
||||
.
|
||||
.P
|
||||
\fIhttp://learnboost\.com\fR
|
||||
.
|
||||
.P
|
||||
Storify \fIhttp://storify\.com\fR lets you turn what people post on social media websites into compelling stories\.
|
||||
.
|
||||
.P
|
||||
\fIhttp://storify\.com\fR
|
||||
.
|
||||
.P
|
||||
Pakistan Survey \fIhttp://pakistansurvey\.org/\fR by Development Seed \fIhttp://developmentseed\.org\fR, provides in\-depth agency\-specific analysis from regional experts with data from 1,000 interviews across 120 villages in all seven tribal agencies and mapping of 142 reported drone strikes in FATA through July 2010\.
|
||||
.
|
||||
.P
|
||||
\fIhttp://pakistansurvey\.org\fR
|
||||
.
|
||||
.P
|
||||
Markup\.IO \fIhttp://markup\.io\fR allows you to draw directly on \fIany\fR website, then share with others to share your thoughts\.
|
||||
.
|
||||
.P
|
||||
\fIhttp://markup\.io\fR
|
||||
.
|
||||
.P
|
||||
Scrabb\.ly \fIhttp://scrabb\.ly\fR is a massively multiplayer scrabble game initially created for the Node Knockout \fIhttp://nodeknockout\.com/\fR competition\.
|
||||
.
|
||||
.P
|
||||
\fIhttp://scrabb\.ly\fR
|
||||
.
|
||||
.P
|
||||
ClickDummy \fIhttp://clickdummy\.net/\fR is a rapid mockup prototyping application for designers and dummies\.
|
||||
.
|
||||
.P
|
||||
\fIhttp://clickdummy\.net\fR
|
||||
.
|
||||
.P
|
||||
Node Knockout \fIhttp://nodeknockout\.com\fR organized the first ever node\-specific competition with hundreds of contestants\.
|
||||
.
|
||||
.P
|
||||
\fIhttp://nodeknockout\.com\fR
|
||||
.
|
||||
.P
|
||||
Widescript \fIhttp://widescript\.com\fR is an innovative app that helps you focus and interact with your texts \- on your desktop, your couch or on the go\.
|
||||
.
|
||||
.P
|
||||
\fIhttp://widescript\.com\fR
|
||||
.
|
||||
.P
|
||||
e\-resistable \fIhttp://www\.e\-resistible\.co\.uk/\fR is an online order takeaway system providing an intuitive way to fill your belly from your computer!
|
||||
.
|
||||
.P
|
||||
\fIhttp://www\.e\-resistible\.co\.uk\fR
|
||||
.
|
||||
.P
|
||||
Top Twitter Trends \fIhttp://toptwittertrends\.com\fR utilizes MongoDB, Socket\.IO, jQuery and many other exciting libraries to bring you trending tweets in realtime\.
|
||||
.
|
||||
.P
|
||||
\fIhttp://toptwittertrends\.com\fR
|
||||
.
|
||||
.P
|
||||
The applications shown above are not listed in any specific order\. To have an application added or removed please contact TJ Holowaychuk \fIhttp://github\.com/visionmedia\fR\.
|
||||
@@ -133,7 +133,7 @@
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 75px;
|
||||
margin-left: 65px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
@@ -187,9 +187,14 @@
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="screencasts.html">Screencasts</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="applications.html">Applications</a></li>
|
||||
</ul>
|
||||
<div class='mp'>
|
||||
<h2 id="Express">Express</h2>
|
||||
<p class="man-name">
|
||||
<code>applications</code>
|
||||
</p>
|
||||
|
||||
|
||||
<br />
|
||||
@@ -223,7 +228,7 @@
|
||||
|
||||
<p><a href="http://nodeknockout.com"><img src="images/apps/nodeko.png" alt="Node Knockout Competition Express" /></a></p>
|
||||
|
||||
<p><a href="http://widescript.com">Widescript</a> is an innovative app that helps you focus and interact with your texts – on your desktop, your couch or on the go.</p>
|
||||
<p><a href="http://widescript.com">Widescript</a> is an innovative app that helps you focus and interact with your texts - on your desktop, your couch or on the go.</p>
|
||||
|
||||
<p><a href="http://widescript.com"><img src="images/apps/widescript.png" alt="Widescript" /></a></p>
|
||||
|
||||
@@ -239,7 +244,9 @@
|
||||
|
||||
|
||||
<p>The applications shown above are not listed in any specific order. To have an application added or removed please contact <a href="http://github.com/visionmedia">TJ Holowaychuk</a>.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
82
docs/contrib.1
Normal file
82
docs/contrib.1
Normal file
@@ -0,0 +1,82 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "CONTRIB" "" "October 2010" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBcontrib\fR
|
||||
.
|
||||
.SS "Development Dependencies"
|
||||
Express development dependencies are stored within the \fI\./support\fR directory\. To update them execute:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ git submodule update \-\-init
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Running Tests"
|
||||
Express uses the Expresso \fIhttp://github\.com/visionmedia/expresso\fR TDD framework to write and run elegant test suites extremely fast\. To run all test suites simply execute:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ make test
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
To target specific suites we may specify the files via:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ make test TESTS=test/view\.test\.js
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
To check test coverage run:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ make test\-cov
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Contributions"
|
||||
To accept a contribution, you should follow these guidelines:
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
All tests \fImust\fR pass
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Your alterations or additions \fImust\fR include tests
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Your commit(s) should be \fIfocused\fR, do not commit once for several changes
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Do \fInot\fR alter release information such as the \fIversion\fR, or \fIHistory\.md\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Indents are \fI2\fR spaces\.
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Documentation"
|
||||
To contribute documentation edit the markdown files in \fI\./docs\fR, however do \fInot\fR run \fImake docs\fR, as they will be re\-built and published with each release\.
|
||||
@@ -133,7 +133,7 @@
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 75px;
|
||||
margin-left: 65px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
@@ -187,10 +187,15 @@
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="screencasts.html">Screencasts</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="applications.html">Applications</a></li>
|
||||
</ul>
|
||||
<h3>Development Dependencies</h3>
|
||||
<div class='mp'>
|
||||
<h2 id="Express">Express</h2>
|
||||
<p class="man-name">
|
||||
<code>contrib</code>
|
||||
</p>
|
||||
<h3 id="Development-Dependencies">Development Dependencies</h3>
|
||||
|
||||
<p>Express development dependencies are stored within the <em>./support</em> directory. To
|
||||
update them execute:</p>
|
||||
@@ -198,7 +203,7 @@ update them execute:</p>
|
||||
<pre><code>$ git submodule update --init
|
||||
</code></pre>
|
||||
|
||||
<h3>Running Tests</h3>
|
||||
<h3 id="Running-Tests">Running Tests</h3>
|
||||
|
||||
<p>Express uses the <a href="http://github.com/visionmedia/expresso">Expresso</a> TDD
|
||||
framework to write and run elegant test suites extremely fast. To run all test suites
|
||||
@@ -217,7 +222,7 @@ simply execute:</p>
|
||||
<pre><code>$ make test-cov
|
||||
</code></pre>
|
||||
|
||||
<h3>Contributions</h3>
|
||||
<h3 id="Contributions">Contributions</h3>
|
||||
|
||||
<p>To accept a contribution, you should follow these guidelines:</p>
|
||||
|
||||
@@ -230,11 +235,13 @@ simply execute:</p>
|
||||
</ul>
|
||||
|
||||
|
||||
<h3>Documentation</h3>
|
||||
<h3 id="Documentation">Documentation</h3>
|
||||
|
||||
<p>To contribute documentation edit the markdown files in <em>./docs</em>, however
|
||||
do <em>not</em> run <em>make docs</em>, as they will be re-built and published with each release.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
30
docs/executable.1
Normal file
30
docs/executable.1
Normal file
@@ -0,0 +1,30 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "EXECUTABLE" "" "October 2010" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBexecutable\fR
|
||||
.
|
||||
.SH "Synopsis"
|
||||
.
|
||||
.nf
|
||||
|
||||
express [\-h|\-\-help] [\-v|\-\-version] [\-c|\-css ENGINE] [PATH]
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.SH "Description"
|
||||
The \fBexpress\fR executable generates apps at the given \fBPATH\fR or the current working directory\. Although Express is not bound to a specific application structure, this executable creates a maintainable base app\.
|
||||
.
|
||||
.SH "Options"
|
||||
.
|
||||
.nf
|
||||
|
||||
\-s, \-\-sessions Add session support
|
||||
\-c, \-\-css ENGINE Add css ENGINE support (less|sass)\. Defaults to plain css
|
||||
\-v, \-\-version Output framework version
|
||||
\-h, \-\-help Display help information
|
||||
.
|
||||
.fi
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 75px;
|
||||
margin-left: 65px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
@@ -187,29 +187,35 @@
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="screencasts.html">Screencasts</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="applications.html">Applications</a></li>
|
||||
</ul>
|
||||
<h2>Synopsis</h2>
|
||||
<div class='mp'>
|
||||
<h2 id="Express">Express</h2>
|
||||
<p class="man-name">
|
||||
<code>executable</code>
|
||||
</p>
|
||||
<h2 id="Synopsis">Synopsis</h2>
|
||||
|
||||
<pre><code>express [options] [PATH]
|
||||
<pre><code>express [-h|--help] [-v|--version] [-c|-css ENGINE] [PATH]
|
||||
</code></pre>
|
||||
|
||||
<h2>Description</h2>
|
||||
<h2 id="Description">Description</h2>
|
||||
|
||||
<p>The <em>express</em> executable generates apps at the given <strong>PATH</strong> or the
|
||||
<p>The <code>express</code> executable generates apps at the given <strong>PATH</strong> or the
|
||||
current working directory. Although Express is not bound to a specific
|
||||
application structure, this executable creates a maintainable base app.</p>
|
||||
|
||||
<h2>Options</h2>
|
||||
<h2 id="Options">Options</h2>
|
||||
|
||||
<pre><code> -s, --sessions Add session support
|
||||
-t, --template ENGINE Add template ENGINE support (jade|ejs). Defaults to jade
|
||||
-c, --css ENGINE Add stylesheet ENGINE support (less|sass|stylus). Defaults to plain css
|
||||
-v, --version Output framework version
|
||||
-h, --help Output help information
|
||||
<pre><code>-s, --sessions Add session support
|
||||
-c, --css ENGINE Add css ENGINE support (less|sass). Defaults to plain css
|
||||
-v, --version Output framework version
|
||||
-h, --help Display help information
|
||||
</code></pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
|
||||
## Synopsis
|
||||
|
||||
express [options] [PATH]
|
||||
express [-h|--help] [-v|--version] [-c|-css ENGINE] [PATH]
|
||||
|
||||
## Description
|
||||
|
||||
The _express_ executable generates apps at the given **PATH** or the
|
||||
The `express` executable generates apps at the given **PATH** or the
|
||||
current working directory. Although Express is not bound to a specific
|
||||
application structure, this executable creates a maintainable base app.
|
||||
|
||||
## Options
|
||||
|
||||
-s, --sessions Add session support
|
||||
-t, --template ENGINE Add template ENGINE support (jade|ejs). Defaults to jade
|
||||
-c, --css ENGINE Add stylesheet ENGINE support (less|sass|stylus). Defaults to plain css
|
||||
-v, --version Output framework version
|
||||
-h, --help Output help information
|
||||
|
||||
|
||||
-s, --sessions Add session support
|
||||
-c, --css ENGINE Add css ENGINE support (less|sass). Defaults to plain css
|
||||
-v, --version Output framework version
|
||||
-h, --help Display help information
|
||||
|
||||
|
||||
1871
docs/guide.1
Normal file
1871
docs/guide.1
Normal file
File diff suppressed because it is too large
Load Diff
773
docs/guide.html
773
docs/guide.html
File diff suppressed because it is too large
Load Diff
582
docs/guide.md
582
docs/guide.md
@@ -1,26 +1,27 @@
|
||||
|
||||
### Installation
|
||||
|
||||
curl:
|
||||
|
||||
$ curl -# http://expressjs.com/install.sh | sh
|
||||
|
||||
npm:
|
||||
|
||||
$ npm install express
|
||||
|
||||
### Creating A Server
|
||||
### Creating An Application
|
||||
|
||||
To create an instance of the _express.HTTPServer_, simply invoke the _createServer()_ method. With our instance _app_ we can then define routes based on the HTTP verbs, in this example _app.get()_.
|
||||
The _express.Server_ now inherits from _http.Server_, however
|
||||
follows the same idiom by providing _express.createServer()_ as shown below. This means
|
||||
that you can utilize Express server's transparently with other libraries.
|
||||
|
||||
var app = require('express').createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('hello world');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
### Creating An HTTPS Server
|
||||
|
||||
To initialize a _express.HTTPSServer_ we do the same as above, however we
|
||||
pass an options object, accepting _key_, _cert_ and the others mentioned in node's [https documentation](http://nodejs.org/docs/v0.3.7/api/https.html#https.createServer).
|
||||
|
||||
var app = require('express').createServer({ key: ... });
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('hello world');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
### Configuration
|
||||
|
||||
@@ -30,43 +31,40 @@ _configure()_ is called without an environment name it will be run in _every_ en
|
||||
prior to the environment specific callback.
|
||||
|
||||
In the example below we only _dumpExceptions_, and respond with exception stack traces
|
||||
in _development_ mode, however for both environments we utilize _methodOverride_ and _bodyParser_.
|
||||
in _development_ mode, however for both environments we utilize _methodOverride_ and _bodyDecoder_.
|
||||
Note the use of _app.router_, which can (optionally) be used to mount the application routes,
|
||||
otherwise the first call to _app.get()_, _app.post()_, etc will mount the routes.
|
||||
otherwise the first call to _app.{get,put,del,post}()_ will mount the routes.
|
||||
|
||||
app.configure(function(){
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.bodyParser());
|
||||
app.use(app.router);
|
||||
});
|
||||
|
||||
app.configure('development', function(){
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||
});
|
||||
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.bodyDecoder());
|
||||
app.use(app.router);
|
||||
});
|
||||
|
||||
app.configure('development', function(){
|
||||
app.use(express.staticProvider(__dirname + '/public'));
|
||||
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||
});
|
||||
|
||||
app.configure('production', function(){
|
||||
var oneYear = 31557600000;
|
||||
app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
|
||||
app.use(express.errorHandler());
|
||||
app.use(express.staticProvider({ root: __dirname + '/public', maxAge: oneYear }));
|
||||
app.use(express.errorHandler());
|
||||
});
|
||||
|
||||
For internal and arbitrary settings Express provides the _set(key[, val])_, _enable(key)_, _disable(key)_ methods:
|
||||
|
||||
app.configure(function(){
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('views');
|
||||
// => "/absolute/path/to/views"
|
||||
|
||||
app.enable('some feature');
|
||||
// same as app.set('some feature', true);
|
||||
|
||||
app.disable('some feature');
|
||||
// same as app.set('some feature', false);
|
||||
|
||||
app.enabled('some feature')
|
||||
// => false
|
||||
});
|
||||
app.configure(function(){
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('views');
|
||||
// => "... views directory ..."
|
||||
|
||||
app.enable('some feature');
|
||||
// same as app.set('some feature', true);
|
||||
|
||||
app.disable('some feature');
|
||||
// same as app.set('some feature', false);
|
||||
});
|
||||
|
||||
To alter the environment we can set the _NODE_ENV_ environment variable, for example:
|
||||
|
||||
@@ -78,10 +76,13 @@ This is _very_ important, as many caching mechanisms are _only enabled_ when in
|
||||
|
||||
Express supports the following settings out of the box:
|
||||
|
||||
* _env_ Application environment set internally, use _app.set('env')_ on _Server#listen()_
|
||||
* _home_ Application base path used for _res.redirect()_ and transparently handling mounted apps.
|
||||
* _views_ Root views directory defaulting to **CWD/views**
|
||||
* _view engine_ Default view engine name for views rendered without extensions
|
||||
* _view options_ An object specifying global view options
|
||||
* _partials_ Root view partials directory defaulting to _views_/partials.
|
||||
* _stream threshold_ Bytesize indicating when a file should be streamed for _res.sendfile()_ using _fs.ReadStream()_ and _sys.pump()_.
|
||||
|
||||
### Routing
|
||||
|
||||
@@ -100,7 +101,7 @@ when _/user/:id_ is compiled, a simplified version of the regexp may look simila
|
||||
\/user\/([^\/]+)\/?
|
||||
|
||||
Regular expression literals may also be passed for complex uses. Since capture
|
||||
groups with literal _RegExp_'s are anonymous we can access them directly `req.params`. So our first capture group would be _req.params[0]_ and the second would follow as _req.params[1]_.
|
||||
groups with literal _RegExp_'s are anonymous we can access them directly `req.params`.
|
||||
|
||||
app.get(/^\/users?(?:\/(\d+)(?:\.\.(\d+))?)?/, function(req, res){
|
||||
res.send(req.params);
|
||||
@@ -147,17 +148,13 @@ may consume:
|
||||
/products.json
|
||||
/products.xml
|
||||
/products
|
||||
|
||||
"/user/:id.:format?"
|
||||
/user/12
|
||||
/user/12.json
|
||||
|
||||
For example we can __POST__ some json, and echo the json back using the _bodyParser_ middleware which will parse json request bodies (as well as others), and place the result in _req.body_:
|
||||
For example we can __POST__ some json, and echo the json back using the _bodyDecoder_ middleware which will parse json request bodies (as well as others), and place the result in _req.body_:
|
||||
|
||||
var express = require('express')
|
||||
, app = express.createServer();
|
||||
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.bodyDecoder());
|
||||
|
||||
app.post('/', function(req, res){
|
||||
res.send(req.body);
|
||||
@@ -165,13 +162,11 @@ For example we can __POST__ some json, and echo the json back using the _bodyPar
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
Typically we may use a "dumb" placeholder such as "/user/:id" which has no restrictions, however say for example we are limiting a user id to digits, we may use _'/user/:id(\\d+)'_ which will _not_ match unless the placeholder value contains only digits.
|
||||
|
||||
### Passing Route Control
|
||||
|
||||
We may pass control to the next _matching_ route, by calling the _third_ argument,
|
||||
the _next()_ function. When a match cannot be made, control is passed back to Connect,
|
||||
and middleware continue to be invoked in the order that they are added via _use()_. The same is true for several routes which have the same path defined, they will simply be executed in order until one does _not_ call _next()_ and decides to respond.
|
||||
and middleware continue to be invoked. The same is true for several routes which have the same path defined, they will simply be executed in order until one does _not_ call _next()_.
|
||||
|
||||
app.get('/users/:id?', function(req, res, next){
|
||||
var id = req.params.id;
|
||||
@@ -186,7 +181,7 @@ and middleware continue to be invoked in the order that they are added via _use(
|
||||
// do something else
|
||||
});
|
||||
|
||||
The _app.all()_ method is useful for applying the same logic for all HTTP verbs in a single call. Below we use this to load a user from our fake database, and assign it to _req.user_.
|
||||
Express 1.0 also introduces the _all()_ method, which provides a route callback matching any HTTP method. This is useful in many ways, one example being the loading of resources before executing subsequent routes as shown below:
|
||||
|
||||
var express = require('express')
|
||||
, app = express.createServer();
|
||||
@@ -228,11 +223,11 @@ passed to _express.createServer()_ as you would with a regular Connect server. F
|
||||
var express = require('express');
|
||||
|
||||
var app = express.createServer(
|
||||
express.logger()
|
||||
, express.bodyParser()
|
||||
express.logger(),
|
||||
express.bodyDecoder()
|
||||
);
|
||||
|
||||
Alternatively we can _use()_ them which is useful when adding middleware within _configure()_ blocks, in a progressive manor.
|
||||
Alternatively we can _use()_ them which is useful when adding middleware within _configure()_ blocks:
|
||||
|
||||
app.use(express.logger({ format: ':method :uri' }));
|
||||
|
||||
@@ -240,12 +235,10 @@ Typically with connect middleware you would _require('connect')_ like so:
|
||||
|
||||
var connect = require('connect');
|
||||
app.use(connect.logger());
|
||||
app.use(connect.bodyParser());
|
||||
|
||||
This is somewhat annoying, so express re-exports these middleware properties, however they are _identical_:
|
||||
|
||||
app.use(express.logger());
|
||||
app.use(express.bodyParser());
|
||||
|
||||
### Route Middleware
|
||||
|
||||
@@ -254,53 +247,53 @@ Routes may utilize route-specific middleware by passing one or more additional c
|
||||
Typically async data retrieval might look similar to below, where we take the _:id_ parameter, and attempt loading a user.
|
||||
|
||||
app.get('/user/:id', function(req, res, next){
|
||||
loadUser(req.params.id, function(err, user){
|
||||
if (err) return next(err);
|
||||
res.send('Viewing user ' + user.name);
|
||||
});
|
||||
loadUser(req.params.id, function(err, user){
|
||||
if (err) return next(err);
|
||||
res.send('Viewing user ' + user.name);
|
||||
});
|
||||
});
|
||||
|
||||
To keep things DRY and to increase readability we can apply this logic within a middleware. As you can see below, abstracting this logic into middleware allows us to reuse it, and clean up our route at the same time.
|
||||
|
||||
function loadUser(req, res, next) {
|
||||
// You would fetch your user from the db
|
||||
var user = users[req.params.id];
|
||||
if (user) {
|
||||
req.user = user;
|
||||
next();
|
||||
} else {
|
||||
next(new Error('Failed to load user ' + req.params.id));
|
||||
}
|
||||
// You would fetch your user from the db
|
||||
var user = users[req.params.id];
|
||||
if (user) {
|
||||
req.user = user;
|
||||
next();
|
||||
} else {
|
||||
next(new Error('Failed to load user ' + req.params.id));
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/user/:id', loadUser, function(req, res){
|
||||
res.send('Viewing user ' + req.user.name);
|
||||
res.send('Viewing user ' + req.user.name);
|
||||
});
|
||||
|
||||
Multiple route middleware can be applied, and will be executed sequentially to apply further logic such as restricting access to a user account. In the example below only the authenticated user may edit his/her account.
|
||||
|
||||
function andRestrictToSelf(req, res, next) {
|
||||
req.authenticatedUser.id == req.user.id
|
||||
? next()
|
||||
: next(new Error('Unauthorized'));
|
||||
req.authenticatedUser.id == req.user.id
|
||||
? next()
|
||||
: next(new Error('Unauthorized'));
|
||||
}
|
||||
|
||||
app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){
|
||||
res.send('Editing user ' + req.user.name);
|
||||
res.send('Editing user ' + req.user.name);
|
||||
});
|
||||
|
||||
Keeping in mind that middleware are simply functions, we can define function that _returns_ the middleware in order to create a more expressive and flexible solution as shown below.
|
||||
|
||||
function andRestrictTo(role) {
|
||||
return function(req, res, next) {
|
||||
req.authenticatedUser.role == role
|
||||
? next()
|
||||
: next(new Error('Unauthorized'));
|
||||
}
|
||||
return function(req, res, next) {
|
||||
req.authenticatedUser.role == role
|
||||
? next()
|
||||
: next(new Error('Unauthorized'));
|
||||
}
|
||||
}
|
||||
|
||||
app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
|
||||
res.send('Deleted user ' + req.user.name);
|
||||
res.send('Deleted user ' + req.user.name);
|
||||
});
|
||||
|
||||
Commonly used "stacks" of middleware can be passed as an array (_applied recursively_), which can be mixed and matched to any degree.
|
||||
@@ -330,20 +323,20 @@ We have seen _app.get()_ a few times, however Express also exposes other familia
|
||||
<input type="submit" value="Submit" />
|
||||
</form>
|
||||
|
||||
By default Express does not know what to do with this request body, so we should add the _bodyParser_ middleware, which will parse _application/x-www-form-urlencoded_ and _application/json_ request bodies and place the variables in _req.body_. We can do this by "using" the middleware as shown below:
|
||||
By default Express does not know what to do with this request body, so we should add the _bodyDecoder_ middleware, which will parse _application/x-www-form-urlencoded_ request bodies and place the variables in _req.body_. We can do this by "using" the middleware as shown below:
|
||||
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.bodyDecoder());
|
||||
|
||||
Our route below will now have access to the _req.body.user_ object which will contain the _name_ and _email_ properties when defined.
|
||||
|
||||
app.post('/', function(req, res){
|
||||
console.log(req.body.user);
|
||||
res.redirect('back');
|
||||
console.log(req.body.user);
|
||||
res.redirect('back');
|
||||
});
|
||||
|
||||
When using methods such as _PUT_ with a form, we can utilize a hidden input named _\_method_, which can be used to alter the HTTP method. To do so we first need the _methodOverride_ middleware, which should be placed below _bodyParser_ so that it can utilize it's _req.body_ containing the form values.
|
||||
When using methods such as _PUT_ with a form, we can utilize a hidden input named _\_method_, which can be used to alter the HTTP method. To do so we first need the _methodOverride_ middleware, which should be placed below _bodyDecoder_ so that it can utilize it's _req.body_ containing the form values.
|
||||
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.bodyDecoder());
|
||||
app.use(express.methodOverride());
|
||||
|
||||
The reason that these are not always defaults, is simply because these are not required for Express to be fully functional. Depending on the needs of your application, you may not need these at all, your methods such as _PUT_ and _DELETE_ can still be accessed by clients which can use them directly, although _methodOverride_ provides a great solution for forms. Below shows what the usage of _PUT_ might look like:
|
||||
@@ -366,21 +359,21 @@ Express provides the _app.error()_ method which receives exceptions thrown withi
|
||||
or passed to _next(err)_. Below is an example which serves different pages based on our
|
||||
ad-hoc _NotFound_ exception:
|
||||
|
||||
function NotFound(msg){
|
||||
this.name = 'NotFound';
|
||||
Error.call(this, msg);
|
||||
Error.captureStackTrace(this, arguments.callee);
|
||||
}
|
||||
|
||||
NotFound.prototype.__proto__ = Error.prototype;
|
||||
|
||||
app.get('/404', function(req, res){
|
||||
throw new NotFound;
|
||||
});
|
||||
|
||||
app.get('/500', function(req, res){
|
||||
throw new Error('keyboard cat!');
|
||||
});
|
||||
function NotFound(msg){
|
||||
this.name = 'NotFound';
|
||||
Error.call(this, msg);
|
||||
Error.captureStackTrace(this, arguments.callee);
|
||||
}
|
||||
|
||||
sys.inherits(NotFound, Error);
|
||||
|
||||
app.get('/404', function(req, res){
|
||||
throw new NotFound;
|
||||
});
|
||||
|
||||
app.get('/500', function(req, res){
|
||||
throw new Error('keyboard cat!');
|
||||
});
|
||||
|
||||
We can call _app.error()_ several times as shown below.
|
||||
Here we check for an instanceof _NotFound_ and show the
|
||||
@@ -400,13 +393,15 @@ handle exceptions in different ways based on the environment.
|
||||
});
|
||||
|
||||
Here we assume all errors as 500 for the simplicity of
|
||||
this demo, however you can choose whatever you like. For example when node performs filesystem syscalls, you may receive an error object with the _error.code_ of _ENOENT_, meaning "no such file or directory", we can utilize this in our error handling and display a page specific to this if desired.
|
||||
this demo, however you can choose whatever you like
|
||||
|
||||
app.error(function(err, req, res){
|
||||
res.render('500.jade', {
|
||||
error: err
|
||||
});
|
||||
});
|
||||
app.error(function(err, req, res){
|
||||
res.render('500.jade', {
|
||||
locals: {
|
||||
error: err
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Our apps could also utilize the Connect _errorHandler_ middleware
|
||||
to report on exceptions. For example if we wish to output exceptions
|
||||
@@ -422,59 +417,25 @@ that are passed or thrown, so we can set _showStack_ to true:
|
||||
The _errorHandler_ middleware also responds with _json_ if _Accept: application/json_
|
||||
is present, which is useful for developing apps that rely heavily on client-side JavaScript.
|
||||
|
||||
### Route Param Pre-conditions
|
||||
|
||||
Route param pre-conditions can drastically improve the readability of your application, through implicit loading of data, and validation of request urls. For example if you are constantly fetching common data for several routes, such as loading a user for _/user/:id_, we might typically do something like below:
|
||||
|
||||
app.get('/user/:userId', function(req, res, next){
|
||||
User.get(req.params.userId, function(err, user){
|
||||
if (err) return next(err);
|
||||
res.send('user ' + user.name);
|
||||
});
|
||||
});
|
||||
|
||||
With preconditions our params can be mapped to callbacks which may perform validation, coercion, or even loading data from a database. Below we invoke _app.param()_ with the parameter name we wish to map to some middleware, as you can see we receive the _id_ argument which contains the placeholder value. Using this we load the user and perform error handling as usual, and simple call _next()_ to pass control to the next precondition or route handler.
|
||||
|
||||
app.param('userId', function(req, res, next, id){
|
||||
User.get(id, function(err, user){
|
||||
if (err) return next(err);
|
||||
if (!user) return next(new Error('failed to find user'));
|
||||
req.user = user;
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
Doing so, as mentioned drastically improves our route readability, and allows us to easily share this logic throughout our application:
|
||||
|
||||
app.get('/user/:userId', function(req, res){
|
||||
res.send('user ' + req.user.name);
|
||||
});
|
||||
|
||||
For simple cases such as route placeholder validation and coercion we can simple pass a callback which has an arity of 1 (accepts one argument). Any errors thrown will be passed to _next(err)_.
|
||||
|
||||
app.param('number', function(n){ return parseInt(n, 10); });
|
||||
|
||||
We may also apply the same callback to several placeholders, for example a route GET _/commits/:from-:to_ are both numbers, so we may define them as an array:
|
||||
|
||||
app.param(['from', 'to'], function(n){ return parseInt(n, 10); });
|
||||
|
||||
### View Rendering
|
||||
|
||||
View filenames take the form "<name>.<engine>", where <engine> is the name
|
||||
View filenames take the form _NAME_._ENGINE_, where _ENGINE_ is the name
|
||||
of the module that will be required. For example the view _layout.ejs_ will
|
||||
tell the view system to _require('ejs')_, the module being loaded must export the method _exports.compile(str, options)_, and return a _Function_ to comply with Express. To alter this behaviour
|
||||
_app.register()_ can be used to map engines to file extensions, so that for example "foo.html" can be rendered by ejs.
|
||||
tell the view system to _require('ejs')_, the module being loaded must export the method _exports.render(str, options)_ to comply with Express, however
|
||||
_app.register()_ can be used to map engines to file extensions, so that for example "foo.html" can be rendered by jade.
|
||||
|
||||
Below is an example using [Jade](http://github.com/visionmedia/jade) to render _index.html_,
|
||||
and since we do not use _layout: false_ the rendered contents of _index.jade_ will be passed as
|
||||
the _body_ local variable in _layout.jade_.
|
||||
Below is an example using [Haml.js](http://github.com/visionmedia/haml.js) to render _index.html_,
|
||||
and since we do not use _layout: false_ the rendered contents of _index.html_ will be passed as
|
||||
the _body_ local variable in _layout.haml_.
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.jade', { title: 'My Site' });
|
||||
res.render('index.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
|
||||
The new _view engine_ setting allows us to specify our default template engine,
|
||||
so for example when using jade we could set:
|
||||
so for example when using [Jade](http://github.com/visionmedia/jade) we could set:
|
||||
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
@@ -494,10 +455,10 @@ mix and match template engines:
|
||||
Express also provides the _view options_ setting, which is applied each time a view is rendered, so for example if you rarely use layouts you may set:
|
||||
|
||||
app.set('view options', {
|
||||
layout: false
|
||||
layout: false
|
||||
});
|
||||
|
||||
Which can then be overridden within the _res.render()_ call if need be:
|
||||
Which can then be overridden within the `res.render()` call if need be:
|
||||
|
||||
res.render('myview.ejs', { layout: true });
|
||||
|
||||
@@ -522,70 +483,65 @@ A good example of this is specifying custom _ejs_ opening and closing tags:
|
||||
|
||||
### View Partials
|
||||
|
||||
The Express view system has built-in support for partials and collections, which are "mini" views representing a document fragment. For example rather than iterating
|
||||
in a view to display comments, we could use partial collection:
|
||||
The Express view system has built-in support for partials and collections, which are
|
||||
sort of "mini" views representing a document fragment. For example rather than iterating
|
||||
in a view to display comments, we would use a partial with collection support:
|
||||
|
||||
partial('comment.haml', { collection: comments });
|
||||
|
||||
To make things even less verbose we can assume the extension as _.haml_ when omitted,
|
||||
however if we wished we could use an ejs partial, within a haml view for example.
|
||||
|
||||
partial('comment', { collection: comments });
|
||||
|
||||
If no other options or local variables are desired, we can omit the object and simply pass our array, which is equivalent to above:
|
||||
And once again even further, when rendering a collection we can simply pass
|
||||
an array, if no other options are desired:
|
||||
|
||||
partial('comment', comments);
|
||||
|
||||
When using the partial collection support a few "magic" locals are provided
|
||||
When using the partial collection support a few "magic" variables are provided
|
||||
for free:
|
||||
|
||||
* _firstInCollection_ true if this is the first object
|
||||
* _indexInCollection_ index of the object in the collection
|
||||
* _lastInCollection_ true if this is the last object
|
||||
* _collectionLength_ length of the collection
|
||||
|
||||
Local variables passed (or generated) take precedence, however locals passed to the parent view are available in the child view as well. So for example if we were to render a blog post with _partial('blog/post', post)_ it would generate the _post_ local, but the view calling this function had the local _user_, it would be available to the _blog/post_ view as well.
|
||||
* _firstInCollection_ True if this is the first object
|
||||
* _indexInCollection_ Index of the object in the collection
|
||||
* _lastInCollection_ True if this is the last object
|
||||
* _collectionLength_ Length of the collection
|
||||
|
||||
For documentation on altering the object name view [res.partial()](http://expressjs.com/guide.html#res-partial-view-options-).
|
||||
|
||||
__NOTE:__ be careful about when you use partial collections, as rendering an array with a length of 100 means we have to render 100 views. For simple collections you may inline the iteration instead of using partial collection support to decrease overhead.
|
||||
|
||||
### View Lookup
|
||||
|
||||
View lookup is performed relative to the parent view, for example if we had a page view named _views/user/list.jade_, and within that view we did _partial('edit')_ it would attempt to load _views/user/edit.jade_, whereas _partial('../messages')_ would load _views/messages.jade_.
|
||||
|
||||
The view system also allows for index templates, allowing you to have a directory of the same name. For example within a route we may have _res.render('users')_ either _views/users.jade_, or _views/users/index.jade_.
|
||||
|
||||
When utilizing index views as shown above, we may reference _views/users/index.jade_ from a view in the same directory by _partial('users')_, and the view system will try _../users/index_, preventing us from needing to call _partial('index')_.
|
||||
|
||||
### Template Engines
|
||||
|
||||
Below are a few template engines commonly used with Express:
|
||||
|
||||
* [Haml](http://github.com/visionmedia/haml.js) haml implementation
|
||||
* [Jade](http://jade-lang.com) haml.js successor
|
||||
* [Haml](http://github.com/visionmedia/haml.js) pythonic indented templates
|
||||
* [EJS](http://github.com/visionmedia/ejs) Embedded JavaScript
|
||||
* [CoffeeKup](http://github.com/mauricemach/coffeekup) CoffeeScript based templating
|
||||
* [jQuery Templates](https://github.com/kof/node-jqtpl) for node
|
||||
|
||||
### Session Support
|
||||
|
||||
Sessions support can be added by using Connect's _session_ middleware. To do so we also need the _cookieParser_ middleware place above it, which will parse and populate cookie data to _req.cookies_.
|
||||
Sessions support can be added by using Connect's _session_ middleware. To do so we also need the _cookieDecoder_ middleware place above it, which will parse and populate cookie data to _req.cookies_.
|
||||
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.session({ secret: "keyboard cat" }));
|
||||
app.use(express.cookieDecoder());
|
||||
app.use(express.session());
|
||||
|
||||
By default the _session_ middleware uses the memory store bundled with Connect, however many implementations exist. For example [connect-redis](http://github.com/visionmedia/connect-redis) supplies a [Redis](http://code.google.com/p/redis/) session store and can be used as shown below:
|
||||
|
||||
var RedisStore = require('connect-redis');
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.session({ secret: "keyboard cat", store: new RedisStore }));
|
||||
app.use(express.cookieDecoder());
|
||||
app.use(express.session({ store: new RedisStore }));
|
||||
|
||||
Now the _req.session_ and _req.sessionStore_ properties will be accessible to all routes and subsequent middleware. Properties on _req.session_ are automatically saved on a response, so for example if we wish to shopping cart data:
|
||||
|
||||
var RedisStore = require('connect-redis');
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.session({ secret: "keyboard cat", store: new RedisStore }));
|
||||
app.use(express.bodyDecoder());
|
||||
app.use(express.cookieDecoder());
|
||||
app.use(express.session({ store: new RedisStore }));
|
||||
|
||||
app.post('/add-to-cart', function(req, res){
|
||||
// Perhaps we posted several items with a form
|
||||
// (use the bodyParser() middleware for this)
|
||||
// (use the bodyDecoder() middleware for this)
|
||||
var items = req.body.items;
|
||||
req.session.items = items;
|
||||
res.redirect('back');
|
||||
@@ -601,11 +557,11 @@ Now the _req.session_ and _req.sessionStore_ properties will be accessible to al
|
||||
res.render('shopping-cart');
|
||||
});
|
||||
|
||||
The _req.session_ object also has methods such as _Session#touch()_, _Session#destroy()_, _Session#regenerate()_ among others to maintain and manipulate sessions. For more information view the [Connect Session](http://senchalabs.github.com/connect/middleware-session.html) documentation.
|
||||
The _req.session_ object also has methods such as _Session#touch()_, _Session#destroy()_, _Session#regenerate()_ among others to maintain and manipulate sessions. For more information view the [Connect Session](http://senchalabs.github.com/connect/session.html) documentation.
|
||||
|
||||
### Migration Guide
|
||||
|
||||
Express 1.x developers may reference the [Migration Guide](migrate.html) to get up to speed on how to upgrade your application to work with Express 2.x, Connect 1.x, and Node 0.4.x.
|
||||
Pre-beta Express developers may reference the [Migration Guide](migrate.html) to get up to speed on how to upgrade your application.
|
||||
|
||||
### req.header(key[, defaultValue])
|
||||
|
||||
@@ -615,16 +571,6 @@ Get the case-insensitive request header _key_, with optional _defaultValue_:
|
||||
req.header('host');
|
||||
req.header('Accept', '*/*');
|
||||
|
||||
The _Referrer_ and _Referer_ header fields are special-cased, either will work:
|
||||
|
||||
// sent Referrer: http://google.com
|
||||
|
||||
req.header('Referer');
|
||||
// => "http://google.com"
|
||||
|
||||
req.header('Referrer');
|
||||
// => "http://google.com"
|
||||
|
||||
### req.accepts(type)
|
||||
|
||||
Check if the _Accept_ header is present, and includes the given _type_.
|
||||
@@ -698,17 +644,17 @@ We may also assert the _type_ as shown below, which would return true for _"appl
|
||||
|
||||
req.is('*/json');
|
||||
|
||||
### req.param(name[, default])
|
||||
### req.param(name)
|
||||
|
||||
Return the value of param _name_ when present or _default_.
|
||||
Return the value of param _name_ when present.
|
||||
|
||||
- Checks route params (_req.params_), ex: /user/:id
|
||||
- Checks route placeholders (_req.params_), ex: /user/:id
|
||||
- Checks query string params (_req.query_), ex: ?id=12
|
||||
- Checks urlencoded body params (_req.body_), ex: id=12
|
||||
|
||||
To utilize urlencoded request bodies, _req.body_
|
||||
should be an object. This can be done by using
|
||||
the _express.bodyParser middleware.
|
||||
the _express.bodyDecoder_ middleware.
|
||||
|
||||
### req.flash(type[, msg])
|
||||
|
||||
@@ -747,45 +693,19 @@ Get or set the response header _key_.
|
||||
res.header('Content-Length');
|
||||
// => undefined
|
||||
|
||||
res.header('Content-Length', 123);
|
||||
res.header('Content-Length', 123);
|
||||
// => 123
|
||||
|
||||
|
||||
res.header('Content-Length');
|
||||
// => 123
|
||||
|
||||
### res.charset
|
||||
|
||||
Sets the charset for subsequent `Content-Type` header fields. For example `res.send()` and `res.render()` default to "utf8", so we may explicitly set the charset before rendering a template:
|
||||
|
||||
res.charset = 'ISO-8859-1';
|
||||
res.render('users');
|
||||
|
||||
or before responding with `res.send()`:
|
||||
|
||||
res.charset = 'ISO-8859-1';
|
||||
res.send(str);
|
||||
|
||||
or with node's `res.end()`:
|
||||
|
||||
res.charset = 'ISO-8859-1';
|
||||
res.header('Content-Type', 'text/plain');
|
||||
res.end(str);
|
||||
|
||||
### res.contentType(type)
|
||||
|
||||
Sets the _Content-Type_ response header to the given _type_.
|
||||
|
||||
var filename = 'path/to/image.png';
|
||||
res.contentType(filename);
|
||||
// Content-Type is now "image/png"
|
||||
|
||||
A literal _Content-Type_ works as well:
|
||||
|
||||
res.contentType('application/json');
|
||||
|
||||
Or simply the extension without leading `.`:
|
||||
|
||||
res.contentType('json');
|
||||
// res.headers['Content-Type'] is now "image/png"
|
||||
|
||||
### res.attachment([filename])
|
||||
|
||||
@@ -793,30 +713,25 @@ Sets the _Content-Disposition_ response header to "attachment", with optional _f
|
||||
|
||||
res.attachment('path/to/my/image.png');
|
||||
|
||||
### res.sendfile(path[, options[, callback]])
|
||||
### res.sendfile(path)
|
||||
|
||||
Used by `res.download()` to transfer an arbitrary file.
|
||||
|
||||
res.sendfile('path/to/my.file');
|
||||
|
||||
This method accepts an optional callback which is called when
|
||||
an error occurs, or when the transfer is complete. By default failures call `next(err)`, however when a callback is supplied you must do this explicitly, or act on the error.
|
||||
This method accepts a callback which when given will be called on an exception, as well as when the transfer has completed. When a callback is not given, and the file has __not__ been streamed, _next(err)_ will be called on an exception.
|
||||
|
||||
res.sendfile(path, function(err){
|
||||
res.sendfile(path, function(err, path){
|
||||
if (err) {
|
||||
next(err);
|
||||
// handle the error
|
||||
} else {
|
||||
console.log('transferred %s', path);
|
||||
}
|
||||
});
|
||||
|
||||
Options may also be passed to the internal _fs.createReadStream()_ call, for example altering the _bufferSize_:
|
||||
When the filesize exceeds the _stream threshold_ (defaulting to 32k), the file will be streamed using _fs.ReadStream_ and _sys.pump()_.
|
||||
|
||||
res.sendfile(path, { bufferSize: 1024 }, function(err){
|
||||
// handle
|
||||
});
|
||||
|
||||
### res.download(file[, filename[, callback]])
|
||||
### res.download(file[, filename])
|
||||
|
||||
Transfer the given _file_ as an attachment with optional alternative _filename_.
|
||||
|
||||
@@ -828,16 +743,11 @@ This is equivalent to:
|
||||
res.attachment(file);
|
||||
res.sendfile(file);
|
||||
|
||||
An optional callback may be supplied as either the second or third argument, which is passed to _res.sendfile()_:
|
||||
|
||||
res.download(path, 'expenses.doc', function(err){
|
||||
// handle
|
||||
});
|
||||
|
||||
### res.send(body|status[, headers|status[, status]])
|
||||
|
||||
The _res.send()_ method is a high level response utility allowing you to pass
|
||||
objects to respond with json, strings for html, Buffer instances, or numbers representing the status code. The following are all valid uses:
|
||||
The `res.send()` method is a high level response utility allowing you to pass
|
||||
objects to respond with json, strings for html, arbitrary _Buffer_s or numbers for status
|
||||
code based responses. The following are all valid uses:
|
||||
|
||||
res.send(); // 204
|
||||
res.send(new Buffer('wahoo'));
|
||||
@@ -851,8 +761,6 @@ By default the _Content-Type_ response header is set, however if explicitly
|
||||
assigned through `res.send()` or previously with `res.header()` or `res.contentType()`
|
||||
it will not be set again.
|
||||
|
||||
Note that this method _end()_s the response, so you will want to use node's _res.write()_ for multiple writes or streaming.
|
||||
|
||||
### res.redirect(url[, status])
|
||||
|
||||
Redirect to the given _url_ with a default response _status_ of 302.
|
||||
@@ -869,21 +777,17 @@ the "home" setting and defaults to "/".
|
||||
|
||||
### res.cookie(name, val[, options])
|
||||
|
||||
Sets the given cookie _name_ to _val_, with options _httpOnly_, _secure_, _expires_ etc.
|
||||
Sets the given cookie _name_ to _val_, with _options_ such as "httpOnly: true", "expires", "secure" etc.
|
||||
|
||||
// "Remember me" for 15 minutes
|
||||
res.cookie('rememberme', 'yes', { expires: new Date(Date.now() + 900000), httpOnly: true });
|
||||
|
||||
The _maxAge_ property may be used to set _expires_ relative to _Date.now()_ in milliseconds, so our example above can now become:
|
||||
To parse incoming _Cookie_ headers, use the _cookieDecoder_ middleware, which provides the _req.cookies_ object:
|
||||
|
||||
res.cookie('rememberme', 'yes', { maxAge: 900000 });
|
||||
|
||||
To parse incoming _Cookie_ headers, use the _cookieParser_ middleware, which provides the _req.cookies_ object:
|
||||
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.cookieDecoder());
|
||||
|
||||
app.get('/', function(req, res){
|
||||
// use req.cookies.rememberme
|
||||
// use req.cookies.rememberme
|
||||
});
|
||||
|
||||
### res.clearCookie(name)
|
||||
@@ -898,10 +802,14 @@ Render _view_ with the given _options_ and optional callback _fn_.
|
||||
When a callback function is given a response will _not_ be made
|
||||
automatically, however otherwise a response of _200_ and _text/html_ is given.
|
||||
|
||||
The _options_ passed are the local variables as well, for example if we want to expose "user" to the view, and prevent a local we do so within the same object:
|
||||
Most engines accept one or more of the following options,
|
||||
both [haml](http://github.com/visionmedia/haml.js) and [jade](http://github.com/visionmedia/jade) accept all:
|
||||
|
||||
var user = { name: 'tj' };
|
||||
res.render('index', { layout: false, user: user });
|
||||
- _scope_ Template evaluation context (value of _this_)
|
||||
- _locals_ Object containing local variables
|
||||
- _debug_ Output debugging information
|
||||
- _status_ Response status code, defaults to 200
|
||||
- _headers_ Response headers object
|
||||
|
||||
### res.partial(view[, options])
|
||||
|
||||
@@ -938,7 +846,7 @@ of _movie.director_ we could use _this.director_.
|
||||
partial('movie', { collection: movies, as: this });
|
||||
// In view: this.director
|
||||
|
||||
Another alternative is to "expand" the properties of the collection item into
|
||||
Another alternative is to "explode" the properties of the collection item into
|
||||
pseudo globals (local variables) by using _as: global_, which again is syntactic sugar:
|
||||
|
||||
partial('movie', { collection: movies, as: global });
|
||||
@@ -960,62 +868,9 @@ This same logic applies to a single partial object usage:
|
||||
|
||||
When a non-collection (does _not_ have _.length_) is passed as the second argument, it is assumed to be the _object_, after which the object's local variable name is derived from the view name:
|
||||
|
||||
var movie = new Movie('Nightmare Before Christmas', 'Tim Burton')
|
||||
partial('movie', movie)
|
||||
partial('movie', movie);
|
||||
// => In view: movie.director
|
||||
|
||||
The exception of this, is when a "plain" object, aka "{}" or "new Object" is passed, which is considered an object with local variable. For example some may expect a "movie" local with the following, however since it is a plain object "director" and "title" are simply locals:
|
||||
|
||||
var movie = { title: 'Nightmare Before Christmas', director: 'Tim Burton' };
|
||||
partial('movie', movie)
|
||||
|
||||
For cases like this where passing a plain object is desired, simply assign it to a key, or use the `object` key which will use the filename-derived variable name. The examples below are equivalent:
|
||||
|
||||
partial('movie', { locals: { movie: movie }})
|
||||
partial('movie', { movie: movie })
|
||||
partial('movie', { object: movie })
|
||||
|
||||
This exact API can be utilized from within a route, to respond with a fragment via Ajax or WebSockets, for example we can render a collection of users directly from a route:
|
||||
|
||||
app.get('/users', function(req, res){
|
||||
if (req.xhr) {
|
||||
// respond with the each user in the collection
|
||||
// passed to the "user" view
|
||||
res.partial('user', users);
|
||||
} else {
|
||||
// respond with layout, and users page
|
||||
// which internally does partial('user', users)
|
||||
// along with other UI
|
||||
res.render('users', { users: users });
|
||||
}
|
||||
});
|
||||
|
||||
### res.local(name[, val])
|
||||
|
||||
Get or set the given local variable _name_. The locals built up for a response are applied to those given to the view rendering methods such as `res.render()`.
|
||||
|
||||
app.all('/movie/:id', function(req, res, next){
|
||||
Movie.get(req.params.id, function(err, movie){
|
||||
// Assigns res.locals.movie = movie
|
||||
res.local('movie', movie);
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/movie/:id', function(req, res){
|
||||
// movie is already a local, however we
|
||||
// can pass more if we wish
|
||||
res.render('movie', { displayReviews: true });
|
||||
});
|
||||
|
||||
### res.locals(obj)
|
||||
|
||||
Assign several locals with the given _obj_. The following are equivalent:
|
||||
|
||||
res.local('foo', bar);
|
||||
res.local('bar', baz);
|
||||
|
||||
res.locals({ foo: bar, bar, baz });
|
||||
|
||||
### app.set(name[, val])
|
||||
|
||||
Apply an application level setting _name_ to _val_, or
|
||||
@@ -1038,20 +893,6 @@ Enable the given setting _name_:
|
||||
app.set('some arbitrary setting');
|
||||
// => true
|
||||
|
||||
app.enabled('some arbitrary setting');
|
||||
// => true
|
||||
|
||||
### app.enabled(name)
|
||||
|
||||
Check if setting _name_ is enabled:
|
||||
|
||||
app.enabled('view cache');
|
||||
// => false
|
||||
|
||||
app.enable('view cache');
|
||||
app.enabled('view cache');
|
||||
// => true
|
||||
|
||||
### app.disable(name)
|
||||
|
||||
Disable the given setting _name_:
|
||||
@@ -1059,22 +900,6 @@ Disable the given setting _name_:
|
||||
app.disable('some setting');
|
||||
app.set('some setting');
|
||||
// => false
|
||||
|
||||
app.disabled('some setting');
|
||||
// => false
|
||||
|
||||
### app.disabled(name)
|
||||
|
||||
Check if setting _name_ is disabled:
|
||||
|
||||
app.enable('view cache');
|
||||
|
||||
app.disabled('view cache');
|
||||
// => false
|
||||
|
||||
app.disable('view cache');
|
||||
app.disabled('view cache');
|
||||
// => true
|
||||
|
||||
### app.configure(env|function[, function])
|
||||
|
||||
@@ -1090,7 +915,7 @@ Define a callback function for the given _env_ (or all environments) with callba
|
||||
|
||||
### app.redirect(name, val)
|
||||
|
||||
For use with _res.redirect()_ we can map redirects at the application level as shown below:
|
||||
For use with `res.redirect()` we can map redirects at the application level as shown below:
|
||||
|
||||
app.redirect('google', 'http://google.com');
|
||||
|
||||
@@ -1101,7 +926,7 @@ Now in a route we may call:
|
||||
We may also map dynamic redirects:
|
||||
|
||||
app.redirect('comments', function(req, res){
|
||||
return '/post/' + req.params.id + '/comments';
|
||||
return '/post/' + req.params.id + '/comments';
|
||||
});
|
||||
|
||||
So now we may do the following, and the redirect will dynamically adjust to
|
||||
@@ -1109,13 +934,9 @@ the context of the request. If we called this route with _GET /post/12_ our
|
||||
redirect _Location_ would be _/post/12/comments_.
|
||||
|
||||
app.get('/post/:id', function(req, res){
|
||||
res.redirect('comments');
|
||||
res.redirect('comments');
|
||||
});
|
||||
|
||||
When mounted, _res.redirect()_ will respect the mount-point. For example if a blog app is mounted at _/blog_, the following will redirect to _/blog/posts_:
|
||||
|
||||
res.redirect('/posts');
|
||||
|
||||
### app.error(function)
|
||||
|
||||
Adds an error handler _function_ which will receive the exception as the first parameter as shown below.
|
||||
@@ -1123,34 +944,24 @@ Note that we may set several error handlers by making several calls to this meth
|
||||
should call _next(err)_ if it does not wish to deal with the exception:
|
||||
|
||||
app.error(function(err, req, res, next){
|
||||
res.send(err.message, 500);
|
||||
});
|
||||
res.send(err.message, 500);
|
||||
});
|
||||
|
||||
### app.helpers(obj)
|
||||
|
||||
Registers static view helpers.
|
||||
|
||||
app.helpers({
|
||||
name: function(first, last){ return first + ', ' + last }
|
||||
, firstName: 'tj'
|
||||
, lastName: 'holowaychuk'
|
||||
});
|
||||
name: function(first, last){ return first + ', ' + last },
|
||||
firstName: 'tj',
|
||||
lastName: 'holowaychuk'
|
||||
});
|
||||
|
||||
Our view could now utilize the _firstName_ and _lastName_ variables,
|
||||
as well as the _name()_ function exposed.
|
||||
|
||||
<%= name(firstName, lastName) %>
|
||||
|
||||
Express also provides a few locals by default:
|
||||
|
||||
- `settings` the app's settings object
|
||||
- `filename` the view's filename
|
||||
- `request` the request object
|
||||
- `response` the response object
|
||||
- `app` the application itself
|
||||
|
||||
This method is aliased as _app.locals()_.
|
||||
|
||||
### app.dynamicHelpers(obj)
|
||||
|
||||
Registers dynamic view helpers. Dynamic view helpers
|
||||
@@ -1159,9 +970,9 @@ evaluated against the _Server_ instance before a view is rendered. The _return v
|
||||
becomes the local variable it is associated with.
|
||||
|
||||
app.dynamicHelpers({
|
||||
session: function(req, res){
|
||||
return req.session;
|
||||
}
|
||||
session: function(req, res){
|
||||
return req.session;
|
||||
}
|
||||
});
|
||||
|
||||
All views would now have _session_ available so that session data can be accessed via _session.name_ etc:
|
||||
@@ -1176,8 +987,8 @@ Assign a callback _fn_ which is called when this _Server_ is passed to _Server#u
|
||||
blog = express.createServer();
|
||||
|
||||
blog.mounted(function(parent){
|
||||
// parent is app
|
||||
// "this" is blog
|
||||
// parent is app
|
||||
// "this" is blog
|
||||
});
|
||||
|
||||
app.use(blog);
|
||||
@@ -1198,21 +1009,14 @@ of layout.hamljs, we can register the engine as ".haml":
|
||||
app.register('.haml', require('haml-js'));
|
||||
|
||||
For engines that do not comply with the Express
|
||||
specification, we can also wrap their api this way. Below
|
||||
we map _.md_ to render markdown files, rendering the html once
|
||||
since it will not change on subsequent calls, and support local substitution
|
||||
in the form of "{name}".
|
||||
specification, we can also wrap their api this way.
|
||||
|
||||
app.register('.md', {
|
||||
compile: function(str, options){
|
||||
var html = md.toHTML(str);
|
||||
return function(locals){
|
||||
return html.replace(/\{([^}]+)\}/g, function(_, name){
|
||||
return locals[name];
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
app.register('.foo', {
|
||||
render: function(str, options) {
|
||||
// perhaps their api is
|
||||
// return foo.toHTML(str, options);
|
||||
}
|
||||
});
|
||||
|
||||
### app.listen([port[, host]])
|
||||
|
||||
|
||||
106
docs/index.1
Normal file
106
docs/index.1
Normal file
@@ -0,0 +1,106 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "INDEX" "" "October 2010" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBindex\fR
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var app = express\.createServer();
|
||||
|
||||
app\.get(\'/\', function(req, res){
|
||||
res\.send(\'Hello World\');
|
||||
});
|
||||
|
||||
app\.listen(3000);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "Features"
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Robust routing
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Redirection helpers
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Dynamic view helpers
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Application level view options
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Content negotiation
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Focus on high performance
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View rendering and partials support
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Environment based configuration
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Session based flash notifications
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Built on Connect \fIhttp://github\.com/senchalabs/connect\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Executable \fIexecutable\.html\fR for generating applications quickly
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
High test coverage
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "Contributors"
|
||||
The following are the major contributors of Express (in no specific order)\.
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
TJ Holowaychuk (visionmedia \fIhttp://github\.com/visionmedia\fR)
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Ciaran Jessup (ciaranj \fIhttp://github\.com/ciaranj\fR)
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Aaron Heckmann (aheckmann \fIhttp://github\.com/aheckmann\fR)
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Guillermo Rauch (guille \fIhttp://github\.com/guille\fR)
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "More Information"
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Google Group \fIhttp://groups\.google\.com/group/express\-js\fR for discussion
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Follow tjholowaychuk \fIhttp://twitter\.com/tjholowaychuk\fR on twitter for updates
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Annotated source documentation \fIapi\.html\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the Connect \fIhttp://github\.com/senchalabs/connect\fR repo for middleware usage
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the Connect Wiki \fIhttp://wiki\.github\.com/senchalabs/connect/\fR for contrib middleware
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the examples \fIhttp://github\.com/visionmedia/express/tree/master/examples/\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the source \fIhttp://github\.com/visionmedia/express\fR
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 75px;
|
||||
margin-left: 65px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
@@ -187,9 +187,14 @@
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="screencasts.html">Screencasts</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="applications.html">Applications</a></li>
|
||||
</ul>
|
||||
<div class='mp'>
|
||||
<h2 id="Express">Express</h2>
|
||||
<p class="man-name">
|
||||
<code>index</code>
|
||||
</p>
|
||||
<pre><code>var app = express.createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
@@ -199,7 +204,7 @@ app.get('/', function(req, res){
|
||||
app.listen(3000);
|
||||
</code></pre>
|
||||
|
||||
<h2>Features</h2>
|
||||
<h2 id="Features">Features</h2>
|
||||
|
||||
<ul>
|
||||
<li>Robust routing</li>
|
||||
@@ -207,7 +212,6 @@ app.listen(3000);
|
||||
<li>Dynamic view helpers</li>
|
||||
<li>Application level view options</li>
|
||||
<li>Content negotiation</li>
|
||||
<li>Application mounting</li>
|
||||
<li>Focus on high performance</li>
|
||||
<li>View rendering and partials support</li>
|
||||
<li>Environment based configuration</li>
|
||||
@@ -218,7 +222,7 @@ app.listen(3000);
|
||||
</ul>
|
||||
|
||||
|
||||
<h2>Contributors</h2>
|
||||
<h2 id="Contributors">Contributors</h2>
|
||||
|
||||
<p>The following are the major contributors of Express (in no specific order).</p>
|
||||
|
||||
@@ -230,31 +234,21 @@ app.listen(3000);
|
||||
</ul>
|
||||
|
||||
|
||||
<h2>Third-Party Modules</h2>
|
||||
|
||||
<p>The following modules compliment or extend Express directly:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="http://github.com/visionmedia/express-resource">express-resource</a> provides resourceful routing</li>
|
||||
<li><a href="http://github.com/visionmedia/express-messages">express-messages</a> flash message notification rendering</li>
|
||||
<li><a href="http://github.com/visionmedia/express-configure">express-configure</a> async configuration support (load settings from redis etc)</li>
|
||||
<li><a href="http://github.com/visionmedia/express-namespace">express-namespace</a> namespaced routing support</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h2>More Information</h2>
|
||||
<h2 id="More-Information">More Information</h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="http://groups.google.com/group/express-js">Google Group</a> for discussion</li>
|
||||
<li>Follow <a href="http://twitter.com/tjholowaychuk">tjholowaychuk</a> on twitter for updates</li>
|
||||
<li>View the <a href="http://senchalabs.github.com/connect">Connect</a> documentation</li>
|
||||
<li>Annotated source <a href="api.html">documentation</a></li>
|
||||
<li>View the <a href="http://github.com/senchalabs/connect">Connect</a> repo for middleware usage</li>
|
||||
<li>View the <a href="http://wiki.github.com/senchalabs/connect/">Connect Wiki</a> for contrib middleware</li>
|
||||
<li>View the <a href="http://github.com/visionmedia/express/tree/master/examples/">examples</a></li>
|
||||
<li>View the <a href="http://github.com/visionmedia/express">source</a></li>
|
||||
<li>View the <a href="contrib.html">contrib guide</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* Dynamic view helpers
|
||||
* Application level view options
|
||||
* Content negotiation
|
||||
* Application mounting
|
||||
* Focus on high performance
|
||||
* View rendering and partials support
|
||||
* Environment based configuration
|
||||
@@ -32,21 +31,12 @@ The following are the major contributors of Express (in no specific order).
|
||||
* Aaron Heckmann ([aheckmann](http://github.com/aheckmann))
|
||||
* Guillermo Rauch ([guille](http://github.com/guille))
|
||||
|
||||
## Third-Party Modules
|
||||
|
||||
The following modules compliment or extend Express directly:
|
||||
|
||||
* [express-resource](http://github.com/visionmedia/express-resource) provides resourceful routing
|
||||
* [express-messages](http://github.com/visionmedia/express-messages) flash message notification rendering
|
||||
* [express-configure](http://github.com/visionmedia/express-configuration) async configuration support (load settings from redis etc)
|
||||
* [express-namespace](http://github.com/visionmedia/express-namespace) namespaced routing support
|
||||
|
||||
## More Information
|
||||
|
||||
* [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
|
||||
* View the [Connect](http://senchalabs.github.com/connect) documentation
|
||||
* Annotated source [documentation](api.html)
|
||||
* View the [Connect](http://github.com/senchalabs/connect) repo for middleware usage
|
||||
* View the [Connect Wiki](http://wiki.github.com/senchalabs/connect/) for contrib middleware
|
||||
* View the [examples](http://github.com/visionmedia/express/tree/master/examples/)
|
||||
* View the [source](http://github.com/visionmedia/express)
|
||||
* View the [contrib guide](contrib.html)
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 75px;
|
||||
margin-left: 65px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
@@ -187,6 +187,6 @@
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="screencasts.html">Screencasts</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="applications.html">Applications</a></li>
|
||||
</ul>
|
||||
|
||||
347
docs/migrate.1
Normal file
347
docs/migrate.1
Normal file
@@ -0,0 +1,347 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "MIGRATE" "" "October 2010" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBmigrate\fR
|
||||
.
|
||||
.SS "Built On Connect"
|
||||
Express 1\.x is written to run on\-top of the Connect \fIhttp://extjs\.github\.com/Connect\fR middlware framework, thus the \fIPlugin\fR has been replaced by Connect\'s middleware\. By abstracting our middleware to Connect we allow additional community frameworks to develop robust, high\-level frameworks using the same technologies as Express\.
|
||||
.
|
||||
.SS "Creating Applications"
|
||||
Previously due to legacy code implemented in the early days of node, Express unfortunately had some globals\. The DSL would previously be accessed as shown below:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
require(\'express\');
|
||||
|
||||
configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
get(\'/\', function(){
|
||||
return \'hello world\';
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Now we utilize the CommonJS module system appropriately, and introduce \fIexpress\.createServer()\fR which accepts the same arguments as \fIhttp\.createServer()\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var express = require(\'express\'),
|
||||
app = express\.createServer();
|
||||
|
||||
app\.configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
app\.get(\'/\', function(req, res){
|
||||
res\.send(\'hello world\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Express 1\.x does \fInot\fR currently allow returning of a string\.
|
||||
.
|
||||
.SS "Plugins vs Middleware"
|
||||
Previously Express was bundled with plugins, which were essentially what are now Connect middleware\. Previously plugins would be utilized in a manor similar to below:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
use(Logger);
|
||||
use(MethodOverride);
|
||||
use(Cookie);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Which we can now \fIuse()\fR within our app, or pass to the \fIexpress\.createServer()\fR method:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var app = express\.createServer(
|
||||
express\.logger(),
|
||||
express\.methodOverride(),
|
||||
express\.cookieDecoder()
|
||||
);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
or:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var app = express\.createServer();
|
||||
|
||||
app\.use(express\.logger());
|
||||
app\.use(express\.methodOverride());
|
||||
app\.use(express\.cookieDecoder());
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
For documentation on creating Connect middleware visit Middleware Authoring \fIhttp://extjs\.github\.com/Connect/#Middleware\-Authoring\fR\.
|
||||
.
|
||||
.SS "Running Applications"
|
||||
Previously a global function \fIrun()\fR, was available:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
run();
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
The new \fIexpress\.Server\fR has the same API as \fIhttp\.Server\fR, so we can do things like:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.listen();
|
||||
app\.listen(3000);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Route Parameters"
|
||||
Previously we could use \fIthis\.param()\fR to attempt fetching a route, query string, or request body parameter:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
get(\'/user/:id\', function(){
|
||||
this\.param(\'id\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Polymorphic parameter access can be done using \fBreq\.param()\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/user/:id\', function(req, res){
|
||||
req\.param(\'id\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Route parameters are available via \fBreq\.params\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/user/:id\', function(req, res){
|
||||
req\.params\.id;
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Passing Route Control"
|
||||
Old express had a weak notion of route passing, which did not support async, and was never properly implemented for practical use:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
get(\'/\', function(){
|
||||
this\.pass(\'/foobar\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Now Express has access to Connect\'s \fInext()\fR function, which is passed as the third and final argument\. Calling \fInext()\fR will pass control to the next \fImatching route\fR, or continue down the stack of Connect middleware\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/user/:id?\', function(req, res, next){
|
||||
next();
|
||||
});
|
||||
|
||||
app\.get(\'/user\', function(){
|
||||
// \.\.\. respond
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "View Rendering"
|
||||
View filenames no longer take the form \fINAME\fR\.\fITYPE\fR\.\fIENGINE\fR, the \fIContent\-Type\fR can be set via \fIres\.contentType()\fR or \fIres\.header()\fR\. For example what was previously \fIlayout\.html\.haml\fR, should now be \fIlayout\.haml\fR\.
|
||||
.
|
||||
.P
|
||||
Previously a view render looked something like this:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
get(\'/\', function(){
|
||||
this\.render(\'index\.html\.haml\', {
|
||||
locals: { title: \'My Site\' }
|
||||
});
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
We now have \fIres\.render()\fR, however the options passed to haml \fIhttp://github\.com/visionmedia/haml\.js\fR, jade \fIhttp://github\.com/visionmedia/jade\fR, and others remain the same\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/\', function(req, res){
|
||||
res\.render(\'index\.haml\', {
|
||||
locals: { title: \'My Site\' }
|
||||
});
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Previously rendering of a collection via \fIpartial()\fR would look something like this:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
this\.partial(\'comment\.html\.haml\', { collection: comments });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Although this worked just fine, it was generally to verbose, the similar but new API looks like this, as \fIpartial()\fR is \fIalways\fR passed as a local variable:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comment\.haml\', { collection: comments });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
To make things even less verbose we can assume the extension when omitted:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comment\', { collection: comments });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
And once again even further, when rendering a collection we can simply pass an array, if no other options are desired:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comments\', comments);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Redirecting"
|
||||
Previously you would
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
this\.redirect(\'/somewhere\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
However you would now:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.redirect(\'/somewhere\');
|
||||
res\.redirect(\'/somewhere\', 301);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "HTTP Client"
|
||||
Previously Express provided a high level http client, this library is no more as it does not belong in Express, however it may be resurrected as a separate module\.
|
||||
.
|
||||
.SS "Core Extensions"
|
||||
Express is no longer dependent on the JavaScript Extensions \fIhttp://github\.com/visionmedia/ext\.js\fR library, so those of you using the methods provided by it such as \fBObject\.merge(a, b)\fR will need to roll your own, or install the module via:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ npm install ext
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 75px;
|
||||
margin-left: 65px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
@@ -187,207 +187,226 @@
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="screencasts.html">Screencasts</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="applications.html">Applications</a></li>
|
||||
</ul>
|
||||
<h3>Express 1.x to 2.x Migration</h3>
|
||||
<div class='mp'>
|
||||
<h2 id="Express">Express</h2>
|
||||
<p class="man-name">
|
||||
<code>migrate</code>
|
||||
</p>
|
||||
<h3 id="Built-On-Connect">Built On Connect</h3>
|
||||
|
||||
<h3>HTTPS</h3>
|
||||
<p>Express 1.x is written to run on-top of the <a href="http://extjs.github.com/Connect">Connect</a> middlware
|
||||
framework, thus the <em>Plugin</em> has been replaced by Connect's middleware. By abstracting our middleware
|
||||
to Connect we allow additional community frameworks to develop robust, high-level frameworks using
|
||||
the same technologies as Express.</p>
|
||||
|
||||
<p> Creating an HTTPS server is simply, simply pass the TLS options to <em>express.createServer()</em>:</p>
|
||||
<h3 id="Creating-Applications">Creating Applications</h3>
|
||||
|
||||
<pre><code> var app = express.createServer({
|
||||
key: ...
|
||||
, cert: ...
|
||||
});
|
||||
<p>Previously due to legacy code implemented in the early days of node,
|
||||
Express unfortunately had some globals. The DSL would previously be
|
||||
accessed as shown below:</p>
|
||||
|
||||
app.listen(443);
|
||||
<pre><code>require('express');
|
||||
|
||||
configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
get('/', function(){
|
||||
return 'hello world';
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<h3>req.header() Referrer</h3>
|
||||
<p>Now we utilize the CommonJS module system appropriately, and
|
||||
introduce <em>express.createServer()</em> which accepts the same arguments
|
||||
as <em>http.createServer()</em>:</p>
|
||||
|
||||
<p> Previously if anyone was doing something similar to:</p>
|
||||
<pre><code>var express = require('express'),
|
||||
app = express.createServer();
|
||||
|
||||
<pre><code> req.headers.referrer || req.headers.referer
|
||||
req.header('Referrer') || req.header('Referer')
|
||||
app.configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('hello world');
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p> With the new special-case we may now simply use <em>Referrer</em> which will return either if defined:</p>
|
||||
<p>Express 1.x does <em>not</em> currently allow returning of a string.</p>
|
||||
|
||||
<pre><code> req.header('Referrer')
|
||||
<h3 id="Plugins-vs-Middleware">Plugins vs Middleware</h3>
|
||||
|
||||
<p>Previously Express was bundled with plugins, which were essentially what
|
||||
are now Connect middleware. Previously plugins would be utilized in a manor
|
||||
similar to below:</p>
|
||||
|
||||
<pre><code>use(Logger);
|
||||
use(MethodOverride);
|
||||
use(Cookie);
|
||||
</code></pre>
|
||||
|
||||
<h3>res.local(name, val)</h3>
|
||||
<p>Which we can now <em>use()</em> within our app, or pass to the <em>express.createServer()</em> method:</p>
|
||||
|
||||
<p> Previously all local variables had to be passed to <em>res.render()</em>, or either <em>app.helpers()</em> or <em>app.dynamicHelpers()</em>, now we may do this at the request-level progressively. The <em>res.local()</em> method accepts a <em>name</em> and <em>val</em>, however the locals passed to <em>res.render()</em> will take precedence.</p>
|
||||
|
||||
<p> For example we may utilize this feature to create locals in middleware:</p>
|
||||
|
||||
<pre><code> function loadUser(req, res, next) {
|
||||
User.get(req.params.id, function(err, user){
|
||||
res.local('user', user);
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
app.get('/user/:id', loadUser, function(req, res){
|
||||
res.render('user');
|
||||
});
|
||||
<pre><code>var app = express.createServer(
|
||||
express.logger(),
|
||||
express.methodOverride(),
|
||||
express.cookieDecoder()
|
||||
);
|
||||
</code></pre>
|
||||
|
||||
<h3>req.param(name[, defaultValue])</h3>
|
||||
<p>or:</p>
|
||||
|
||||
<p> Previously only <em>name</em> was accepted, so some of you may have been doing the following:</p>
|
||||
<pre><code>var app = express.createServer();
|
||||
|
||||
<pre><code> var id = req.param('id') || req.user.id;
|
||||
app.use(express.logger());
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.cookieDecoder());
|
||||
</code></pre>
|
||||
|
||||
<p> The new <em>defaultValue</em> argument can handle this nicely:</p>
|
||||
<p>For documentation on creating Connect middleware visit <a href="http://extjs.github.com/Connect/#Middleware-Authoring">Middleware Authoring</a>.</p>
|
||||
|
||||
<pre><code> var id = req.param('id', req.user.id);
|
||||
<h3 id="Running-Applications">Running Applications</h3>
|
||||
|
||||
<p>Previously a global function <em>run()</em>, was available:</p>
|
||||
|
||||
<pre><code>run();
|
||||
</code></pre>
|
||||
|
||||
<h3>app.helpers() / app.locals()</h3>
|
||||
<p>The new <em>express.Server</em> has the same API as <em>http.Server</em>,
|
||||
so we can do things like:</p>
|
||||
|
||||
<p> <em>app.locals()</em> is now an alias of <em>app.helpers()</em>, as helpers makes more sense for functions.</p>
|
||||
|
||||
<h3>req.accepts(type)</h3>
|
||||
|
||||
<p> <em>req.accepts()</em> now accepts extensions:</p>
|
||||
|
||||
<pre><code> // Accept: text/html
|
||||
req.accepts('html');
|
||||
req.accepts('.html');
|
||||
// => true
|
||||
|
||||
// Accept: text/*; application/json
|
||||
req.accepts('html');
|
||||
req.accepts('text/*');
|
||||
req.accepts('text/plain');
|
||||
req.accepts('application/json');
|
||||
// => true
|
||||
|
||||
req.accepts('image/png');
|
||||
req.accepts('png');
|
||||
// => false
|
||||
<pre><code>app.listen();
|
||||
app.listen(3000);
|
||||
</code></pre>
|
||||
|
||||
<h3>res.cookie()</h3>
|
||||
<h3 id="Route-Parameters">Route Parameters</h3>
|
||||
|
||||
<p> Previously only directly values could be passed, so for example:</p>
|
||||
<p>Previously we could use <em>this.param()</em> to attempt
|
||||
fetching a route, query string, or request body parameter:</p>
|
||||
|
||||
<pre><code>res.cookie('rememberme', 'yes', { expires: new Date(Date.now() + 900000) });
|
||||
<pre><code>get('/user/:id', function(){
|
||||
this.param('id');
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>However now we have the alternative <em>maxAge</em> property which may be used to set <em>expires</em> relative to <em>Date.now()</em> in milliseconds, so our example above can now become:</p>
|
||||
<p>Polymorphic parameter access can be done using <code>req.param()</code>:</p>
|
||||
|
||||
<pre><code>res.cookie('rememberme', 'yes', { maxAge: 900000 });
|
||||
<pre><code>app.get('/user/:id', function(req, res){
|
||||
req.param('id');
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<h3>res.download() / res.sendfile()</h3>
|
||||
<p>Route parameters are available via <code>req.params</code>:</p>
|
||||
|
||||
<p> Both of these methods now utilize Connect’s static file server behind the scenes (actually the previous Express code was ported to Connect 1.0). With this change comes a change to the callback as well. Previously the <em>path</em> and <em>stream</em> were passed, however now only an <em>error</em> is passed, when no error has occurred the callback will be invoked indicating that the file transfer is complete. The callback remains optional:</p>
|
||||
|
||||
<pre><code> res.download('/path/to/file');
|
||||
|
||||
res.download('/path/to/file', function(err){
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
console.log('transferred');
|
||||
}
|
||||
});
|
||||
<pre><code>app.get('/user/:id', function(req, res){
|
||||
req.params.id;
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p> The <em>stream threshold</em> setting was removed.</p>
|
||||
<h3 id="Passing-Route-Control">Passing Route Control</h3>
|
||||
|
||||
<h3>res.render()</h3>
|
||||
<p>Old express had a weak notion of route passing,
|
||||
which did not support async, and was never properly
|
||||
implemented for practical use:</p>
|
||||
|
||||
<p> Previously locals were passed as a separate key:</p>
|
||||
|
||||
<pre><code> res.render('user', { layout: false, locals: { user: user }});
|
||||
<pre><code>get('/', function(){
|
||||
this.pass('/foobar');
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p> In Express 2.0 both the locals and the options are one in the same, meaning you cannot have a local variable named <em>layout</em> as it is reserved for express, however this cleans up the API:</p>
|
||||
<p>Now Express has access to Connect's <em>next()</em> function,
|
||||
which is passed as the third and final argument. Calling <em>next()</em> will
|
||||
pass control to the next <em>matching route</em>, or continue down the stack
|
||||
of Connect middleware.</p>
|
||||
|
||||
<pre><code> res.render('user', { layout: false, user: user });
|
||||
<pre><code>app.get('/user/:id?', function(req, res, next){
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/user', function(){
|
||||
// ... respond
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<h3>res.partial()</h3>
|
||||
<h3 id="View-Rendering">View Rendering</h3>
|
||||
|
||||
<p> Express 2.0 adds the <em>res.partial()</em> method, helpful for rendering partial fragments over WebSockets or Ajax requests etc. The API is identical to the <em>partial()</em> calls within views.</p>
|
||||
<p>View filenames no longer take the form <em>Express</em>.<em>TYPE</em>.<em>ENGINE</em>,
|
||||
the <em>Content-Type</em> can be set via <em>res.contentType()</em> or
|
||||
<em>res.header()</em>. For example what was previously <em>layout.html.haml</em>,
|
||||
should now be <em>layout.haml</em>.</p>
|
||||
|
||||
<pre><code> // render a collection of comments
|
||||
res.partial('comment', [comment1, comment2]);
|
||||
<p>Previously a view render looked something like this:</p>
|
||||
|
||||
// render a single comment
|
||||
res.partial('comment', comment);
|
||||
<pre><code>get('/', function(){
|
||||
this.render('index.html.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<h3>partial() locals</h3>
|
||||
<p>We now have <em>res.render()</em>, however the options passed to <a href="http://github.com/visionmedia/haml.js">haml</a>, <a href="http://github.com/visionmedia/jade">jade</a>, and others
|
||||
remain the same.</p>
|
||||
|
||||
<p> Both <em>res.partial()</em> and the <em>partial()</em> functions accept an single object consisting of both the options and the locals. Previously with Express 1.x you may pass <em>user</em> to a partial, along with <em>date</em> like so:</p>
|
||||
|
||||
<pre><code> partial('user', { object: user, locals: { date: new Date }})
|
||||
<pre><code>app.get('/', function(req, res){
|
||||
res.render('index.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>or perhaps if you preferred not to use the inferred name <em>user</em> you may used a local for this as well:</p>
|
||||
<p>Previously rendering of a collection via <em>partial()</em> would look something like this:</p>
|
||||
|
||||
<pre><code> partial('user', { locals: { user: user, date: new Date }})
|
||||
<pre><code>this.partial('comment.html.haml', { collection: comments });
|
||||
</code></pre>
|
||||
|
||||
<p> With recent changes to Express 2.x the object passed is now both, so the following is valid for the <em>object</em> option and locals:</p>
|
||||
<p>Although this worked just fine, it was generally to verbose, the similar but new API
|
||||
looks like this, as <em>partial()</em> is <em>always</em> passed as a local variable:</p>
|
||||
|
||||
<pre><code> partial('user', { object: user, date: new Date })
|
||||
<pre><code>partial('comment.haml', { collection: comments });
|
||||
</code></pre>
|
||||
|
||||
<p> Or the following which is equivalent, however the local var name is explicitly set to <em>user</em> instead of deduced from the filename.</p>
|
||||
<p>To make things even less verbose we can assume the extension when omitted:</p>
|
||||
|
||||
<pre><code> partial('user', { user: user, date: new Date })
|
||||
<pre><code>partial('comment', { collection: comments });
|
||||
</code></pre>
|
||||
|
||||
<p> When a “basic” object aka <em>{}</em> or <em>new Object</em> is passed, it is considered options, otherwise it is considered the <em>object</em>. The following are equivalent:</p>
|
||||
<p>And once again even further, when rendering a collection we can simply pass
|
||||
an array, if no other options are desired:</p>
|
||||
|
||||
<pre><code> partial('user', user);
|
||||
partial('user', { object: user });
|
||||
<pre><code>partial('comments', comments);
|
||||
</code></pre>
|
||||
|
||||
<h3>Template Engine Compliance</h3>
|
||||
<h3 id="Redirecting">Redirecting</h3>
|
||||
|
||||
<p> To comply with Express previously engines needed the following signature:</p>
|
||||
<p>Previously you would</p>
|
||||
|
||||
<pre><code> engine.render(str, options, function(err){});
|
||||
<pre><code>this.redirect('/somewhere');
|
||||
</code></pre>
|
||||
|
||||
<p> Now they must export a <em>compile()</em> function, returning a function which when called with local variables will render the template. This allows Express to cache the compiled function in memory during production.</p>
|
||||
<p>However you would now:</p>
|
||||
|
||||
<pre><code> var fn = engine.compile(str, options);
|
||||
fn(locals);
|
||||
<pre><code>res.redirect('/somewhere');
|
||||
res.redirect('/somewhere', 301);
|
||||
</code></pre>
|
||||
|
||||
<h3>View Partial Lookup</h3>
|
||||
<h3 id="HTTP-Client">HTTP Client</h3>
|
||||
|
||||
<p> Previously partials were loaded relative to the now removed <em>view partials</em> directory setting, or by default <em>views/partials</em>, now they are relative to the view calling them, read more on <a href="guide.html#View-Lookup">view lookup</a>.</p>
|
||||
<p>Previously Express provided a high level http client, this library is no more
|
||||
as it does not belong in Express, however it may be resurrected as a separate module.</p>
|
||||
|
||||
<h3>Mime Types</h3>
|
||||
<h3 id="Core-Extensions">Core Extensions</h3>
|
||||
|
||||
<p> Express and Connect now utilize the <em>mime</em> module in npm, so to add more use:</p>
|
||||
<p>Express is no longer dependent on the <a href="http://github.com/visionmedia/ext.js">JavaScript Extensions</a> library, so those of you using the methods provided by it such as <code>Object.merge(a, b)</code> will need to
|
||||
roll your own, or install the module via:</p>
|
||||
|
||||
<pre><code> require('mime').define({ 'foo/bar': ['foo', 'bar'] });
|
||||
<pre><code>$ npm install ext
|
||||
</code></pre>
|
||||
|
||||
<h3>static() middleware</h3>
|
||||
|
||||
<p> Previously named <code>staticProvider()</code>, the now <code>static()</code> middleware takes a single directory path, followed by options.</p>
|
||||
|
||||
<pre><code> app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
|
||||
</code></pre>
|
||||
|
||||
<p>Previously when using options the <code>root</code> option would be used for this:</p>
|
||||
|
||||
<pre><code> app.use(express.staticProvider({ root: __dirname + '/public', maxAge: oneYear }));
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
262
docs/migrate.md
262
docs/migrate.md
@@ -1,177 +1,189 @@
|
||||
|
||||
### Express 1.x to 2.x Migration
|
||||
### Built On Connect
|
||||
|
||||
### HTTPS
|
||||
Express 1.x is written to run on-top of the [Connect](http://extjs.github.com/Connect) middlware
|
||||
framework, thus the _Plugin_ has been replaced by Connect's middleware. By abstracting our middleware
|
||||
to Connect we allow additional community frameworks to develop robust, high-level frameworks using
|
||||
the same technologies as Express.
|
||||
|
||||
Creating an HTTPS server is simply, simply pass the TLS options to _express.createServer()_:
|
||||
|
||||
var app = express.createServer({
|
||||
key: ...
|
||||
, cert: ...
|
||||
});
|
||||
### Creating Applications
|
||||
|
||||
app.listen(443);
|
||||
Previously due to legacy code implemented in the early days of node,
|
||||
Express unfortunately had some globals. The DSL would previously be
|
||||
accessed as shown below:
|
||||
|
||||
### req.header() Referrer
|
||||
require('express');
|
||||
|
||||
Previously if anyone was doing something similar to:
|
||||
|
||||
req.headers.referrer || req.headers.referer
|
||||
req.header('Referrer') || req.header('Referer')
|
||||
configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
With the new special-case we may now simply use _Referrer_ which will return either if defined:
|
||||
|
||||
req.header('Referrer')
|
||||
get('/', function(){
|
||||
return 'hello world';
|
||||
});
|
||||
|
||||
### res.local(name, val)
|
||||
Now we utilize the CommonJS module system appropriately, and
|
||||
introduce _express.createServer()_ which accepts the same arguments
|
||||
as _http.createServer()_:
|
||||
|
||||
Previously all local variables had to be passed to _res.render()_, or either _app.helpers()_ or _app.dynamicHelpers()_, now we may do this at the request-level progressively. The _res.local()_ method accepts a _name_ and _val_, however the locals passed to _res.render()_ will take precedence.
|
||||
var express = require('express'),
|
||||
app = express.createServer();
|
||||
|
||||
app.configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('hello world');
|
||||
});
|
||||
|
||||
For example we may utilize this feature to create locals in middleware:
|
||||
Express 1.x does _not_ currently allow returning of a string.
|
||||
|
||||
function loadUser(req, res, next) {
|
||||
User.get(req.params.id, function(err, user){
|
||||
res.local('user', user);
|
||||
next();
|
||||
});
|
||||
}
|
||||
### Plugins vs Middleware
|
||||
|
||||
app.get('/user/:id', loadUser, function(req, res){
|
||||
res.render('user');
|
||||
});
|
||||
Previously Express was bundled with plugins, which were essentially what
|
||||
are now Connect middleware. Previously plugins would be utilized in a manor
|
||||
similar to below:
|
||||
|
||||
### req.param(name[, defaultValue])
|
||||
use(Logger);
|
||||
use(MethodOverride);
|
||||
use(Cookie);
|
||||
|
||||
Previously only _name_ was accepted, so some of you may have been doing the following:
|
||||
|
||||
var id = req.param('id') || req.user.id;
|
||||
Which we can now _use()_ within our app, or pass to the _express.createServer()_ method:
|
||||
|
||||
The new _defaultValue_ argument can handle this nicely:
|
||||
|
||||
var id = req.param('id', req.user.id);
|
||||
var app = express.createServer(
|
||||
express.logger(),
|
||||
express.methodOverride(),
|
||||
express.cookieDecoder()
|
||||
);
|
||||
|
||||
### app.helpers() / app.locals()
|
||||
or:
|
||||
|
||||
_app.locals()_ is now an alias of _app.helpers()_, as helpers makes more sense for functions.
|
||||
var app = express.createServer();
|
||||
|
||||
### req.accepts(type)
|
||||
app.use(express.logger());
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.cookieDecoder());
|
||||
|
||||
_req.accepts()_ now accepts extensions:
|
||||
|
||||
|
||||
// Accept: text/html
|
||||
req.accepts('html');
|
||||
req.accepts('.html');
|
||||
// => true
|
||||
|
||||
// Accept: text/*; application/json
|
||||
req.accepts('html');
|
||||
req.accepts('text/*');
|
||||
req.accepts('text/plain');
|
||||
req.accepts('application/json');
|
||||
// => true
|
||||
|
||||
req.accepts('image/png');
|
||||
req.accepts('png');
|
||||
// => false
|
||||
For documentation on creating Connect middleware visit [Middleware Authoring](http://extjs.github.com/Connect/#Middleware-Authoring).
|
||||
|
||||
### res.cookie()
|
||||
### Running Applications
|
||||
|
||||
Previously only directly values could be passed, so for example:
|
||||
Previously a global function _run()_, was available:
|
||||
|
||||
res.cookie('rememberme', 'yes', { expires: new Date(Date.now() + 900000) });
|
||||
run();
|
||||
|
||||
However now we have the alternative _maxAge_ property which may be used to set _expires_ relative to _Date.now()_ in milliseconds, so our example above can now become:
|
||||
The new _express.Server_ has the same API as _http.Server_,
|
||||
so we can do things like:
|
||||
|
||||
res.cookie('rememberme', 'yes', { maxAge: 900000 });
|
||||
app.listen();
|
||||
app.listen(3000);
|
||||
|
||||
### res.download() / res.sendfile()
|
||||
### Route Parameters
|
||||
|
||||
Both of these methods now utilize Connect's static file server behind the scenes (actually the previous Express code was ported to Connect 1.0). With this change comes a change to the callback as well. Previously the _path_ and _stream_ were passed, however now only an _error_ is passed, when no error has occurred the callback will be invoked indicating that the file transfer is complete. The callback remains optional:
|
||||
|
||||
res.download('/path/to/file');
|
||||
Previously we could use _this.param()_ to attempt
|
||||
fetching a route, query string, or request body parameter:
|
||||
|
||||
res.download('/path/to/file', function(err){
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
console.log('transferred');
|
||||
}
|
||||
});
|
||||
get('/user/:id', function(){
|
||||
this.param('id');
|
||||
});
|
||||
|
||||
The _stream threshold_ setting was removed.
|
||||
Polymorphic parameter access can be done using `req.param()`:
|
||||
|
||||
### res.render()
|
||||
app.get('/user/:id', function(req, res){
|
||||
req.param('id');
|
||||
});
|
||||
|
||||
Previously locals were passed as a separate key:
|
||||
|
||||
res.render('user', { layout: false, locals: { user: user }});
|
||||
Route parameters are available via `req.params`:
|
||||
|
||||
In Express 2.0 both the locals and the options are one in the same, meaning you cannot have a local variable named _layout_ as it is reserved for express, however this cleans up the API:
|
||||
|
||||
res.render('user', { layout: false, user: user });
|
||||
app.get('/user/:id', function(req, res){
|
||||
req.params.id;
|
||||
});
|
||||
|
||||
### res.partial()
|
||||
### Passing Route Control
|
||||
|
||||
Express 2.0 adds the _res.partial()_ method, helpful for rendering partial fragments over WebSockets or Ajax requests etc. The API is identical to the _partial()_ calls within views.
|
||||
|
||||
// render a collection of comments
|
||||
res.partial('comment', [comment1, comment2]);
|
||||
Old express had a weak notion of route passing,
|
||||
which did not support async, and was never properly
|
||||
implemented for practical use:
|
||||
|
||||
// render a single comment
|
||||
res.partial('comment', comment);
|
||||
get('/', function(){
|
||||
this.pass('/foobar');
|
||||
});
|
||||
|
||||
### partial() locals
|
||||
Now Express has access to Connect's _next()_ function,
|
||||
which is passed as the third and final argument. Calling _next()_ will
|
||||
pass control to the next _matching route_, or continue down the stack
|
||||
of Connect middleware.
|
||||
|
||||
Both _res.partial()_ and the _partial()_ functions accept an single object consisting of both the options and the locals. Previously with Express 1.x you may pass _user_ to a partial, along with _date_ like so:
|
||||
|
||||
partial('user', { object: user, locals: { date: new Date }})
|
||||
app.get('/user/:id?', function(req, res, next){
|
||||
next();
|
||||
});
|
||||
|
||||
or perhaps if you preferred not to use the inferred name _user_ you may used a local for this as well:
|
||||
app.get('/user', function(){
|
||||
// ... respond
|
||||
});
|
||||
|
||||
partial('user', { locals: { user: user, date: new Date }})
|
||||
### View Rendering
|
||||
|
||||
With recent changes to Express 2.x the object passed is now both, so the following is valid for the _object_ option and locals:
|
||||
|
||||
partial('user', { object: user, date: new Date })
|
||||
View filenames no longer take the form _NAME_._TYPE_._ENGINE_,
|
||||
the _Content-Type_ can be set via _res.contentType()_ or
|
||||
_res.header()_. For example what was previously _layout.html.haml_,
|
||||
should now be _layout.haml_.
|
||||
|
||||
Or the following which is equivalent, however the local var name is explicitly set to _user_ instead of deduced from the filename.
|
||||
|
||||
partial('user', { user: user, date: new Date })
|
||||
Previously a view render looked something like this:
|
||||
|
||||
When a "basic" object aka _{}_ or _new Object_ is passed, it is considered options, otherwise it is considered the _object_. The following are equivalent:
|
||||
|
||||
partial('user', user);
|
||||
partial('user', { object: user });
|
||||
get('/', function(){
|
||||
this.render('index.html.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
|
||||
### Template Engine Compliance
|
||||
We now have _res.render()_, however the options passed to [haml](http://github.com/visionmedia/haml.js), [jade](http://github.com/visionmedia/jade), and others
|
||||
remain the same.
|
||||
|
||||
To comply with Express previously engines needed the following signature:
|
||||
|
||||
engine.render(str, options, function(err){});
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
|
||||
Now they must export a _compile()_ function, returning a function which when called with local variables will render the template. This allows Express to cache the compiled function in memory during production.
|
||||
|
||||
var fn = engine.compile(str, options);
|
||||
fn(locals);
|
||||
Previously rendering of a collection via _partial()_ would look something like this:
|
||||
|
||||
### View Partial Lookup
|
||||
this.partial('comment.html.haml', { collection: comments });
|
||||
|
||||
Previously partials were loaded relative to the now removed _view partials_ directory setting, or by default _views/partials_, now they are relative to the view calling them, read more on [view lookup](guide.html#View-Lookup).
|
||||
Although this worked just fine, it was generally to verbose, the similar but new API
|
||||
looks like this, as _partial()_ is _always_ passed as a local variable:
|
||||
|
||||
### Mime Types
|
||||
partial('comment.haml', { collection: comments });
|
||||
|
||||
Express and Connect now utilize the _mime_ module in npm, so to add more use:
|
||||
|
||||
require('mime').define({ 'foo/bar': ['foo', 'bar'] });
|
||||
To make things even less verbose we can assume the extension when omitted:
|
||||
|
||||
### static() middleware
|
||||
partial('comment', { collection: comments });
|
||||
|
||||
Previously named `staticProvider()`, the now `static()` middleware takes a single directory path, followed by options.
|
||||
|
||||
app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
|
||||
And once again even further, when rendering a collection we can simply pass
|
||||
an array, if no other options are desired:
|
||||
|
||||
Previously when using options the `root` option would be used for this:
|
||||
partial('comments', comments);
|
||||
|
||||
app.use(express.staticProvider({ root: __dirname + '/public', maxAge: oneYear }));
|
||||
|
||||
### Redirecting
|
||||
|
||||
Previously you would
|
||||
|
||||
this.redirect('/somewhere');
|
||||
|
||||
However you would now:
|
||||
|
||||
res.redirect('/somewhere');
|
||||
res.redirect('/somewhere', 301);
|
||||
|
||||
### HTTP Client
|
||||
|
||||
Previously Express provided a high level http client, this library is no more
|
||||
as it does not belong in Express, however it may be resurrected as a separate module.
|
||||
|
||||
### Core Extensions
|
||||
|
||||
Express is no longer dependent on the [JavaScript Extensions](http://github.com/visionmedia/ext.js) library, so those of you using the methods provided by it such as `Object.merge(a, b)` will need to
|
||||
roll your own, or install the module via:
|
||||
|
||||
$ npm install ext
|
||||
@@ -1,223 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
|
||||
<style>
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
margin-bottom: 30px;
|
||||
color: rgba(255,255,255,0.7); }
|
||||
html {
|
||||
background: #1c1c1c url(images/bg.tile.jpg); }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding-bottom: 30px;
|
||||
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
background: url(images/bg.jpg) 50% 0 no-repeat;
|
||||
color: #8b8b8b; }
|
||||
|
||||
* {
|
||||
outline: none; }
|
||||
|
||||
em {
|
||||
color: white; }
|
||||
|
||||
a img {
|
||||
border: none !important; }
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
a:hover {
|
||||
opacity: 0.8; }
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
margin: 45px 0 0 0;
|
||||
color: white;
|
||||
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
|
||||
|
||||
h3 {
|
||||
font-size: 18px; }
|
||||
h4 {
|
||||
margin-left: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 20px 10px;
|
||||
padding: 25px 20px;
|
||||
background: rgba(0,0,0,0.5);
|
||||
border: 1px solid #323232;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px; }
|
||||
|
||||
code {
|
||||
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
|
||||
|
||||
ul {
|
||||
margin: 15px 0;
|
||||
padding: 0 0 0 35px; }
|
||||
ul li {
|
||||
margin: 0;
|
||||
padding: 2px 0;
|
||||
list-style: square; }
|
||||
ul li ul {
|
||||
margin: 0;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.man-name, #Express { display:none; }
|
||||
|
||||
.sect {
|
||||
margin-left: 40px; }
|
||||
img {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
margin-left: 30%;
|
||||
margin-bottom: 30px;
|
||||
width: 194px;
|
||||
height: 51px;
|
||||
background: url(images/logo.png) 0 0 no-repeat;
|
||||
text-indent: -99999px; }
|
||||
#logo:hover {
|
||||
opacity: 0.7; }
|
||||
#logo:active {
|
||||
opacity: 0.3; }
|
||||
|
||||
#ribbon {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 2; }
|
||||
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
min-height: 800px;
|
||||
background: url(images/top.png) 0 0 repeat-x; }
|
||||
|
||||
#container {
|
||||
margin: 0 auto;
|
||||
padding-top: 80px;
|
||||
width: 550px; }
|
||||
|
||||
#toc {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: 0 0 0 15px;
|
||||
padding: 15px;
|
||||
height: 100%;
|
||||
background: rgba(0,0,0,0.2);
|
||||
overflow: auto;
|
||||
border-right: 1px solid rgba(255,255,255,0.05);
|
||||
}
|
||||
#toc li {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
#toc li a {
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 75px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
display: inline;
|
||||
list-style: none; }
|
||||
#menu li a {
|
||||
display: block;
|
||||
float: left;
|
||||
margin: 0 2px;
|
||||
padding: 3px 15px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
-webkit-border-radius: 8px;
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
#menu li a:hover,
|
||||
#menu li a.active {
|
||||
background: rgba(0,0,0,0.5); }
|
||||
#menu li a:active {
|
||||
background: rgba(0,0,0,0.1);
|
||||
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
|
||||
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
|
||||
</style>
|
||||
<script>
|
||||
$(function(){
|
||||
$('.section').hide();
|
||||
$('.toggle, a.section-title').toggle(function(){
|
||||
$(this).siblings('ul').fadeIn(300);
|
||||
return false;
|
||||
}, function(){
|
||||
$(this).siblings('ul').fadeOut(300);
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a href='http://github.com/visionmedia/express'>
|
||||
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
|
||||
</a>
|
||||
<div id="wrapper">
|
||||
<div id="container">
|
||||
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
|
||||
<p id="tagline">
|
||||
High performance, high class web development for
|
||||
<a href="http://nodejs.org">Node.js</a>
|
||||
</p>
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="screencasts.html">Screencasts</a></li>
|
||||
<li><a href="applications.html">Applications</a></li>
|
||||
</ul>
|
||||
<h3>Introduction</h3>
|
||||
|
||||
<p>This introduction screencast covers the basics of Express, and how to get started with your first application.</p>
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=139583' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=139583' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
|
||||
|
||||
<h3>View Partials</h3>
|
||||
|
||||
<p>In this screencast we work with partials to display a collection of users using the <a href="http://jade-lang.com">Jade</a> template engine, and learn about view path resolution.</p>
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=139591' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=139591' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
|
||||
|
||||
<h3>Route Specific Middleware</h3>
|
||||
|
||||
<p>In the screencast below we learn about the benefits of route-specific middleware.</p>
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=140296' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=140296' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
|
||||
|
||||
<h3>Route Param Preconditions</h3>
|
||||
|
||||
<p>Learn about route parameter (<em>/user/:id</em>) pre-conditions, providing automated validation, and loading of data via the named route param segments.</p>
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=140300' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=140300' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,24 +0,0 @@
|
||||
|
||||
### Introduction
|
||||
|
||||
This introduction screencast covers the basics of Express, and how to get started with your first application.
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=139583' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=139583' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
|
||||
### View Partials
|
||||
|
||||
In this screencast we work with partials to display a collection of users using the [Jade](http://jade-lang.com) template engine, and learn about view path resolution.
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=139591' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=139591' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
|
||||
### Route Specific Middleware
|
||||
|
||||
In the screencast below we learn about the benefits of route-specific middleware.
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=140296' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=140296' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
|
||||
### Route Param Preconditions
|
||||
|
||||
Learn about route parameter (_/user/:id_) pre-conditions, providing automated validation, and loading of data via the named route param segments.
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=140300' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=140300' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
@@ -6,60 +6,60 @@ require.paths.unshift(__dirname + '/../../support');
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, crypto = require('crypto');
|
||||
var express = require('../../lib/express'),
|
||||
crypto = require('crypto');
|
||||
|
||||
var app = express.createServer(
|
||||
express.bodyParser()
|
||||
, express.cookieParser()
|
||||
, express.session({ secret: 'keyboard cat' })
|
||||
);
|
||||
var app = express.createServer();
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
app.use(express.bodyDecoder());
|
||||
app.use(express.cookieDecoder());
|
||||
app.use(express.session());
|
||||
|
||||
// Message helper, ideally we would use req.flash()
|
||||
// however this is more light-weight for an example
|
||||
|
||||
app.dynamicHelpers({
|
||||
message: function(req){
|
||||
var err = req.session.error
|
||||
, msg = req.session.success;
|
||||
delete req.session.error;
|
||||
delete req.session.success;
|
||||
if (err) return '<p class="msg error">' + err + '</p>';
|
||||
if (msg) return '<p class="msg success">' + msg + '</p>';
|
||||
}
|
||||
message: function(req){
|
||||
var err = req.session.error,
|
||||
msg = req.session.success;
|
||||
delete req.session.error;
|
||||
delete req.session.success;
|
||||
if (err) return '<p class="msg error">' + err + '</p>';
|
||||
if (msg) return '<p class="msg success">' + msg + '</p>';
|
||||
}
|
||||
});
|
||||
|
||||
// Generate a salt for the user to prevent rainbow table attacks
|
||||
|
||||
var users = {
|
||||
tj: {
|
||||
name: 'tj'
|
||||
, salt: 'randomly-generated-salt'
|
||||
, pass: md5('foobar' + 'randomly-generated-salt')
|
||||
}
|
||||
tj: {
|
||||
name: 'tj',
|
||||
salt: 'randomly-generated-salt',
|
||||
pass: md5('foobar' + 'randomly-generated-salt')
|
||||
}
|
||||
};
|
||||
|
||||
// Used to generate a hash of the plain-text password + salt
|
||||
|
||||
function md5(str) {
|
||||
return crypto.createHash('md5').update(str).digest('hex');
|
||||
return crypto.createHash('md5').update(str).digest('hex');
|
||||
}
|
||||
|
||||
// Authenticate using our plain-object database of doom!
|
||||
|
||||
function authenticate(name, pass, fn) {
|
||||
var user = users[name];
|
||||
// query the db for the given username
|
||||
if (!user) return fn(new Error('cannot find user'));
|
||||
// apply the same algorithm to the POSTed password, applying
|
||||
// the md5 against the pass / salt, if there is a match we
|
||||
// found the user
|
||||
if (user.pass == md5(pass + user.salt)) return fn(null, user);
|
||||
// Otherwise password is invalid
|
||||
fn(new Error('invalid password'));
|
||||
var user = users[name];
|
||||
// query the db for the given username
|
||||
if (!user) return fn(new Error('cannot find user'));
|
||||
// apply the same algorithm to the POSTed password, applying
|
||||
// the md5 against the pass / salt, if there is a match we
|
||||
// found the user
|
||||
if (user.pass == md5(pass + user.salt)) return fn(null, user);
|
||||
// Otherwise password is invalid
|
||||
fn(new Error('invalid password'));
|
||||
}
|
||||
|
||||
function restrict(req, res, next) {
|
||||
@@ -72,54 +72,54 @@ function restrict(req, res, next) {
|
||||
}
|
||||
|
||||
function accessLogger(req, res, next) {
|
||||
console.log('/restricted accessed by %s', req.session.user.name);
|
||||
next();
|
||||
console.log('/restricted accessed by %s', req.session.user.name);
|
||||
next();
|
||||
}
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('/login');
|
||||
res.redirect('/login');
|
||||
});
|
||||
|
||||
app.get('/restricted', restrict, accessLogger, function(req, res){
|
||||
res.send('Wahoo! restricted area');
|
||||
res.send('Wahoo! restricted area');
|
||||
});
|
||||
|
||||
app.get('/logout', function(req, res){
|
||||
// destroy the user's session to log them out
|
||||
// will be re-created next request
|
||||
req.session.destroy(function(){
|
||||
res.redirect('home');
|
||||
});
|
||||
// destroy the user's session to log them out
|
||||
// will be re-created next request
|
||||
req.session.destroy(function(){
|
||||
res.redirect('home');
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/login', function(req, res){
|
||||
if (req.session.user) {
|
||||
req.session.success = 'Authenticated as ' + req.session.user.name
|
||||
+ ' click to <a href="/logout">logout</a>. '
|
||||
+ ' You may now access <a href="/restricted">/restricted</a>.';
|
||||
}
|
||||
res.render('login');
|
||||
if (req.session.user) {
|
||||
req.session.success = 'Authenticated as ' + req.session.user.name
|
||||
+ ' click to <a href="/logout">logout</a>. '
|
||||
+ ' You may now access <a href="/restricted">/restricted</a>.';
|
||||
}
|
||||
res.render('login');
|
||||
});
|
||||
|
||||
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
|
||||
req.session.regenerate(function(){
|
||||
// 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;
|
||||
res.redirect('back');
|
||||
});
|
||||
} else {
|
||||
req.session.error = 'Authentication failed, please check your '
|
||||
+ ' username and password.'
|
||||
+ ' (use "tj" and "foobar")';
|
||||
res.redirect('back');
|
||||
}
|
||||
});
|
||||
authenticate(req.body.username, req.body.password, function(err, user){
|
||||
if (user) {
|
||||
// Regenerate session when signing in
|
||||
// to prevent fixation
|
||||
req.session.regenerate(function(){
|
||||
// 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;
|
||||
res.redirect('back');
|
||||
});
|
||||
} else {
|
||||
req.session.error = 'Authentication failed, please check your '
|
||||
+ ' username and password.'
|
||||
+ ' (use "tj" and "foobar")';
|
||||
res.redirect('back');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
@@ -6,56 +6,20 @@ require.paths.unshift(__dirname + '/../../support');
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, messages = require('express-messages');
|
||||
var express = require('./../../lib/express');
|
||||
|
||||
// Our app IS the exports, this prevents require('./app').app,
|
||||
// instead it is require('./app');
|
||||
var app = module.exports = express.createServer();
|
||||
|
||||
// Config
|
||||
// Illustrates that an app can be broken into
|
||||
// several files, but yet extend the same app
|
||||
require('./main');
|
||||
require('./contact');
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
// Illustrates that one app (Server instance) can
|
||||
// be "mounted" to another at the given route.
|
||||
app.use('/blog', require('./blog'));
|
||||
|
||||
// mount hook
|
||||
|
||||
app.mounted(function(other){
|
||||
console.log('ive been mounted!');
|
||||
});
|
||||
|
||||
// Flash message helper provided by express-messages
|
||||
// $ npm install express-messages
|
||||
|
||||
app.dynamicHelpers({
|
||||
messages: messages
|
||||
, base: function(){
|
||||
// return the app's mount-point
|
||||
// so that urls can adjust. For example
|
||||
// if you run this example /post/add works
|
||||
// however if you run the mounting example
|
||||
// it adjusts to /blog/post/add
|
||||
return '/' == app.route ? '' : app.route;
|
||||
}
|
||||
});
|
||||
|
||||
// Middleware
|
||||
|
||||
app.configure(function(){
|
||||
app.use(express.logger('\x1b[33m:method\x1b[0m \x1b[32m:url\x1b[0m :response-time'));
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.session({ secret: 'keyboard cat' }));
|
||||
app.use(app.router);
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||
});
|
||||
|
||||
// Routes
|
||||
|
||||
require('./routes/site')(app);
|
||||
require('./routes/post')(app);
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
45
examples/blog/blog/index.js
Normal file
45
examples/blog/blog/index.js
Normal file
@@ -0,0 +1,45 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('./../../../lib/express'),
|
||||
fs = require('fs');
|
||||
|
||||
// Export our app as the module
|
||||
var app = module.exports = express.createServer();
|
||||
|
||||
// Set views directory
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// Load our posts
|
||||
var posts = JSON.parse(fs.readFileSync(__dirname + '/posts.json', 'utf8'));
|
||||
|
||||
// Set our default view engine to "ejs"
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
app.dynamicHelpers({
|
||||
basepath: function(){
|
||||
// "this" is the app, we can
|
||||
// dynamically provide the "home"
|
||||
// setting to all views
|
||||
return this.set('home');
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index', {
|
||||
locals: {
|
||||
posts: posts
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/post/:id', function(req, res){
|
||||
var id = req.params.id;
|
||||
res.render('post', {
|
||||
locals: {
|
||||
post: posts[id]
|
||||
}
|
||||
});
|
||||
});
|
||||
4
examples/blog/blog/posts.json
Normal file
4
examples/blog/blog/posts.json
Normal file
@@ -0,0 +1,4 @@
|
||||
[
|
||||
{ "id": 0, "title": "Post one", "body": "Just some lame post" },
|
||||
{ "id": 1, "title": "Post two", "body": "Just another lame post" }
|
||||
]
|
||||
2
examples/blog/blog/views/index.ejs
Normal file
2
examples/blog/blog/views/index.ejs
Normal file
@@ -0,0 +1,2 @@
|
||||
<h2>Posts</h2>
|
||||
<%- partial('post', { collection: posts, as: global }) %>
|
||||
9
examples/blog/blog/views/layout.ejs
Normal file
9
examples/blog/blog/views/layout.ejs
Normal file
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Blog Example</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Best Blog Ever</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
2
examples/blog/blog/views/partials/post.ejs
Normal file
2
examples/blog/blog/views/partials/post.ejs
Normal file
@@ -0,0 +1,2 @@
|
||||
<h3><a href="<%= basepath %>/post/<%= id %>"><%= title %></a></h3>
|
||||
<p><%= body %></p>
|
||||
1
examples/blog/blog/views/post.ejs
Normal file
1
examples/blog/blog/views/post.ejs
Normal file
@@ -0,0 +1 @@
|
||||
<%- partial('post', { object: post, as: global }) %>
|
||||
9
examples/blog/contact.js
Normal file
9
examples/blog/contact.js
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
// in ./app.js we did "module.exports", allowing
|
||||
// us to grab the app from the parent module (the one
|
||||
// which required it)
|
||||
var app = module.parent.exports;
|
||||
|
||||
app.get('/contact', function(req, res){
|
||||
res.send('<p>Wahoo contact page</p>');
|
||||
});
|
||||
10
examples/blog/main.js
Normal file
10
examples/blog/main.js
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
// in ./app.js we did "module.exports", allowing
|
||||
// us to grab the app from the parent module (the one
|
||||
// which required it)
|
||||
var app = module.parent.exports;
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('<p>Visit <a href="/blog">/blog</a>'
|
||||
+ ' or <a href="/contact">/contact</a></p>');
|
||||
});
|
||||
@@ -1,67 +0,0 @@
|
||||
|
||||
// Fake data store
|
||||
|
||||
var ids = 0
|
||||
, db = {};
|
||||
|
||||
var Post = exports = module.exports = function Post(title, body) {
|
||||
this.id = ++ids;
|
||||
this.title = title;
|
||||
this.body = body;
|
||||
this.createdAt = new Date;
|
||||
};
|
||||
|
||||
Post.prototype.save = function(fn){
|
||||
db[this.id] = this;
|
||||
fn();
|
||||
};
|
||||
|
||||
Post.prototype.validate = function(fn){
|
||||
if (!this.title) return fn(new Error('_title_ required'));
|
||||
if (!this.body) return fn(new Error('_body_ required'));
|
||||
if (this.body.length < 10) {
|
||||
return fn(new Error(
|
||||
'_body_ should be at least **10** characters long, was only _' + this.title.length + '_'));
|
||||
}
|
||||
fn();
|
||||
};
|
||||
|
||||
|
||||
Post.prototype.update = function(data, fn){
|
||||
this.updatedAt = new Date;
|
||||
for (var key in data) {
|
||||
if (undefined != data[key]) {
|
||||
this[key] = data[key];
|
||||
}
|
||||
}
|
||||
this.save(fn);
|
||||
};
|
||||
|
||||
Post.prototype.destroy = function(fn){
|
||||
exports.destroy(this.id, fn);
|
||||
};
|
||||
|
||||
exports.count = function(fn){
|
||||
fn(null, Object.keys(db).length);
|
||||
};
|
||||
|
||||
exports.all = function(fn){
|
||||
var arr = Object.keys(db).reduce(function(arr, id){
|
||||
arr.push(db[id]);
|
||||
return arr;
|
||||
}, []);
|
||||
fn(null, arr);
|
||||
};
|
||||
|
||||
exports.get = function(id, fn){
|
||||
fn(null, db[id]);
|
||||
};
|
||||
|
||||
exports.destroy = function(id, fn) {
|
||||
if (db[id]) {
|
||||
delete db[id];
|
||||
fn();
|
||||
} else {
|
||||
fn(new Error('post ' + id + ' does not exist'));
|
||||
}
|
||||
};
|
||||
@@ -1,58 +0,0 @@
|
||||
body {
|
||||
font: 13px "Helvetica Neue", Arial, sans-serif;
|
||||
color: #111;
|
||||
padding: 60px 80px;
|
||||
}
|
||||
h1, h2 {
|
||||
color: #c00;
|
||||
}
|
||||
a {
|
||||
color: #c00;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
label {
|
||||
padding: 6px 0;
|
||||
display: block;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
textarea,
|
||||
input {
|
||||
outline: none;
|
||||
padding: 5px;
|
||||
background: #f1f1f1;
|
||||
border: 1px solid #aaa;
|
||||
}
|
||||
textarea:focus,
|
||||
input:focus {
|
||||
background: #fff;
|
||||
}
|
||||
textarea {
|
||||
width: 500px;
|
||||
height: 200px;
|
||||
}
|
||||
a.edit {
|
||||
margin-left: 10px;
|
||||
font-size: 11px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.date {
|
||||
font-size: 11px;
|
||||
}
|
||||
#messages ul {
|
||||
padding: 10px;
|
||||
border: 1px solid;
|
||||
}
|
||||
#messages ul li {
|
||||
list-style: none;
|
||||
}
|
||||
#messages ul.info {
|
||||
color: #2EBBE6;
|
||||
background: #F7FBFD;
|
||||
}
|
||||
#messages ul.error {
|
||||
color: #E4250C;
|
||||
background: #FDEAE7;
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var basicAuth = require('../../../lib/express').basicAuth
|
||||
, Post = require('../models/post');
|
||||
|
||||
module.exports = function(app){
|
||||
/**
|
||||
* Apply basic auth to all post related routes
|
||||
*/
|
||||
|
||||
app.all('/post(/*)?', basicAuth(function(user, pass){
|
||||
return 'admin' == user && 'express' == pass;
|
||||
}));
|
||||
|
||||
/**
|
||||
* Map :post to the database, loading
|
||||
* every time :post is present.
|
||||
*/
|
||||
|
||||
app.param('post', function(req, res, next, id){
|
||||
Post.get(id, function(err, post){
|
||||
if (err) return next(err);
|
||||
if (!post) return next(new Error('failed to load post ' + id));
|
||||
req.post = post;
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a post.
|
||||
*/
|
||||
|
||||
app.get('/post/add', function(req, res){
|
||||
res.render('post/form', { post: {}});
|
||||
});
|
||||
|
||||
/**
|
||||
* Save a post.
|
||||
*/
|
||||
|
||||
app.post('/post', function(req, res){
|
||||
var data = req.body.post
|
||||
, post = new Post(data.title, data.body);
|
||||
|
||||
post.validate(function(err){
|
||||
if (err) {
|
||||
req.flash('error', err.message);
|
||||
return res.redirect('back');
|
||||
}
|
||||
|
||||
post.save(function(err){
|
||||
req.flash('info', 'Successfully created post _%s_', post.title);
|
||||
res.redirect('/post/' + post.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Display the post.
|
||||
*/
|
||||
|
||||
app.get('/post/:post', function(req, res){
|
||||
res.render('post', { post: req.post });
|
||||
});
|
||||
|
||||
/**
|
||||
* Display the post edit form.
|
||||
*/
|
||||
|
||||
app.get('/post/:post/edit', function(req, res){
|
||||
res.render('post/form', { post: req.post });
|
||||
});
|
||||
|
||||
/**
|
||||
* Update post. Typically a data layer would handle this stuff.
|
||||
*/
|
||||
|
||||
app.put('/post/:post', function(req, res, next){
|
||||
var post = req.post;
|
||||
post.validate(function(err){
|
||||
if (err) {
|
||||
req.flash('error', err.message);
|
||||
return res.redirect('back');
|
||||
}
|
||||
post.update(req.body.post, function(err){
|
||||
if (err) return next(err);
|
||||
req.flash('info', 'Successfully updated post');
|
||||
res.redirect('back');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Post = require('../models/post');
|
||||
|
||||
module.exports = function(app){
|
||||
app.get('/', function(req, res){
|
||||
Post.count(function(err, count){
|
||||
Post.all(function(err, posts){
|
||||
res.render('index', {
|
||||
count: count
|
||||
, posts: posts
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
h1 Blog
|
||||
|
||||
!= messages()
|
||||
|
||||
- if (count)
|
||||
p Display all #{count} post(s)
|
||||
#posts!= partial('post', posts)
|
||||
- else
|
||||
p
|
||||
| It looks like you have no posts!
|
||||
p
|
||||
| Click
|
||||
a(href=base + '/post/add') here
|
||||
| to create a post. Login
|
||||
| as
|
||||
em "admin"
|
||||
| and
|
||||
em "express"
|
||||
| .
|
||||
@@ -1,7 +0,0 @@
|
||||
!!! 5
|
||||
html
|
||||
head
|
||||
title Blog
|
||||
link(rel='stylesheet', href=base + '/style.css')
|
||||
body
|
||||
#container!= body
|
||||
@@ -1,20 +0,0 @@
|
||||
|
||||
- if (post.title)
|
||||
h1 Editing #{post.title}
|
||||
- else
|
||||
h1 New Post
|
||||
|
||||
!= messages()
|
||||
|
||||
form#post(action=base + '/post' + (post.title ? '/' + post.id : ''), method='post')
|
||||
- if (post.title)
|
||||
input(type='hidden', name='_method', value='put')
|
||||
p
|
||||
label(for='post[title]') Title:
|
||||
input(type='text', name='post[title]', value=post.title)
|
||||
p
|
||||
label(for='post[body]') Body:
|
||||
textarea(name='post[body]')= post.body || ''
|
||||
p
|
||||
input(type='submit', value=post.title ? 'Update' : 'Create')
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
.post
|
||||
// title
|
||||
h2
|
||||
= post.title
|
||||
a.edit(href=base + '/post/' + post.id + '/edit') Edit
|
||||
|
||||
// flash messages
|
||||
!= messages()
|
||||
|
||||
// dates
|
||||
p.date.created Created at #{post.createdAt}
|
||||
- if (post.updatedAt)
|
||||
p.date.updated Updated at #{post.updatedAt}
|
||||
|
||||
// body
|
||||
pre.body= post.body
|
||||
@@ -1,48 +1,45 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('./../../lib/express');
|
||||
|
||||
var app = express.createServer(
|
||||
// Place default Connect favicon above logger so it is not in
|
||||
// the logging output
|
||||
express.favicon(),
|
||||
// Place default Connect favicon above logger so it is not in
|
||||
// the logging output
|
||||
express.favicon(),
|
||||
|
||||
// Custom logger format
|
||||
express.logger({ format: '\x1b[1m:method\x1b[0m \x1b[33m:url\x1b[0m :response-time' }),
|
||||
// Custom logger format
|
||||
express.logger({ format: '\x1b[1m:method\x1b[0m \x1b[33m:url\x1b[0m :response-time' }),
|
||||
|
||||
// Provides req.cookies
|
||||
express.cookieParser(),
|
||||
// Provides req.cookies
|
||||
express.cookieDecoder(),
|
||||
|
||||
// Parses x-www-form-urlencoded request bodies (and json)
|
||||
express.bodyParser()
|
||||
// Parses x-www-form-urlencoded request bodies (and json)
|
||||
express.bodyDecoder()
|
||||
);
|
||||
|
||||
app.get('/', function(req, res){
|
||||
if (req.cookies.remember) {
|
||||
res.send('Remembered :). Click to <a href="/forget">forget</a>!.');
|
||||
} else {
|
||||
res.send('<form method="post"><p>Check to <label>'
|
||||
+ '<input type="checkbox" name="remember"/> remember me</label> '
|
||||
+ '<input type="submit" value="Submit"/>.</p></form>');
|
||||
}
|
||||
if (req.cookies.remember) {
|
||||
res.send('Remembered :). Click to <a href="/forget">forget</a>!.');
|
||||
} else {
|
||||
res.send('<form method="post"><p>Check to <label>'
|
||||
+ '<input type="checkbox" name="remember"/> remember me</label> '
|
||||
+ '<input type="submit" value="Submit"/>.</p></form>');
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/forget', function(req, res){
|
||||
res.clearCookie('remember');
|
||||
res.redirect('back');
|
||||
res.clearCookie('remember');
|
||||
res.redirect('back');
|
||||
});
|
||||
|
||||
app.post('/', function(req, res){
|
||||
if (req.body.remember) {
|
||||
res.cookie('remember', '1', { path: '/', expires: new Date(Date.now() + 900000), httpOnly: true });
|
||||
}
|
||||
res.redirect('back');
|
||||
if (req.body.remember) {
|
||||
res.cookie('remember', '1', { path: '/', expires: new Date(Date.now() + 900000), httpOnly: true });
|
||||
}
|
||||
res.redirect('back');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
@@ -1,46 +1,31 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('./../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('<ul>'
|
||||
+ '<li>Download <a href="/files/amazing.txt">amazing.txt</a>.</li>'
|
||||
+ '<li>Download <a href="/files/missing.txt">missing.txt</a>.</li>'
|
||||
+ '</ul>');
|
||||
res.send('<ul>'
|
||||
+ '<li>Download <a href="/files/amazing.txt">amazing.txt</a>.</li>'
|
||||
+ '<li>Download <a href="/files/missing.txt">missing.txt</a>.</li>'
|
||||
+ '</ul>');
|
||||
});
|
||||
|
||||
// /files/* is accessed via req.params[0]
|
||||
// but here we name it :file
|
||||
app.get('/files/:file(*)', function(req, res, next){
|
||||
var file = req.params.file
|
||||
, path = __dirname + '/files/' + file;
|
||||
// either res.download(path) and let
|
||||
// express handle failures, or provide
|
||||
// a callback
|
||||
res.download(path, function(err){
|
||||
if (err) return next(err);
|
||||
// the response has invoked .end()
|
||||
// so you cannnot respond here (of course)
|
||||
// but the callback is handy for statistics etc.
|
||||
console.log('transferred %s', path);
|
||||
});
|
||||
app.get('/files/*', function(req, res){
|
||||
var file = req.params[0];
|
||||
res.download(__dirname + '/files/' + file);
|
||||
});
|
||||
|
||||
app.error(function(err, req, res, next){
|
||||
if ('ENOENT' == err.code) {
|
||||
res.send('Cant find that file, sorry!');
|
||||
} else {
|
||||
// Not a 404
|
||||
next(err);
|
||||
}
|
||||
if (process.ENOENT == err.errno) {
|
||||
res.send('Cant find that file, sorry!');
|
||||
} else {
|
||||
// Not a 404
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
@@ -1 +1 @@
|
||||
what an amazing download
|
||||
wow supes cool are you glad you downloaded this?
|
||||
@@ -6,29 +6,27 @@ require.paths.unshift(__dirname + '/../../support');
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('./../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// Register ejs as .html
|
||||
|
||||
app.register('.html', require('ejs'));
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'html');
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
{ name: 'tj', email: 'tj@sencha.com' }
|
||||
, { name: 'ciaran', email: 'ciaranj@gmail.com' }
|
||||
, { name: 'aaron', email: 'aaron.heckmann+github@gmail.com' }
|
||||
{ name: 'tj', email: 'tj@sencha.com' },
|
||||
{ name: 'ciaran', email: 'ciaranj@gmail.com' },
|
||||
{ name: 'aaron', email: 'aaron.heckmann+github@gmail.com' }
|
||||
];
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('users', { users: users });
|
||||
res.render('users.ejs', {
|
||||
locals: {
|
||||
users: users
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
app.listen(3000);
|
||||
6
examples/ejs/views/layout.ejs
Normal file
6
examples/ejs/views/layout.ejs
Normal file
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Users</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Users</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
3
examples/ejs/views/users.ejs
Normal file
3
examples/ejs/views/users.ejs
Normal file
@@ -0,0 +1,3 @@
|
||||
<ul id="users">
|
||||
<%- partial('user', users) %>
|
||||
</ul>
|
||||
@@ -1,3 +0,0 @@
|
||||
<ul id="users">
|
||||
<%- partial('user', users) %>
|
||||
</ul>
|
||||
@@ -6,9 +6,10 @@ require.paths.unshift(__dirname + '/../../support');
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('./../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
var app = express.createServer(),
|
||||
sys = require('sys');
|
||||
|
||||
// Serve default connect favicon
|
||||
app.use(express.favicon());
|
||||
@@ -31,7 +32,7 @@ app.use(app.router);
|
||||
// exception
|
||||
|
||||
app.use(function(req, res, next){
|
||||
next(new NotFound(req.url));
|
||||
next(new NotFound(req.url));
|
||||
});
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
@@ -39,21 +40,17 @@ app.set('views', __dirname + '/views');
|
||||
// Provide our app with the notion of NotFound exceptions
|
||||
|
||||
function NotFound(path){
|
||||
this.name = 'NotFound';
|
||||
if (path) {
|
||||
Error.call(this, 'Cannot find ' + path);
|
||||
this.path = path;
|
||||
} else {
|
||||
Error.call(this, 'Not Found');
|
||||
}
|
||||
Error.captureStackTrace(this, arguments.callee);
|
||||
this.name = 'NotFound';
|
||||
if (path) {
|
||||
Error.call(this, 'Cannot find ' + path);
|
||||
this.path = path;
|
||||
} else {
|
||||
Error.call(this, 'Not Found');
|
||||
}
|
||||
Error.captureStackTrace(this, arguments.callee);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Error.prototype`.
|
||||
*/
|
||||
|
||||
NotFound.prototype.__proto__ = Error.prototype;
|
||||
sys.inherits(NotFound, Error);
|
||||
|
||||
// We can call app.error() several times as shown below.
|
||||
// Here we check for an instanceof NotFound and show the
|
||||
@@ -64,32 +61,42 @@ NotFound.prototype.__proto__ = Error.prototype;
|
||||
// in the development environment.
|
||||
|
||||
app.error(function(err, req, res, next){
|
||||
if (err instanceof NotFound) {
|
||||
res.render('404.jade', { status: 404, error: err });
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
if (err instanceof NotFound) {
|
||||
res.render('404.jade', {
|
||||
status: 404,
|
||||
locals: {
|
||||
error: err
|
||||
}
|
||||
});
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
// Here we assume all errors as 500 for the simplicity of
|
||||
// this demo, however you can choose whatever you like
|
||||
|
||||
app.error(function(err, req, res){
|
||||
res.render('500.jade', { status: 500, error: err });
|
||||
res.render('500.jade', {
|
||||
status: 500,
|
||||
locals: {
|
||||
error: err
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Routes
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.jade');
|
||||
res.render('index.jade');
|
||||
});
|
||||
|
||||
app.get('/404', function(req, res){
|
||||
throw new NotFound(req.url);
|
||||
throw new NotFound;
|
||||
});
|
||||
|
||||
app.get('/500', function(req, res, next){
|
||||
next(new Error('keyboard cat!'));
|
||||
next(new Error('keyboard cat!'));
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('./../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
// Caught and passed down to the errorHandler middleware
|
||||
throw new Error('something broke!');
|
||||
// Caught and passed down to the errorHandler middleware
|
||||
throw new Error('something broke!');
|
||||
});
|
||||
|
||||
app.get('/next', function(req, res, next){
|
||||
// We can also pass exceptions to next()
|
||||
next(new Error('oh no!'))
|
||||
// We can also pass exceptions to next()
|
||||
next(new Error('oh no!'))
|
||||
});
|
||||
|
||||
// The errorHandler middleware in this case will dump exceptions to stderr
|
||||
// as well as show the stack trace in responses, currently handles text/plain,
|
||||
// text/html, and application/json responses to aid in development
|
||||
app.use('/', express.errorHandler({ dump: true, stack: true }));
|
||||
app.use('/', express.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||
|
||||
app.listen(3000);
|
||||
@@ -11,8 +11,8 @@ var express = require('../../lib/express');
|
||||
// App with session support
|
||||
|
||||
var app = express.createServer(
|
||||
express.cookieParser()
|
||||
, express.session({ secret: 'keyboard cat' })
|
||||
express.cookieDecoder(),
|
||||
express.session()
|
||||
);
|
||||
|
||||
// View settings
|
||||
@@ -20,29 +20,58 @@ var app = express.createServer(
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
// Dynamic helpers are functions which are executed
|
||||
// on each view render, unless dynamicHelpers is false.
|
||||
|
||||
// So for example we do not need to call messages() in our
|
||||
// template, "messages" will be populated with the return
|
||||
// value of this function.
|
||||
|
||||
app.dynamicHelpers({
|
||||
// express-messages is a dynamicHelper that
|
||||
// renders the flash messages to HTML for you
|
||||
// $ npm install express-messages
|
||||
messages: require('express-messages')
|
||||
messages: function(req, res){
|
||||
// In the case of flash messages
|
||||
// we return a function, allowing
|
||||
// flash messages to only be flushed
|
||||
// when called, otherwise every request
|
||||
// will flush flash messages regardless.
|
||||
return function(){
|
||||
// Grab the flash messages
|
||||
var messages = req.flash();
|
||||
// We will render the "messages.ejs" partial
|
||||
return res.partial('messages', {
|
||||
// Our target object is our messages
|
||||
object: messages,
|
||||
// We want it to be named "types" in the partial
|
||||
// since they are keyed like this:
|
||||
// { info: ['foo'], error: ['bar']}
|
||||
as: 'types',
|
||||
// Pass a local named "hasMessages" so we can easily
|
||||
// check if we have any messages at all
|
||||
locals: { hasMessages: Object.keys(messages).length },
|
||||
// We dont want dynamicHelpers in this partial, as
|
||||
// it would cause infinite recursion
|
||||
dynamicHelpers: false
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.dynamicHelpers({
|
||||
// Another dynamic helper example. Since dynamic
|
||||
// helpers resolve at view rendering time, we can
|
||||
// "inject" the "page" local variable per request
|
||||
// providing us with the request url.
|
||||
page: function(req, res){
|
||||
return req.url;
|
||||
}
|
||||
// Another dynamic helper example. Since dynamic
|
||||
// helpers resolve at view rendering time, we can
|
||||
// "inject" the "page" local variable per request
|
||||
// providing us with the request url.
|
||||
page: function(req, res){
|
||||
return req.url;
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
// Not very realistic notifications but illustrates usage
|
||||
req.flash('info', 'email queued');
|
||||
req.flash('info', 'email sent');
|
||||
req.flash('error', 'delivery failed');
|
||||
res.render('index');
|
||||
// Not very realistic notifications but illustrates usage
|
||||
req.flash('info', 'email queued');
|
||||
req.flash('info', 'email sent');
|
||||
req.flash('error', 'delivery failed');
|
||||
res.render('index');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
<%- body %>
|
||||
</body>
|
||||
<body>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
12
examples/flash/views/partials/messages.ejs
Normal file
12
examples/flash/views/partials/messages.ejs
Normal file
@@ -0,0 +1,12 @@
|
||||
<% if (hasMessages) { %>
|
||||
<div id="messages">
|
||||
<% for (var type in types) { %>
|
||||
<h2><%= type %> messages</h2>
|
||||
<ul id="<%= type %>">
|
||||
<% types[type].forEach(function(msg){ %>
|
||||
<li><%= msg %></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
<% } %>
|
||||
</div>
|
||||
<% } %>
|
||||
@@ -1,19 +1,17 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('./../../lib/express'),
|
||||
sys = require('sys');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// Here we use the bodyDecoder middleware
|
||||
// to parse urlencoded request bodies
|
||||
// which populates req.body
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.bodyDecoder());
|
||||
|
||||
// The methodOverride middleware allows us
|
||||
// to set a hidden input of _method to an arbitrary
|
||||
@@ -21,60 +19,60 @@ app.use(express.bodyParser());
|
||||
app.use(express.methodOverride());
|
||||
|
||||
// Required by session
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.cookieDecoder());
|
||||
|
||||
// Required by req.flash() for persistent
|
||||
// notifications
|
||||
app.use(express.session({ secret: 'keyboard cat' }));
|
||||
app.use(express.session());
|
||||
|
||||
app.get('/', function(req, res){
|
||||
// get ?name=foo
|
||||
var name = req.param('name') || '';
|
||||
|
||||
// Switch the button label based if we have a name
|
||||
var label = name ? 'Update' : 'Save';
|
||||
// get ?name=foo
|
||||
var name = req.param('name') || '';
|
||||
|
||||
// Switch the button label based if we have a name
|
||||
var label = name ? 'Update' : 'Save';
|
||||
|
||||
// Buffer all flash messages.
|
||||
// Typically this would all be done in a template
|
||||
// however for illustration purposes we iterate
|
||||
// here.
|
||||
|
||||
// The messages in req.flash() persist until called,
|
||||
// at which time they are flushed from the session
|
||||
var msgs = '<ul>',
|
||||
flash = req.flash();
|
||||
Object.keys(flash).forEach(function(type){
|
||||
flash[type].forEach(function(msg){
|
||||
msgs += '<li class="' + type + '">' + msg + '</li>';
|
||||
// Buffer all flash messages.
|
||||
// Typically this would all be done in a template
|
||||
// however for illustration purposes we iterate
|
||||
// here.
|
||||
|
||||
// The messages in req.flash() persist until called,
|
||||
// at which time they are flushed from the session
|
||||
var msgs = '<ul>',
|
||||
flash = req.flash();
|
||||
Object.keys(flash).forEach(function(type){
|
||||
flash[type].forEach(function(msg){
|
||||
msgs += '<li class="' + type + '">' + msg + '</li>';
|
||||
});
|
||||
});
|
||||
});
|
||||
msgs += '</ul>';
|
||||
msgs += '</ul>';
|
||||
|
||||
// If we have a name, we are updating,
|
||||
// so add the hidden _method input
|
||||
res.send(msgs
|
||||
+ '<form method="post">'
|
||||
+ (name ? '<input type="hidden" value="put" name="_method" />' : '')
|
||||
+ 'Name: <input type="text" name="name" value="' + name + '" />'
|
||||
+ '<input type="submit" value="' + label + '" />'
|
||||
+ '</form>');
|
||||
// If we have a name, we are updating,
|
||||
// so add the hidden _method input
|
||||
res.send(msgs
|
||||
+ '<form method="post">'
|
||||
+ (name ? '<input type="hidden" value="put" name="_method" />' : '')
|
||||
+ 'Name: <input type="text" name="name" value="' + name + '" />'
|
||||
+ '<input type="submit" value="' + label + '" />'
|
||||
+ '</form>');
|
||||
});
|
||||
|
||||
app.post('/', function(req, res){
|
||||
if (req.body.name) {
|
||||
// Typically here we would create a resource
|
||||
req.flash('info', 'Saved ' + req.body.name);
|
||||
res.redirect('/?name=' + req.body.name);
|
||||
} else {
|
||||
req.flash('error', 'Error: name required');
|
||||
res.redirect('/');
|
||||
}
|
||||
if (req.body.name) {
|
||||
// Typically here we would create a resource
|
||||
req.flash('info', 'Saved ' + req.body.name);
|
||||
res.redirect('/?name=' + req.body.name);
|
||||
} else {
|
||||
req.flash('error', 'Error: name required');
|
||||
res.redirect('/');
|
||||
}
|
||||
});
|
||||
|
||||
app.put('/', function(req, res){
|
||||
// Typically here we would update a resource
|
||||
req.flash('info', 'Updated ' + req.body.name);
|
||||
res.redirect('/?name=' + req.body.name);
|
||||
// Typically here we would update a resource
|
||||
req.flash('info', 'Updated ' + req.body.name);
|
||||
res.redirect('/?name=' + req.body.name);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -13,54 +10,49 @@ var app = express.createServer();
|
||||
// Fake items
|
||||
|
||||
var items = [
|
||||
{ name: 'foo' }
|
||||
, { name: 'bar' }
|
||||
, { name: 'baz' }
|
||||
{ name: 'foo' },
|
||||
{ name: 'bar' },
|
||||
{ name: 'baz' }
|
||||
];
|
||||
|
||||
// Routes
|
||||
|
||||
app.get('/', function(req, res, next){
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.write('<p>Visit /item/2</p>');
|
||||
res.write('<p>Visit /item/2.json</p>');
|
||||
res.write('<p>Visit /item/2.xml</p>');
|
||||
res.end();
|
||||
res.send('Visit /item/2');
|
||||
});
|
||||
|
||||
app.get('/item/:id.:format?', function(req, res, next){
|
||||
var id = req.params.id
|
||||
, format = req.params.format
|
||||
, item = items[id];
|
||||
// Ensure item exists
|
||||
if (item) {
|
||||
// Serve the format
|
||||
switch (format) {
|
||||
case 'json':
|
||||
// Detects json
|
||||
res.send(item);
|
||||
break;
|
||||
case 'xml':
|
||||
// Set contentType as xml then sends
|
||||
// the string
|
||||
var xml = ''
|
||||
+ '<items>'
|
||||
+ '<item>' + item.name + '</item>'
|
||||
+ '</items>';
|
||||
res.contentType('.xml');
|
||||
res.send(xml);
|
||||
break;
|
||||
case 'html':
|
||||
default:
|
||||
// By default send some hmtl
|
||||
res.send('<h1>' + item.name + '</h1>');
|
||||
var id = req.params.id,
|
||||
format = req.params.format,
|
||||
item = items[id];
|
||||
// Ensure item exists
|
||||
if (item) {
|
||||
// Serve the format
|
||||
switch (format) {
|
||||
case 'json':
|
||||
// Detects json
|
||||
res.send(item);
|
||||
break;
|
||||
case 'xml':
|
||||
// Set contentType as xml then sends
|
||||
// the string
|
||||
var xml = ''
|
||||
+ '<items>'
|
||||
+ '<item>' + item.name + '</item>'
|
||||
+ '</items>';
|
||||
res.contentType('.xml');
|
||||
res.send(xml);
|
||||
break;
|
||||
case 'html':
|
||||
default:
|
||||
// By default send some hmtl
|
||||
res.send('<h1>' + item.name + '</h1>');
|
||||
}
|
||||
} else {
|
||||
// We could simply pass route control and potentially 404
|
||||
// by calling next(), or pass an exception like below.
|
||||
next(new Error('Item ' + id + ' does not exist'));
|
||||
}
|
||||
} else {
|
||||
// We could simply pass route control and potentially 404
|
||||
// by calling next(), or pass an exception like below.
|
||||
next(new Error('Item ' + id + ' does not exist'));
|
||||
}
|
||||
});
|
||||
|
||||
// Middleware
|
||||
|
||||
@@ -6,15 +6,14 @@ require.paths.unshift(__dirname + '/../../support');
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, http = require('http');
|
||||
var express = require('./../../lib/express'),
|
||||
http = require('http');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// Expose our views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
/**
|
||||
* Request github json api `path`.
|
||||
@@ -25,20 +24,20 @@ app.set('view engine', 'jade');
|
||||
*/
|
||||
|
||||
function request(path, fn){
|
||||
var client = http.createClient(80, 'github.com')
|
||||
, req = client.request('GET', '/api/v2/json' + path, { Host: 'github.com' });
|
||||
req.on('response', function(res){
|
||||
res.body = '';
|
||||
res.on('data', function(chunk){ res.body += chunk; });
|
||||
res.on('end', function(){
|
||||
try {
|
||||
fn(null, JSON.parse(res.body));
|
||||
} catch (err) {
|
||||
fn(err);
|
||||
}
|
||||
var client = http.createClient(80, 'github.com'),
|
||||
req = client.request('GET', '/api/v2/json' + path, { Host: 'github.com' });
|
||||
req.addListener('response', function(res){
|
||||
res.body = '';
|
||||
res.addListener('data', function(chunk){ res.body += chunk; });
|
||||
res.addListener('end', function(){
|
||||
try {
|
||||
fn(null, JSON.parse(res.body));
|
||||
} catch (err) {
|
||||
fn(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
req.end();
|
||||
req.end();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,11 +48,11 @@ function request(path, fn){
|
||||
*/
|
||||
|
||||
function sort(repos){
|
||||
return repos.sort(function(a, b){
|
||||
if (a.watchers == b.watchers) return 0;
|
||||
if (a.watchers > b.watchers) return -1;
|
||||
if (a.watchers < b.watchers) return 1;
|
||||
});
|
||||
return repos.sort(function(a, b){
|
||||
if (a.watchers == b.watchers) return 0;
|
||||
if (a.watchers > b.watchers) return -1;
|
||||
if (a.watchers < b.watchers) return 1;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,9 +64,9 @@ function sort(repos){
|
||||
*/
|
||||
|
||||
function totalWatchers(repos) {
|
||||
return repos.reduce(function(sum, repo){
|
||||
return sum + repo.watchers;
|
||||
}, 0);
|
||||
return repos.reduce(function(sum, repo){
|
||||
return sum + repo.watchers;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,7 +74,7 @@ function totalWatchers(repos) {
|
||||
*/
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('/repos/visionmedia');
|
||||
res.redirect('/repos/visionmedia');
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -83,33 +82,37 @@ app.get('/', function(req, res){
|
||||
*/
|
||||
|
||||
app.get('/repos/*', function(req, res, next){
|
||||
var names = req.params[0].split('/')
|
||||
, users = [];
|
||||
(function fetchData(name){
|
||||
// We have a user name
|
||||
if (name) {
|
||||
console.log('... fetching \x1b[33m%s\x1b[0m', name);
|
||||
request('/repos/show/' + name, function(err, user){
|
||||
if (err) {
|
||||
next(err)
|
||||
var names = req.params[0].split('/'),
|
||||
users = [];
|
||||
(function fetchData(name){
|
||||
// We have a user name
|
||||
if (name) {
|
||||
console.log('... fetching \x1b[33m%s\x1b[0m', name);
|
||||
request('/repos/show/' + name, function(err, user){
|
||||
if (err) {
|
||||
next(err)
|
||||
} else {
|
||||
user.totalWatchers = totalWatchers(user.repositories);
|
||||
user.repos = sort(user.repositories);
|
||||
user.name = name;
|
||||
users.push(user);
|
||||
fetchData(names.shift());
|
||||
}
|
||||
});
|
||||
// No more users
|
||||
} else {
|
||||
user.totalWatchers = totalWatchers(user.repositories);
|
||||
user.repos = sort(user.repositories);
|
||||
user.name = name;
|
||||
users.push(user);
|
||||
fetchData(names.shift());
|
||||
console.log('... done');
|
||||
res.render('index.jade', {
|
||||
locals: {
|
||||
users: users
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// No more users
|
||||
} else {
|
||||
console.log('... done');
|
||||
res.render('index', { users: users });
|
||||
}
|
||||
})(names.shift());
|
||||
})(names.shift());
|
||||
});
|
||||
|
||||
// Serve statics from ./public
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
app.use(express.staticProvider(__dirname + '/public'));
|
||||
|
||||
// Listen on port 3000
|
||||
app.listen(3000);
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
body {
|
||||
padding: 30px 50px;
|
||||
font: 12px/1.4 "Helvetica Neue", Arial, sans-serif;
|
||||
padding: 30px 50px;
|
||||
font: 12px/1.4 "Helvetica Neue", Arial, sans-serif;
|
||||
}
|
||||
a {
|
||||
color: #00AAFF;
|
||||
text-decoration: none;
|
||||
color: #00AAFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.user {
|
||||
margin: 0 10px;
|
||||
float: left;
|
||||
width: 300px;
|
||||
width: 25%;
|
||||
}
|
||||
table td:nth-child(2) {
|
||||
padding: 0 5px;
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('./../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
res.send('Hello World');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
@@ -6,7 +6,7 @@ require.paths.unshift(__dirname + '/../../support');
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('./../../lib/express');
|
||||
|
||||
// Path to our public directory
|
||||
|
||||
@@ -15,63 +15,33 @@ var pub = __dirname + '/public';
|
||||
// Auto-compile sass to css with "compiler"
|
||||
// and then serve with connect's staticProvider
|
||||
|
||||
var app = express.createServer();
|
||||
app.use(express.compiler({ src: pub, enable: ['sass'] }));
|
||||
app.use(app.router);
|
||||
app.use(express.static(pub));
|
||||
app.use(express.errorHandler({ dump: true, stack: true }));
|
||||
var app = express.createServer(
|
||||
express.compiler({ src: pub, enable: ['sass'] }),
|
||||
express.staticProvider(pub)
|
||||
);
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// Set our default template engine to "jade"
|
||||
// which prevents the need for extensions
|
||||
// (although you can still mix and match)
|
||||
// which prevents the need for extensions (although you can still mix and match)
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
function User(name, email) {
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
new User('tj', 'tj@vision-media.ca')
|
||||
, new User('ciaran', 'ciaranj@gmail.com')
|
||||
, new User('aaron', 'aaron.heckmann+github@gmail.com')
|
||||
{ name: 'tj', email: 'tj@sencha.com' },
|
||||
{ name: 'ciaran', email: 'ciaranj@gmail.com' },
|
||||
{ name: 'aaron', email: 'aaron.heckmann+github@gmail.com' }
|
||||
];
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('users', { users: users });
|
||||
});
|
||||
|
||||
app.get('/users/callback', function(req, res){
|
||||
// a callback is also accepted
|
||||
res.partial('users/user', users, function(err, html){
|
||||
if (err) throw err;
|
||||
res.send(html);
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/users', function(req, res){
|
||||
// we can use res.partial() as if
|
||||
// we were in a view, utilizing the same api
|
||||
// to render a fragment
|
||||
res.partial('users/user', users);
|
||||
});
|
||||
|
||||
app.get('/users/list', function(req, res){
|
||||
// use "object" to utilize the name deduced from
|
||||
// the view filename. The examples below are equivalent
|
||||
|
||||
//res.partial('users/list', { object: users });
|
||||
res.partial('users/list', { list: users });
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res){
|
||||
res.partial('users/user', users[req.params.id]);
|
||||
res.render('users', {
|
||||
locals: {
|
||||
users: users
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -1,3 +0,0 @@
|
||||
ul#users
|
||||
- each user in list
|
||||
li!= partial('user', user)
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
// $ npm install markdown
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, md = require('markdown').markdown;
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// register .md so that markdown may comply
|
||||
// with the express view system by implementing
|
||||
// a .compile() method
|
||||
|
||||
app.register('.md', {
|
||||
compile: function(str, options){
|
||||
var html = md.toHTML(str);
|
||||
return function(locals){
|
||||
return html.replace(/\{([^}]+)\}/g, function(_, name){
|
||||
return locals[name];
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'md');
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index', { layout: false, title: 'Markdown Example' });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
# {title}
|
||||
|
||||
Just an example view rendered with _markdown_.
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, blog = require('../blog/app');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.session({ secret: 'keyboard cat' }));
|
||||
|
||||
// mount the blog. the blog app is written using the "base"
|
||||
// local variable, allowing it's urls to adjust to wherever
|
||||
// we wish to mount it.
|
||||
app.use('/blog', blog);
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Visit <a href="/blog">/blog</a>');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Server listening on port 3000');
|
||||
@@ -6,45 +6,46 @@ require.paths.unshift(__dirname + '/../../support');
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, form = require('connect-form');
|
||||
var express = require('../../lib/express'),
|
||||
form = require('connect-form'),
|
||||
sys = require('sys');
|
||||
|
||||
var app = express.createServer(
|
||||
// connect-form (http://github.com/visionmedia/connect-form)
|
||||
// middleware uses the formidable middleware to parse urlencoded
|
||||
// and multipart form data
|
||||
form({ keepExtensions: true })
|
||||
// connect-form (http://github.com/visionmedia/connect-form)
|
||||
// middleware uses the formidable middleware to parse urlencoded
|
||||
// and multipart form data
|
||||
form({ keepExtensions: true })
|
||||
);
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('<form method="post" enctype="multipart/form-data">'
|
||||
+ '<p>Image: <input type="file" name="image" /></p>'
|
||||
+ '<p><input type="submit" value="Upload" /></p>'
|
||||
+ '</form>');
|
||||
res.send('<form method="post" enctype="multipart/form-data">'
|
||||
+ '<p>Image: <input type="file" name="image" /></p>'
|
||||
+ '<p><input type="submit" value="Upload" /></p>'
|
||||
+ '</form>');
|
||||
});
|
||||
|
||||
app.post('/', function(req, res, next){
|
||||
|
||||
// connect-form adds the req.form object
|
||||
// we can (optionally) define onComplete, passing
|
||||
// the exception (if any) fields parsed, and files parsed
|
||||
req.form.complete(function(err, fields, files){
|
||||
if (err) {
|
||||
next(err);
|
||||
} else {
|
||||
console.log('\nuploaded %s to %s'
|
||||
, files.image.filename
|
||||
, files.image.path);
|
||||
res.redirect('back');
|
||||
}
|
||||
});
|
||||
// connect-form adds the req.form object
|
||||
// we can (optionally) define onComplete, passing
|
||||
// the exception (if any) fields parsed, and files parsed
|
||||
req.form.complete(function(err, fields, files){
|
||||
if (err) {
|
||||
next(err);
|
||||
} else {
|
||||
console.log('\nuploaded %s to %s',
|
||||
files.image.filename,
|
||||
files.image.path);
|
||||
res.redirect('back');
|
||||
}
|
||||
});
|
||||
|
||||
// We can add listeners for several form
|
||||
// events such as "progress"
|
||||
req.form.on('progress', function(bytesReceived, bytesExpected){
|
||||
var percent = (bytesReceived / bytesExpected * 100) | 0;
|
||||
process.stdout.write('Uploading: %' + percent + '\r');
|
||||
});
|
||||
// We can add listeners for several form
|
||||
// events such as "progress"
|
||||
req.form.addListener('progress', function(bytesReceived, bytesExpected){
|
||||
var percent = (bytesReceived / bytesExpected * 100) | 0;
|
||||
sys.print('Uploading: %' + percent + '\r');
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
module.exports = {
|
||||
|
||||
// /
|
||||
|
||||
index: function(req, res){
|
||||
res.render();
|
||||
}
|
||||
|
||||
// /
|
||||
|
||||
index: function(req, res){
|
||||
res.render();
|
||||
}
|
||||
};
|
||||
@@ -2,54 +2,54 @@
|
||||
// Fake user database for example
|
||||
|
||||
var users = [
|
||||
{ id: 0, name: 'TJ', email: 'tj@vision-media.ca' }
|
||||
, { id: 1, name: 'Simon', email: 'simon@vision-media.ca' }
|
||||
{ id: 0, name: 'TJ', email: 'tj@vision-media.ca' },
|
||||
{ id: 1, name: 'Simon', email: 'simon@vision-media.ca' }
|
||||
];
|
||||
|
||||
function get(id, fn) {
|
||||
if (users[id]) {
|
||||
fn(null, users[id]);
|
||||
} else {
|
||||
fn(new Error('User ' + id + ' does not exist'));
|
||||
}
|
||||
if (users[id]) {
|
||||
fn(null, users[id]);
|
||||
} else {
|
||||
fn(new Error('User ' + id + ' does not exist'));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
// /users
|
||||
|
||||
index: function(req, res){
|
||||
res.render(users);
|
||||
},
|
||||
|
||||
// /users
|
||||
|
||||
index: function(req, res){
|
||||
res.render(users);
|
||||
},
|
||||
|
||||
// /users/:id
|
||||
// /users/:id
|
||||
|
||||
show: function(req, res, next){
|
||||
get(req.params.id, function(err, user){
|
||||
if (err) return next(err);
|
||||
res.render(user);
|
||||
});
|
||||
},
|
||||
|
||||
// /users/:id/edit
|
||||
|
||||
edit: function(req, res, next){
|
||||
get(req.params.id, function(err, user){
|
||||
if (err) return next(err);
|
||||
res.render(user);
|
||||
});
|
||||
},
|
||||
|
||||
// PUT /users/:id
|
||||
|
||||
update: function(req, res, next){
|
||||
var id = req.params.id;
|
||||
get(id, function(err){
|
||||
if (err) return next(err);
|
||||
var user = users[id] = req.body.user;
|
||||
user.id = id;
|
||||
req.flash('info', 'Successfully updated _' + user.name + '_.');
|
||||
res.redirect('back');
|
||||
});
|
||||
}
|
||||
show: function(req, res, next){
|
||||
get(req.params.id, function(err, user){
|
||||
if (err) return next(err);
|
||||
res.render(user);
|
||||
});
|
||||
},
|
||||
|
||||
// /users/:id/edit
|
||||
|
||||
edit: function(req, res, next){
|
||||
get(req.params.id, function(err, user){
|
||||
if (err) return next(err);
|
||||
res.render(user);
|
||||
});
|
||||
},
|
||||
|
||||
// PUT /users/:id
|
||||
|
||||
update: function(req, res, next){
|
||||
var id = req.params.id;
|
||||
get(id, function(err){
|
||||
if (err) return next(err);
|
||||
var user = users[id] = req.body.user;
|
||||
user.id = id;
|
||||
req.flash('info', 'Successfully updated _' + user.name + '_.');
|
||||
res.redirect('back');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -3,146 +3,149 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var fs = require('fs')
|
||||
, express = require('../../lib/express');
|
||||
var fs = require('fs'),
|
||||
express = require('../../lib/express');
|
||||
|
||||
exports.boot = function(app){
|
||||
bootApplication(app);
|
||||
bootControllers(app);
|
||||
bootApplication(app);
|
||||
bootControllers(app);
|
||||
};
|
||||
|
||||
// App settings and middleware
|
||||
|
||||
function bootApplication(app) {
|
||||
app.use(express.logger({ format: ':method :url :status' }));
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.session({ secret: 'keyboard cat' }));
|
||||
app.use(app.router);
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
app.use(express.logger({ format: ':method :url :status' }));
|
||||
app.use(express.bodyDecoder());
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.cookieDecoder());
|
||||
app.use(express.session());
|
||||
app.use(app.router);
|
||||
app.use(express.staticProvider(__dirname + '/public'));
|
||||
|
||||
// Example 500 page
|
||||
app.error(function(err, req, res){
|
||||
console.dir(err)
|
||||
res.render('500');
|
||||
});
|
||||
// Example 500 page
|
||||
app.error(function(err, req, res){
|
||||
console.dir(err)
|
||||
res.render('500');
|
||||
});
|
||||
|
||||
// Example 404 page via simple Connect middleware
|
||||
app.use(function(req, res){
|
||||
res.render('404');
|
||||
});
|
||||
// Example 404 page via simple Connect middleware
|
||||
app.use(function(req, res){
|
||||
res.render('404');
|
||||
});
|
||||
|
||||
// Setup ejs views as default, with .html as the extension
|
||||
app.set('views', __dirname + '/views');
|
||||
app.register('.html', require('ejs'));
|
||||
app.set('view engine', 'html');
|
||||
// Setup ejs views as default, with .html as the extension
|
||||
app.set('views', __dirname + '/views');
|
||||
app.register('.html', require('ejs'));
|
||||
app.set('view engine', 'html');
|
||||
|
||||
// Some dynamic view helpers
|
||||
app.dynamicHelpers({
|
||||
request: function(req){
|
||||
return req;
|
||||
},
|
||||
// Some dynamic view helpers
|
||||
app.dynamicHelpers({
|
||||
request: function(req){
|
||||
return req;
|
||||
},
|
||||
|
||||
hasMessages: function(req){
|
||||
return Object.keys(req.session.flash || {}).length;
|
||||
},
|
||||
hasMessages: function(req){
|
||||
return Object.keys(req.session.flash || {}).length;
|
||||
},
|
||||
|
||||
messages: function(req){
|
||||
return function(){
|
||||
var msgs = req.flash();
|
||||
return Object.keys(msgs).reduce(function(arr, type){
|
||||
return arr.concat(msgs[type]);
|
||||
}, []);
|
||||
}
|
||||
}
|
||||
});
|
||||
messages: function(req){
|
||||
return function(){
|
||||
var msgs = req.flash();
|
||||
return Object.keys(msgs).reduce(function(arr, type){
|
||||
return arr.concat(msgs[type]);
|
||||
}, []);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Bootstrap controllers
|
||||
|
||||
function bootControllers(app) {
|
||||
fs.readdir(__dirname + '/controllers', function(err, files){
|
||||
if (err) throw err;
|
||||
files.forEach(function(file){
|
||||
bootController(app, file);
|
||||
fs.readdir(__dirname + '/controllers', function(err, files){
|
||||
if (err) throw err;
|
||||
files.forEach(function(file){
|
||||
bootController(app, file);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Example (simplistic) controller support
|
||||
|
||||
function bootController(app, file) {
|
||||
var name = file.replace('.js', '')
|
||||
, actions = require('./controllers/' + name)
|
||||
, plural = name + 's' // realistically we would use an inflection lib
|
||||
, prefix = '/' + plural;
|
||||
var name = file.replace('.js', ''),
|
||||
actions = require('./controllers/' + name),
|
||||
plural = name + 's', // realistically we would use an inflection lib
|
||||
prefix = '/' + plural;
|
||||
|
||||
// Special case for "app"
|
||||
if (name == 'app') prefix = '/';
|
||||
|
||||
Object.keys(actions).map(function(action){
|
||||
var fn = controllerAction(name, plural, action, actions[action]);
|
||||
switch(action) {
|
||||
case 'index':
|
||||
app.get(prefix, fn);
|
||||
break;
|
||||
case 'show':
|
||||
app.get(prefix + '/:id.:format?', fn);
|
||||
break;
|
||||
case 'add':
|
||||
app.get(prefix + '/:id/add', fn);
|
||||
break;
|
||||
case 'create':
|
||||
app.post(prefix + '/:id', fn);
|
||||
break;
|
||||
case 'edit':
|
||||
app.get(prefix + '/:id/edit', fn);
|
||||
break;
|
||||
case 'update':
|
||||
app.put(prefix + '/:id', fn);
|
||||
break;
|
||||
case 'destroy':
|
||||
app.del(prefix + '/:id', fn);
|
||||
break;
|
||||
// Special case for "app"
|
||||
if (name == 'app') {
|
||||
prefix = '/';
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(actions).map(function(action){
|
||||
var fn = controllerAction(name, plural, action, actions[action]);
|
||||
switch(action) {
|
||||
case 'index':
|
||||
app.get(prefix, fn);
|
||||
break;
|
||||
case 'show':
|
||||
app.get(prefix + '/:id.:format?', fn);
|
||||
break;
|
||||
case 'add':
|
||||
app.get(prefix + '/:id/add', fn);
|
||||
break;
|
||||
case 'create':
|
||||
app.post(prefix + '/:id', fn);
|
||||
break;
|
||||
case 'edit':
|
||||
app.get(prefix + '/:id/edit', fn);
|
||||
break;
|
||||
case 'update':
|
||||
app.put(prefix + '/:id', fn);
|
||||
break;
|
||||
case 'destroy':
|
||||
app.del(prefix + '/:id', fn);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Proxy res.render() to add some magic
|
||||
|
||||
function controllerAction(name, plural, action, fn) {
|
||||
return function(req, res, next){
|
||||
var render = res.render
|
||||
, format = req.params.format
|
||||
, path = __dirname + '/views/' + name + '/' + action + '.html';
|
||||
res.render = function(obj, options, fn){
|
||||
res.render = render;
|
||||
// Template path
|
||||
if (typeof obj === 'string') {
|
||||
return res.render(obj, options, fn);
|
||||
}
|
||||
return function(req, res, next){
|
||||
var render = res.render,
|
||||
format = req.params.format,
|
||||
path = __dirname + '/views/' + name + '/' + action + '.html';
|
||||
res.render = function(obj, options, fn){
|
||||
res.render = render;
|
||||
// Template path
|
||||
if (typeof obj === 'string') {
|
||||
return res.render(obj, options, fn);
|
||||
}
|
||||
|
||||
// Format support
|
||||
if (action == 'show' && format) {
|
||||
if (format === 'json') {
|
||||
return res.send(obj);
|
||||
} else {
|
||||
throw new Error('unsupported format "' + format + '"');
|
||||
}
|
||||
}
|
||||
// Format support
|
||||
if (action == 'show' && format) {
|
||||
if (format === 'json') {
|
||||
return res.send(obj);
|
||||
} else {
|
||||
throw new Error('unsupported format "' + format + '"');
|
||||
}
|
||||
}
|
||||
|
||||
// Render template
|
||||
res.render = render;
|
||||
options = options || {};
|
||||
// Expose obj as the "users" or "user" local
|
||||
if (action == 'index') {
|
||||
options[plural] = obj;
|
||||
} else {
|
||||
options[name] = obj;
|
||||
}
|
||||
return res.render(path, options, fn);
|
||||
// Render template
|
||||
res.render = render;
|
||||
options = options || {};
|
||||
options.locals = options.locals || {};
|
||||
// Expose obj as the "users" or "user" local
|
||||
if (action == 'index') {
|
||||
options.locals[plural] = obj;
|
||||
} else {
|
||||
options.locals[name] = obj;
|
||||
}
|
||||
return res.render(path, options, fn);
|
||||
};
|
||||
fn.apply(this, arguments);
|
||||
};
|
||||
fn.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - MVC Example</title>
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<%- body %>
|
||||
</body>
|
||||
<head>
|
||||
<title>Express - MVC Example</title>
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +0,0 @@
|
||||
<% if (hasMessages) { %>
|
||||
<ul id="messages">
|
||||
<% messages().forEach(function(msg){ %>
|
||||
<li><%- msg %></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
<% } %>
|
||||
7
examples/mvc/views/partials/messages.html
Normal file
7
examples/mvc/views/partials/messages.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<% if (hasMessages) { %>
|
||||
<ul id="messages">
|
||||
<% messages().forEach(function(msg){ %>
|
||||
<li><%- msg %></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
<% } %>
|
||||
@@ -1,5 +1,5 @@
|
||||
<h1>Editing user <%= user.name %></h1>
|
||||
<%- partial('../messages') %>
|
||||
<%- partial('messages') %>
|
||||
<form method="post" action="/users/<%= user.id %>">
|
||||
<input type="hidden" name="_method" value="put" />
|
||||
<p>Name: <input type="text" name="user[name]", value="<%= user.name %>" /></p>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<h1>Users</h1>
|
||||
<%- partial('../messages') %>
|
||||
<%- partial('messages') %>
|
||||
<ul>
|
||||
<% users.forEach(function(user){ %>
|
||||
<li><a href="/users/<%= user.id %>"><%= user.name %></a></li>
|
||||
<% }) %>
|
||||
<% users.forEach(function(user){ %>
|
||||
<li><a href="/users/<%= user.id %>"><%= user.name %></a></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
@@ -1,5 +1,5 @@
|
||||
<h1>Viewing user #<%= user.id %></h1>
|
||||
<%- partial('../messages') %>
|
||||
<%- partial('messages') %>
|
||||
<ul>
|
||||
<li><%- user.name %></li>
|
||||
<li><%- user.email %></li>
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, app = express.createServer();
|
||||
|
||||
// Faux database
|
||||
|
||||
var users = [
|
||||
{ name: 'tj' }
|
||||
, { name: 'tobi' }
|
||||
, { name: 'loki' }
|
||||
, { name: 'jane' }
|
||||
, { name: 'bandit' }
|
||||
];
|
||||
|
||||
// Convert :to and :from to integers
|
||||
|
||||
app.param(['to', 'from'], function(n){ return parseInt(n, 10); });
|
||||
|
||||
// Load user by id
|
||||
|
||||
app.param('user', function(req, res, next, id){
|
||||
if (req.user = users[id]) {
|
||||
next();
|
||||
} else {
|
||||
next(new Error('failed to find user'));
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET index.
|
||||
*/
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Visit /user/0 or /users/0-2');
|
||||
});
|
||||
|
||||
/**
|
||||
* GET :user.
|
||||
*/
|
||||
|
||||
app.get('/user/:user', function(req, res, next){
|
||||
res.send('user ' + req.user.name);
|
||||
});
|
||||
|
||||
/**
|
||||
* GET users :from - :to.
|
||||
*/
|
||||
|
||||
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; });
|
||||
res.send('users ' + names.slice(from, to).join(', '));
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express application listening on port 3000');
|
||||
@@ -1,35 +0,0 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// Set our default template engine to "jade"
|
||||
// which prevents the need for extensions
|
||||
// (although you can still mix and match)
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
// Dummy record
|
||||
var ninja = {
|
||||
name: 'leonardo',
|
||||
summary: { email: 'hunter.loftis+github@gmail.com', master: 'splinter', description: 'peaceful leader' },
|
||||
weapons: ['katana', 'fists', 'shell'],
|
||||
victims: ['shredder', 'brain', 'beebop', 'rocksteady']
|
||||
};
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('ninja', { ninja: ninja });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -1,5 +0,0 @@
|
||||
!!!
|
||||
html
|
||||
head
|
||||
title Partials Example
|
||||
body!= body
|
||||
@@ -1 +0,0 @@
|
||||
li= value
|
||||
@@ -1 +0,0 @@
|
||||
li.weapon= weapon
|
||||
@@ -1,22 +0,0 @@
|
||||
h1= ninja.name
|
||||
|
||||
// file, partial name, and partial object all match ('summary')
|
||||
// the partial filename prefix '_' is completely optional.
|
||||
|
||||
// In this case we need to specify ninja.summary as the object
|
||||
// option, since it is a "plain" object Express cannot otherwise
|
||||
// tell if it is intended to be locals, or THE summary object
|
||||
#summary!= partial('summary', { object: ninja.summary })
|
||||
|
||||
// file, partial name = '_weapon', resolves to 'weapon' object within partial
|
||||
#weapons
|
||||
h2 Weapons
|
||||
// the weapon partial is rendered once per item in
|
||||
// the weapons array or "collection"
|
||||
ul!= partial('weapon', ninja.weapons)
|
||||
|
||||
// partial name 'victim' resolves to 'victim.jade'
|
||||
// or 'victim/index.jade', providing the "victim" local
|
||||
#victims
|
||||
h2 Victims
|
||||
ul!= partial('victim', ninja.victims)
|
||||
@@ -1,4 +0,0 @@
|
||||
h2 Summary
|
||||
p= summary.email
|
||||
p= summary.description
|
||||
p taught by master #{summary.master}
|
||||
@@ -1,5 +0,0 @@
|
||||
// this is insane overkill, I do not recommend
|
||||
// doing tiny partials like this as it gets expensive
|
||||
// with collections, however this illustrates the new
|
||||
// partial lookup mechanism
|
||||
!= partial('../../li', { object: victim, as: 'value' })
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -13,58 +10,45 @@ var app = express.createServer();
|
||||
// Ad-hoc example resource method
|
||||
|
||||
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;
|
||||
obj.range(req, res, a, b, format);
|
||||
});
|
||||
this.get(path + '/:id', obj.show);
|
||||
this.del(path + '/:id', obj.destroy);
|
||||
this.get(path, obj.index);
|
||||
this.get(path + '/:a..:b', function(req, res){
|
||||
var a = parseInt(req.params.a, 10),
|
||||
b = parseInt(req.params.b, 10);
|
||||
obj.range(req, res, a, b);
|
||||
});
|
||||
this.get(path + '/:id', obj.show);
|
||||
this.del(path + '/:id', obj.destroy);
|
||||
};
|
||||
|
||||
// Fake records
|
||||
|
||||
var users = [
|
||||
{ name: 'tj' }
|
||||
, { name: 'ciaran' }
|
||||
, { name: 'aaron' }
|
||||
, { name: 'guillermo' }
|
||||
, { name: 'simon' }
|
||||
, { name: 'tobi' }
|
||||
{ name: 'tj' },
|
||||
{ name: 'ciaran' },
|
||||
{ name: 'aaron' },
|
||||
{ name: 'guillermo' },
|
||||
{ name: 'simon' },
|
||||
{ name: 'tobi' }
|
||||
];
|
||||
|
||||
// Fake controller.
|
||||
// Fake controller
|
||||
|
||||
var User = {
|
||||
index: function(req, res){
|
||||
res.send(users);
|
||||
},
|
||||
show: function(req, res){
|
||||
res.send(users[req.params.id] || { error: 'Cannot find user' });
|
||||
},
|
||||
destroy: function(req, res){
|
||||
var id = req.params.id;
|
||||
var destroyed = id in users;
|
||||
delete users[id];
|
||||
res.send(destroyed ? 'destroyed' : 'Cannot find user');
|
||||
},
|
||||
range: function(req, res, a, b, format){
|
||||
var range = users.slice(a, b + 1);
|
||||
switch (format) {
|
||||
case 'json':
|
||||
res.send(range);
|
||||
break;
|
||||
case 'html':
|
||||
default:
|
||||
var html = '<ul>' + range.map(function(user){
|
||||
return '<li>' + user.name + '</li>';
|
||||
}).join('\n') + '</ul>';
|
||||
res.send(html);
|
||||
break;
|
||||
index: function(req, res){
|
||||
res.send(users);
|
||||
},
|
||||
show: function(req, res){
|
||||
res.send(users[req.params.id] || { error: 'Cannot find user' });
|
||||
},
|
||||
destroy: function(req, res){
|
||||
var id = req.params.id;
|
||||
var destroyed = id in users;
|
||||
delete users[id];
|
||||
res.send(destroyed ? 'destroyed' : 'Cannot find user');
|
||||
},
|
||||
range: function(req, res, a, b){
|
||||
res.send(users.slice(a, b+1));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// curl http://localhost:3000/users -- responds with all users
|
||||
@@ -76,16 +60,15 @@ var User = {
|
||||
app.resource('/users', User);
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send([
|
||||
'<h1>Examples:</h1> <ul>'
|
||||
, '<li>GET /users</li>'
|
||||
, '<li>GET /users/1</li>'
|
||||
, '<li>GET /users/3</li>'
|
||||
, '<li>GET /users/1..3</li>'
|
||||
, '<li>GET /users/1..3.json</li>'
|
||||
, '<li>DELETE /users/4</li>'
|
||||
, '</ul>'
|
||||
].join('\n'));
|
||||
res.send([
|
||||
'<h1>Examples:</h1> <ul>',
|
||||
'<li>GET /users</li>',
|
||||
'<li>GET /users/1</li>',
|
||||
'<li>GET /users/3</li>',
|
||||
'<li>GET /users/1..3</li>',
|
||||
'<li>DELETE /users/4</li>',
|
||||
'</ul>',
|
||||
].join('\n'));
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -19,44 +16,44 @@ var app = express.createServer();
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
{ id: 0, name: 'tj', email: 'tj@vision-media.ca', role: 'member' }
|
||||
, { id: 1, name: 'ciaran', email: 'ciaranj@gmail.com', role: 'member' }
|
||||
, { id: 2, name: 'aaron', email: 'aaron.heckmann+github@gmail.com', role: 'admin' }
|
||||
{ id: 0, name: 'tj', email: 'tj@vision-media.ca', role: 'member' },
|
||||
{ id: 1, name: 'ciaran', email: 'ciaranj@gmail.com', role: 'member' },
|
||||
{ id: 2, name: 'aaron', email: 'aaron.heckmann+github@gmail.com', role: 'admin' }
|
||||
];
|
||||
|
||||
function loadUser(req, res, next) {
|
||||
// You would fetch your user from the db
|
||||
var user = users[req.params.id];
|
||||
if (user) {
|
||||
req.user = user;
|
||||
next();
|
||||
} else {
|
||||
next(new Error('Failed to load user ' + req.params.id));
|
||||
}
|
||||
// You would fetch your user from the db
|
||||
var user = users[req.params.id];
|
||||
if (user) {
|
||||
req.user = user;
|
||||
next();
|
||||
} else {
|
||||
next(new Error('Failed to load user ' + req.params.id));
|
||||
}
|
||||
}
|
||||
|
||||
function andRestrictToSelf(req, res, next) {
|
||||
// If our authenticated user is the user we are viewing
|
||||
// then everything is fine :)
|
||||
if (req.authenticatedUser.id == req.user.id) {
|
||||
next();
|
||||
} else {
|
||||
// You may want to implement specific exceptions
|
||||
// such as UnauthorizedError or similar so that you
|
||||
// can handle these in app.error() specifically
|
||||
// (view ./examples/pages for this)
|
||||
next(new Error('Unauthorized'));
|
||||
}
|
||||
// If our authenticated user is the user we are viewing
|
||||
// then everything is fine :)
|
||||
if (req.authenticatedUser.id == req.user.id) {
|
||||
next();
|
||||
} else {
|
||||
// You may want to implement specific exceptions
|
||||
// such as UnauthorizedError or similar so that you
|
||||
// can handle these in app.error() specifically
|
||||
// (view ./examples/pages for this)
|
||||
next(new Error('Unauthorized'));
|
||||
}
|
||||
}
|
||||
|
||||
function andRestrictTo(role) {
|
||||
return function(req, res, next) {
|
||||
if (req.authenticatedUser.role == role) {
|
||||
next();
|
||||
} else {
|
||||
next(new Error('Unauthorized'));
|
||||
return function(req, res, next) {
|
||||
if (req.authenticatedUser.role == role) {
|
||||
next();
|
||||
} else {
|
||||
next(new Error('Unauthorized'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Middleware for faux authentication
|
||||
@@ -65,24 +62,24 @@ function andRestrictTo(role) {
|
||||
// may interacte with middleware
|
||||
|
||||
app.use(function(req, res, next){
|
||||
req.authenticatedUser = users[0];
|
||||
next();
|
||||
req.authenticatedUser = users[0];
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('/user/0');
|
||||
res.redirect('/user/0');
|
||||
});
|
||||
|
||||
app.get('/user/:id', loadUser, function(req, res){
|
||||
res.send('Viewing user ' + req.user.name);
|
||||
res.send('Viewing user ' + req.user.name);
|
||||
});
|
||||
|
||||
app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){
|
||||
res.send('Editing user ' + req.user.name);
|
||||
res.send('Editing user ' + req.user.name);
|
||||
});
|
||||
|
||||
app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
|
||||
res.send('Deleted user ' + req.user.name);
|
||||
res.send('Deleted user ' + req.user.name);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
@@ -16,9 +16,9 @@ var express = require('../../lib/express')
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('views', __dirname + '/views');
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.bodyDecoder());
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
app.use(express.staticProvider(__dirname + '/public'));
|
||||
|
||||
// General
|
||||
|
||||
|
||||
@@ -2,11 +2,16 @@
|
||||
// Fake posts database
|
||||
|
||||
var posts = [
|
||||
{ title: 'Foo', body: 'some foo bar' }
|
||||
, { title: 'Foo bar', body: 'more foo bar' }
|
||||
, { title: 'Foo bar baz', body: 'more foo bar baz' }
|
||||
];
|
||||
{ title: 'Foo', body: 'some foo bar' }
|
||||
, { title: 'Foo bar', body: 'more foo bar' }
|
||||
, { title: 'Foo bar baz', body: 'more foo bar baz' }
|
||||
];
|
||||
|
||||
exports.list = function(req, res){
|
||||
res.render('posts', { title: 'Posts', posts: posts });
|
||||
res.render('post/list', {
|
||||
locals: {
|
||||
title: 'Posts'
|
||||
, posts: posts
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
|
||||
exports.index = function(req, res){
|
||||
res.render('index', { title: 'Route Separation Example' });
|
||||
res.render('index', {
|
||||
locals: {
|
||||
title: 'Route Separation Example'
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -2,12 +2,17 @@
|
||||
// Fake user database
|
||||
|
||||
var users = [
|
||||
{ name: 'TJ', email: 'tj@vision-media.ca' }
|
||||
, { name: 'Tobi', email: 'tobi@vision-media.ca' }
|
||||
];
|
||||
{ name: 'TJ', email: 'tj@vision-media.ca' }
|
||||
, { name: 'Tobi', email: 'tobi@vision-media.ca' }
|
||||
];
|
||||
|
||||
exports.list = function(req, res){
|
||||
res.render('users', { title: 'Users', users: users });
|
||||
res.render('user/list', {
|
||||
locals: {
|
||||
title: 'Users'
|
||||
, users: users
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.load = function(req, res, next){
|
||||
@@ -21,16 +26,20 @@ exports.load = function(req, res, next){
|
||||
};
|
||||
|
||||
exports.view = function(req, res){
|
||||
res.render('users/view', {
|
||||
title: 'Viewing user ' + req.user.name
|
||||
, user: req.user
|
||||
res.render('user/view', {
|
||||
locals: {
|
||||
title: 'Viewing user ' + req.user.name
|
||||
, user: req.user
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.edit = function(req, res){
|
||||
res.render('users/edit', {
|
||||
title: 'Editing user ' + req.user.name
|
||||
, user: req.user
|
||||
res.render('user/edit', {
|
||||
locals: {
|
||||
title: 'Editing user ' + req.user.name
|
||||
, user: req.user
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user