Comparar commits

...

64 Commits

Autor SHA1 Mensagem Data
Tj Holowaychuk 24a4a80ccd Release 2.4.5 2011-08-19 10:12:38 -07:00
Tj Holowaychuk 2fdd906a41 docs 2011-08-19 08:53:02 -07:00
Tj Holowaychuk 71cc6bac22 google analytics 2011-08-19 08:51:54 -07:00
Tj Holowaychuk 3a46660932 typo. Closes #815 2011-08-18 09:03:46 -07:00
Tj Holowaychuk adca07a858 Refactored Route to use a single array of callbacks 2011-08-17 15:48:27 -07:00
Tj Holowaychuk 6b924bf1df fixed route error handlers when errors are thrown 2011-08-17 14:39:27 -07:00
Tj Holowaychuk eb88f160b6 Added support for routes to handle errors. Closes #809
currently only the route end-point callbacks
support this, however this will change in the near future
to support route middleware etc
2011-08-17 14:33:11 -07:00
Tj Holowaychuk 01e6df759e Added "basepath" setting to work in conjunction with reverse proxies etc. Closes #805
this will allow you to essentially "trick" the express
app into thinking it is mounted when it is not.
2011-08-17 09:27:28 -07:00
Tj Holowaychuk 1dc4e6fcb4 qs >= 0.3.1 2011-08-17 07:53:16 -07:00
Tj Holowaychuk fff6951d94 use nextRoute() internally 2011-08-16 18:29:57 -07:00
Tj Holowaychuk 1ebd49af75 Changed: removed .call(self) for route callbacks
not sure why we had this, ive never even used it
and the tests dont cover it, and its slower
2011-08-16 18:28:41 -07:00
Tj Holowaychuk 8c2107e337 Added app.routes.all(). Closes #803
not a huge fan of this API-wise, but at least it is something for now
2011-08-16 17:51:20 -07:00
Tj Holowaychuk cddc5442f1 typo 2011-08-15 16:31:11 -07:00
Tj Holowaychuk ac8cb270fe Fixed res.redirect() on windows due to join() usage. Closes #808 2011-08-15 13:46:58 -07:00
Tj Holowaychuk d54ee58f93 Added support for multiple callbacks for app.param(). Closes #801
you can also make several calls to `app.param()` for the same
param name, which is equivalent to passing multiple in
a single call
2011-08-11 11:07:04 -07:00
Tj Holowaychuk ce0fa0a3b2 Added test for multiple app.param() calls for the same param 2011-08-11 10:17:23 -07:00
Tj Holowaychuk e2f41147ed added another test 2011-08-11 10:12:16 -07:00
Tj Holowaychuk af3f7f0a65 Added test for app.param(fn) 2011-08-11 10:11:00 -07:00
Tj Holowaychuk a37003945b docs 2011-08-05 19:35:11 -07:00
Tj Holowaychuk 75361fb177 Release 2.4.4 2011-08-05 04:29:52 -07:00
Tj Holowaychuk 21f9b386db empty string 2011-08-05 04:28:31 -07:00
Tj Holowaychuk 271cb16ecd Fixed res.send(204) (again?) 2011-08-05 04:28:08 -07:00
Tj Holowaychuk 803ec213d7 Fixed res.header() intention of a set, even when undefined 2011-08-03 19:59:40 -07:00
Tj Holowaychuk 9e9042f1eb added header.jade to jade example 2011-08-02 08:59:14 -07:00
Tj Holowaychuk ec4c86f46d fixed * consumption 2011-07-29 09:51:09 -07:00
Arpad Borsos 45f22ec602 specialcase .:format routing to not include a dot in the capture group 2011-07-29 09:43:34 -07:00
Tj Holowaychuk 0e1eb72058 fixed a jade test 2011-07-29 09:32:36 -07:00
Tj Holowaychuk 4690b6cdf2 tweak generated stylus 2011-07-25 11:15:10 -07:00
Tj Holowaychuk 22204a5ce1 Fixed res.send(204) support. Closes #771 2011-07-22 08:34:13 -07:00
Tj Holowaychuk 1ec16c0450 qs >= 0.3.0 2011-07-19 12:08:50 -07:00
Tj Holowaychuk c72abc5293 Release 2.4.3 2011-07-14 12:58:24 -07:00
Tj Holowaychuk 93189ad0b6 Fixed options.filename, exposing to template engines
this is useful for performing relative
lookups within the template engine itself,
without manually specifyin the path
2011-07-14 12:53:49 -07:00
Tj Holowaychuk c0aab36187 Added docs for status option special-case. Closes #739 2011-07-07 09:09:34 -07:00
Tj Holowaychuk 5ae994ee8f Release 2.4.2 2011-07-06 20:15:44 -07:00
Tj Holowaychuk 60d16eab77 Revert "removed jsonp stripping"
This reverts commit 0ae18bca60.
2011-07-06 20:14:42 -07:00
Tj Holowaychuk fc60dfc1a6 docs 2011-07-06 09:59:09 -07:00
Tj Holowaychuk 45d149c146 docs for multiple envs in app.configure() calls 2011-07-06 09:58:32 -07:00
Tj Holowaychuk 4dfc1a69c3 Release 2.4.1 2011-07-06 09:57:06 -07:00
Tj Holowaychuk 0ae18bca60 removed jsonp stripping
I cannot recall why I added this, doesnt make
sense to me now haha.
2011-07-06 09:45:21 -07:00
Tj Holowaychuk 2aaf0defe7 res.send() using res.json() 2011-07-06 09:32:16 -07:00
Tj Holowaychuk 73ea5cd7ee Added res.json() JSONP support. Closes #737 2011-07-06 09:31:37 -07:00
Tj Holowaychuk e7f2d229ec added failing jsonp tests for res.json() 2011-07-06 09:25:10 -07:00
Tj Holowaychuk 87bc265817 moved jsonp tests 2011-07-06 09:24:08 -07:00
Tj Holowaychuk 796aaff295 connect 1.5.2 2011-07-06 09:07:09 -07:00
Tj Holowaychuk 8f87c50320 fixed connect-redis example 2011-07-05 09:55:04 -07:00
Tj Holowaychuk ee4471b345 when cookie path === null dont default it 2011-07-04 13:51:08 -07:00
Tj Holowaychuk 6815feb8cf docs 2011-07-04 13:45:08 -07:00
Tj Holowaychuk dbbe7be891 Added extending-templates example. Closes #730 2011-07-04 13:34:41 -07:00
Tj Holowaychuk 09c9452e5c Changed; default cookie path to "home" setting. Closes #731 2011-07-04 13:18:50 -07:00
Tj Holowaychuk b0e669ba00 remove pids/logs creation from express(1)
just confused people
2011-07-04 13:05:13 -07:00
Tj Holowaychuk f46ae9f3b2 docs 2011-07-04 09:20:35 -07:00
Tj Holowaychuk 9f2b344be8 Added support for multiple env app.configure() calls. Closes #735 2011-07-04 09:16:12 -07:00
Tj Holowaychuk 6b47271679 updated error-pages example 2011-07-01 14:14:46 -07:00
Tj Holowaychuk 6f7075be74 Added "strict routing" setting for trailing slashes
for people who are:

  a) over-optimizing seo
  b) working on fs-like APIs
2011-06-29 16:35:49 -07:00
Tj Holowaychuk 4b9cc3d698 docs 2011-06-28 11:27:51 -07:00
Tj Holowaychuk 3faa790b53 Release 2.4.0 2011-06-28 09:41:21 -07:00
Tj Holowaychuk 9477c9b516 docs for res.status() 2011-06-28 09:32:28 -07:00
Tj Holowaychuk b04be51848 Added chainable res.status(code)
ex:

  res.status(500).send("lame")
2011-06-28 09:32:03 -07:00
Tj Holowaychuk 9e4020efd3 Merge branch 'feature/res-json' 2011-06-28 09:14:09 -07:00
Tj Holowaychuk 6db19db665 docs for res.json 2011-06-28 09:14:04 -07:00
Tj Holowaychuk 1386f80ae5 Added res.json() and tests 2011-06-28 09:11:54 -07:00
Tj Holowaychuk e4342a7097 Added error handling to web-service example 2011-06-28 08:49:43 -07:00
Tj Holowaychuk fda31b75f9 Added simple web-service example 2011-06-23 09:45:19 -07:00
Tj Holowaychuk 8ca0a45b33 hmac for auth example 2011-06-23 08:50:20 -07:00
37 arquivos alterados com 1405 adições e 315 exclusões
+49
Ver Arquivo
@@ -1,4 +1,53 @@
2.4.5 / 2011-08-19
==================
* Added support for routes to handle errors. Closes #809
* Added `app.routes.all()`. Closes #803
* Added "basepath" setting to work in conjunction with reverse proxies etc. * Refactored `Route` to use a single array of callbacks
* Added support for multiple callbacks for `app.param()`. Closes #801
Closes #805
* Changed: removed .call(self) for route callbacks
* Dependency: `qs >= 0.3.1`
* Fixed `res.redirect()` on windows due to `join()` usage. Closes #808
2.4.4 / 2011-08-05
==================
* Fixed `res.header()` intention of a set, even when `undefined`
* Fixed `*`, value no longer required
* Fixed `res.send(204)` support. Closes #771
2.4.3 / 2011-07-14
==================
* Added docs for `status` option special-case. Closes #739
* Fixed `options.filename`, exposing the view path to template engines
2.4.2. / 2011-07-06
==================
* Revert "removed jsonp stripping" for XSS
2.4.1 / 2011-07-06
==================
* Added `res.json()` JSONP support. Closes #737
* Added _extending-templates_ example. Closes #730
* Added "strict routing" setting for trailing slashes
* Added support for multiple envs in `app.configure()` calls. Closes #735
* Changed: `res.send()` using `res.json()`
* Changed: when cookie `path === null` don't default it
* Changed; default cookie path to "home" setting. Closes #731
* Removed _pids/logs_ creation from express(1)
2.4.0 / 2011-06-28
==================
* Added chainable `res.status(code)`
* Added `res.json()`, an explicit version of `res.send(obj)`
* Added simple web-service example
2.3.12 / 2011-06-22
==================
+4 -6
Ver Arquivo
@@ -11,7 +11,7 @@ var fs = require('fs')
* Framework version.
*/
var version = '2.3.12';
var version = '2.4.4';
/**
* Add session support.
@@ -143,10 +143,10 @@ var sass = [
var stylus = [
'body'
, ' padding 50px'
, ' font 14px "Lucida Grande", Helvetica, Arial, sans-serif'
, ' padding: 50px'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif'
, 'a'
, ' color #00B7FF'
, ' color: #00B7FF'
].join('\n');
/**
@@ -260,8 +260,6 @@ while (args.length) {
function createApplicationAt(path) {
mkdir(path, function(){
mkdir(path + '/pids');
mkdir(path + '/logs');
mkdir(path + '/public/javascripts');
mkdir(path + '/public/images');
mkdir(path + '/public/stylesheets', function(){
+13
Ver Arquivo
@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;
+13
Ver Arquivo
@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;
+13
Ver Arquivo
@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;
+52 -5
Ver Arquivo
@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;
@@ -215,6 +228,7 @@
<li><a href="#res.sendfile()">sendfile()</a></li>
<li><a href="#res.download()">download()</a></li>
<li><a href="#res.send()">send()</a></li>
<li><a href="#res.json()">json()</a></li>
<li><a href="#res.redirect()">redirect()</a></li>
<li><a href="#res.cookie()">cookie()</a></li>
<li><a href="#res.clearcookie()">clearCookie()</a></li>
@@ -333,6 +347,13 @@ app.configure('production', function(){
});
</code></pre>
<p>For similar environments you may also pass several env strings:</p>
<pre><code>app.configure('stage', 'prod', function(){
// config
});
</code></pre>
<p>For internal and arbitrary settings Express provides the <em>set(key[, val])</em>, <em>enable(key)</em>, <em>disable(key)</em> methods:</p>
<pre><code> app.configure(function(){
@@ -363,12 +384,14 @@ app.configure('production', function(){
<p>Express supports the following settings out of the box:</p>
<ul>
<li><em>home</em> Application base path used for <em>res.redirect()</em> and transparently handling mounted apps.</li>
<li><em>basepath</em> Application base path used for <em>res.redirect()</em> and transparently handling mounted apps.</li>
<li><em>views</em> Root views directory defaulting to <strong>CWD/views</strong></li>
<li><em>view engine</em> Default view engine name for views rendered without extensions</li>
<li><em>view options</em> An object specifying global view options</li>
<li><em>view cache</em> Enable view caching (enabled in production)</li>
<li><em>case sensitive routes</em> Enable case-sensitive routing</li>
<li><em>strict routing</em> When enabled trailing slashes are no longer ignored</li>
<li><em>jsonp callback</em> Enable <em>res.send()</em> / <em>res.json()</em> transparent jsonp support</li>
</ul>
@@ -1266,6 +1289,19 @@ it will not be set again.</p>
<p>Note that this method <em>end()</em>s the response, so you will want to use node&rsquo;s <em>res.write()</em> for multiple writes or streaming.</p>
<h3 id="res.json()">res.json(obj[, headers|status[, status]])</h3>
<p> Send a JSON response with optional <em>headers</em> and <em>status</em>. This method
is ideal for JSON-only APIs, however <em>res.send(obj)</em> will send JSON as
well, though not ideal for cases when you want to send for example a string
as JSON, since the default for <em>res.send(string)</em> is text/html.</p>
<pre><code>res.json(null);
res.json({ user: 'tj' });
res.json('oh noes!', 500);
res.json('I dont have that', 404);
</code></pre>
<h3 id="res.redirect()">res.redirect(url[, status])</h3>
<p>Redirect to the given <em>url</em> with a default response <em>status</em> of 302.</p>
@@ -1279,11 +1315,12 @@ res.redirect('back');
<p>Express supports &ldquo;redirect mapping&rdquo;, which by default provides <em>home</em>, and <em>back</em>.
The <em>back</em> map checks the <em>Referrer</em> and <em>Referer</em> headers, while <em>home</em> utilizes
the &ldquo;home&rdquo; setting and defaults to &ldquo;/&rdquo;.</p>
the &ldquo;basepath&rdquo; setting and defaults to &ldquo;/&rdquo;.</p>
<h3 id="res.cookie()">res.cookie(name, val[, options])</h3>
<p>Sets the given cookie <em>name</em> to <em>val</em>, with options <em>httpOnly</em>, <em>secure</em>, <em>expires</em> etc.</p>
<p>Sets the given cookie <em>name</em> to <em>val</em>, with options <em>httpOnly</em>, <em>secure</em>, <em>expires</em> etc. The <em>path</em> option defaults to the app&rsquo;s &ldquo;basepath&rdquo; setting, which
is typically &ldquo;/&rdquo;.</p>
<pre><code>// "Remember me" for 15 minutes
res.cookie('rememberme', 'yes', { expires: new Date(Date.now() + 900000), httpOnly: true });
@@ -1305,7 +1342,8 @@ app.get('/', function(req, res){
<h3 id="res.clearcookie()">res.clearCookie(name[, options])</h3>
<p>Clear cookie <em>name</em> by setting &ldquo;expires&rdquo; far in the past.</p>
<p>Clear cookie <em>name</em> by setting &ldquo;expires&rdquo; far in the past. Much like
<em>res.cookie()</em> the <em>path</em> option also defaults to the &ldquo;basepath&rdquo; setting.</p>
<pre><code>res.clearCookie('rememberme');
</code></pre>
@@ -1322,6 +1360,16 @@ automatically, however otherwise a response of <em>200</em> and <em>text/html</e
res.render('index', { layout: false, user: user });
</code></pre>
<p>This <em>options</em> object is also considered an &ldquo;options&rdquo; object. For example
when you pass the <em>status</em> local, it&rsquo;s not only available to the view, it
sets the response status to this number. This is also useful if a template
engine accepts specific options, such as <em>debug</em>, or <em>compress</em>. Below
is an example of how one might render an error page, passing the <em>status</em> for
display, as well as it setting <em>res.statusCode</em>.</p>
<pre><code> res.render('error', { status: 500, message: 'Internal Server Error' });
</code></pre>
<h3 id="res.partial()">res.partial(view[, options])</h3>
<p>Render <em>view</em> partial with the given <em>options</em>. This method is always available
@@ -1594,7 +1642,6 @@ as well as the <em>name()</em> function exposed.</p>
<p>Express also provides a few locals by default:</p>
<pre><code>- `settings` the app's settings object
- `filename` the view's filename
- `layout(path)` specify the layout from within a view
</code></pre>
+35 -5
Ver Arquivo
@@ -71,6 +71,12 @@ otherwise the first call to _app.get()_, _app.post()_, etc will mount the routes
app.use(express.errorHandler());
});
For similar environments you may also pass several env strings:
app.configure('stage', 'prod', function(){
// config
});
For internal and arbitrary settings Express provides the _set(key[, val])_, _enable(key)_, _disable(key)_ methods:
app.configure(function(){
@@ -98,12 +104,14 @@ This is _very_ important, as many caching mechanisms are _only enabled_ when in
Express supports the following settings out of the box:
* _home_ Application base path used for _res.redirect()_ and transparently handling mounted apps.
* _basepath_ 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
* _view cache_ Enable view caching (enabled in production)
* _case sensitive routes_ Enable case-sensitive routing
* _strict routing_ When enabled trailing slashes are no longer ignored
* _jsonp callback_ Enable _res.send()_ / _res.json()_ transparent jsonp support
### Routing
@@ -915,6 +923,18 @@ 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.json(obj[, headers|status[, status]])
Send a JSON response with optional _headers_ and _status_. This method
is ideal for JSON-only APIs, however _res.send(obj)_ will send JSON as
well, though not ideal for cases when you want to send for example a string
as JSON, since the default for _res.send(string)_ is text/html.
res.json(null);
res.json({ user: 'tj' });
res.json('oh noes!', 500);
res.json('I dont have that', 404);
### res.redirect(url[, status])
Redirect to the given _url_ with a default response _status_ of 302.
@@ -927,11 +947,12 @@ Redirect to the given _url_ with a default response _status_ of 302.
Express supports "redirect mapping", which by default provides _home_, and _back_.
The _back_ map checks the _Referrer_ and _Referer_ headers, while _home_ utilizes
the "home" setting and defaults to "/".
the "basepath" 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 _httpOnly_, _secure_, _expires_ etc. The _path_ option defaults to the app's "basepath" setting, which
is typically "/".
// "Remember me" for 15 minutes
res.cookie('rememberme', 'yes', { expires: new Date(Date.now() + 900000), httpOnly: true });
@@ -950,7 +971,8 @@ To parse incoming _Cookie_ headers, use the _cookieParser_ middleware, which pro
### res.clearCookie(name[, options])
Clear cookie _name_ by setting "expires" far in the past.
Clear cookie _name_ by setting "expires" far in the past. Much like
_res.cookie()_ the _path_ option also defaults to the "basepath" setting.
res.clearCookie('rememberme');
@@ -965,6 +987,15 @@ The _options_ passed are the local variables as well, for example if we want to
var user = { name: 'tj' };
res.render('index', { layout: false, user: user });
This _options_ object is also considered an "options" object. For example
when you pass the _status_ local, it's not only available to the view, it
sets the response status to this number. This is also useful if a template
engine accepts specific options, such as _debug_, or _compress_. Below
is an example of how one might render an error page, passing the _status_ for
display, as well as it setting _res.statusCode_.
res.render('error', { status: 500, message: 'Internal Server Error' });
### res.partial(view[, options])
Render _view_ partial with the given _options_. This method is always available
@@ -1206,7 +1237,6 @@ as well as the _name()_ function exposed.
Express also provides a few locals by default:
- `settings` the app's settings object
- `filename` the view's filename
- `layout(path)` specify the layout from within a view
This method is aliased as _app.locals()_.
+14 -5
Ver Arquivo
@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;
@@ -233,7 +246,7 @@ app.listen(3000);
<h2>Third-Party Modules</h2>
<p>The following modules compliment or extend Express directly:</p>
<p>The following modules complement or extend Express directly:</p>
<ul>
<li><a href="http://github.com/visionmedia/express-resource">express-resource</a> provides resourceful routing</li>
@@ -254,10 +267,6 @@ app.listen(3000);
<li><a href="http://groups.google.com/group/express-js">Google Group</a> for discussion</li>
<li>Visit the <a href="http://github.com/visionmedia/express/wiki">Wiki</a></li>
<li><a href="http://hideyukisaito.com/doc/expressjs/">日本語ドキュメンテーション</a> by <a href="https://github.com/hideyukisaito">hideyukisaito</a></li>
<li>Screencast &ndash; <a href="http://bit.ly/eRYu0O">Introduction</a></li>
<li>Screencast &ndash; <a href="http://bit.ly/dU13Fx">View Partials</a></li>
<li>Screencast &ndash; <a href="http://bit.ly/hX4IaH">Route Specific Middleware</a></li>
<li>Screencast &ndash; <a href="http://bit.ly/eNqmVs">Route Path Placeholder Preconditions</a></li>
</ul>
</div>
+1 -1
Ver Arquivo
@@ -34,7 +34,7 @@ The following are the major contributors of Express (in no specific order).
## Third-Party Modules
The following modules compliment or extend Express directly:
The following modules complement 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
+13
Ver Arquivo
@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;
+13
Ver Arquivo
@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;
+13
Ver Arquivo
@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;
+5 -6
Ver Arquivo
@@ -35,16 +35,15 @@ var users = {
tj: {
name: 'tj'
, salt: 'randomly-generated-salt'
, pass: md5('foobar' + 'randomly-generated-salt')
, pass: hash('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');
function hash(msg, key) {
return crypto.createHmac('sha256', key).update(msg).digest('hex');
}
// Authenticate using our plain-object database of doom!
function authenticate(name, pass, fn) {
@@ -52,9 +51,9 @@ function authenticate(name, pass, fn) {
// 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
// the hash against the pass / salt, if there is a match we
// found the user
if (user.pass == md5(pass + user.salt)) return fn(null, user);
if (user.pass == hash(pass, user.salt)) return fn(null, user);
// Otherwise password is invalid
fn(new Error('invalid password'));
}
+36 -49
Ver Arquivo
@@ -9,13 +9,15 @@ require.paths.unshift(__dirname + '/../../support');
var express = require('../../lib/express');
var app = express.createServer();
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
// Serve default connect favicon
app.use(express.favicon());
// Logger is placed below favicon, so favicon.ico
// requests will not be logged
app.use(express.logger({ format: '":method :url" :status' }));
app.use(express.logger('":method :url" :status'));
// "app.router" positions our routes
// specifically above the middleware
@@ -23,59 +25,38 @@ app.use(express.logger({ format: '":method :url" :status' }));
app.use(app.router);
// When no more middleware require execution, aka
// our router is finished and did not respond, we
// can assume that it is "not found". Instead of
// letting Connect deal with this, we define our
// custom middleware here to simply pass a NotFound
// exception
// Since this is the last non-error-handling
// middleware use()d, we assume 404, as nothing else
// responded.
app.use(function(req, res, next){
next(new NotFound(req.url));
// the status option, or res.statusCode = 404
// are equivalent, however with the option we
// get the "status" local available as well
res.render('404', { status: 404, url: req.url });
});
app.set('views', __dirname + '/views');
// error-handling middleware, take the same form
// as regular middleware, however they require an
// arity of 4, aka the signature (err, req, res, next).
// when connect has an error, it will invoke ONLY error-handling
// middleware.
// Provide our app with the notion of NotFound exceptions
// If we were to next() here any remaining non-error-handling
// middleware would then be executed, or if we next(err) to
// continue passing the error, only error-handling middleware
// would remain being executed, however here
// we simply respond with an error page.
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);
}
/**
* Inherit from `Error.prototype`.
*/
NotFound.prototype.__proto__ = Error.prototype;
// We can call app.error() several times as shown below.
// Here we check for an instanceof NotFound and show the
// 404 page, or we pass on to the next error handler.
// These handlers could potentially be defined within
// configure() blocks to provide introspection when
// 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);
}
});
// 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 });
app.use(function(err, req, res, next){
// we may use properties of the error object
// here and next(err) appropriately, or if
// we possibly recovered from the error, simply next().
res.render('500', {
status: err.status || 500
, error: err
});
});
// Routes
@@ -84,8 +65,14 @@ app.get('/', function(req, res){
res.render('index.jade');
});
app.get('/404', function(req, res){
throw new NotFound(req.url);
app.get('/404', function(req, res, next){
next();
});
app.get('/403', function(req, res, next){
var err = new Error('not allowed!');
err.status = 403;
next(err);
});
app.get('/500', function(req, res, next){
+1 -4
Ver Arquivo
@@ -1,4 +1 @@
- if (error.path)
h2 Cannot find #{error.path}
- else
h2 Page Not Found
h2 Cannot find #{url}
+4 -1
Ver Arquivo
@@ -5,4 +5,7 @@ ul
a(href="/500") 500
li
| visit
a(href="/404") 404
a(href="/404") 404
li
| visit
a(href='/403') 403
+66
Ver Arquivo
@@ -0,0 +1,66 @@
/**
* Module dependencies.
*/
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' }
];
// dynamic helpers are simply functions that are invoked
// per request (once), passed both the request and response
// objects. These can be used for request-specific
// details within a view, such telling the layout which
// scripts to include.
app.dynamicHelpers({
// by simply returning an object here
// we can set it's properties such as "page.title"
// within a view, and it remains specific to that request,
// so it would be valid to do:
// page.title = user.name + "'s account"
page: function() {
return {};
},
// the scripts array here is assigned once,
// so by returning a closure, we can use script(path)
// in a template, instead of something like
// scripts.push(path).
script: function(req){
req._scripts = [];
return function(path){
req._scripts.push(path);
}
},
// to expose our scripts array for iteration within
// our views (typically the layout), we simply return it
// here, and since composite types are mutable, it will
// contain all of the paths pushed with the helper above.
scripts: function(req){
return req._scripts;
}
});
app.get('/', function(req, res){
res.render('users', { users: users });
});
app.listen(3000);
console.log('Express app started on port 3000');
@@ -0,0 +1,11 @@
<html>
<head>
<title><%- page.title %></title>
<% for (var i in scripts) { %>
<script src="<%= scripts[i] %>"></script>
<% } %>
</head>
<body>
<%- body %>
</body>
</html>
@@ -0,0 +1,8 @@
<% page.title = 'Users' %>
<% script('/javascripts/jquery.js') %>
<% script('/javascripts/users.js') %>
<h1>Users</h1>
<ul id="users">
<%- partial('user', users) %>
</ul>
@@ -0,0 +1 @@
<li><%= user.name %> &lt;<%= user.email %>&gt;</li>
+3
Ver Arquivo
@@ -0,0 +1,3 @@
head
title Jade Example
link(rel="stylesheet", href="/stylesheets/style.css")
+1 -3
Ver Arquivo
@@ -1,6 +1,4 @@
!!!
html
head
title Jade Example
link(rel="stylesheet", href="/stylesheets/style.css")
include header
body!= body
+17 -12
Ver Arquivo
@@ -5,21 +5,26 @@
var express = require('../../lib/express');
// $ npm install connect-redis
var RedisStore = require('connect-redis');
// pass the express to the connect redis module
// allowing it to inherit from express.session.Store
var RedisStore = require('connect-redis')(express);
var app = express.createServer(
express.logger(),
var app = express.createServer();
// Required by session() middleware
express.cookieParser(),
app.use(express.favicon());
// Populates:
// - req.session
// - req.sessionStore
// - req.sessionID (or req.session.id)
express.session({ secret: 'keyboard cat', store: new RedisStore })
);
// request logging
app.use(express.logger());
// required to parse the session cookie
app.use(express.cookieParser());
// Populates:
// - req.session
// - req.sessionStore
// - req.sessionID (or req.session.id)
app.use(express.session({ secret: 'keyboard cat', store: new RedisStore }));
app.get('/', function(req, res){
var body = '';
+121
Ver Arquivo
@@ -0,0 +1,121 @@
/**
* Module dependencies.
*/
var express = require('../../lib/express');
var app = express.createServer();
// configuration
// if we wanted to supply more than JSON, we could
// use something similar to the content-negotiation
// example.
// here we validate the API key,
// by mounting this middleware to /api/v1
// meaning only paths prefixed with "/api/v1"
// will cause this middleware to be invoked
app.use('/api/v1', function(req, res, next){
var key = req.query['api-key'];
// key isnt present
if (!key) return next(new Error('api key required'));
// key is invalid
if (!~apiKeys.indexOf(key)) return next(new Error('invalid api key'));
// all good, store req.key for route access
req.key = key;
next();
});
// position our routes above the error handling middleware,
// and below our API middleware, since we want the API validation
// to take place BEFORE our routes
app.use(app.router);
// middleware with an arity of 4 are considered
// error handling middleware. When you next(err)
// it will be passed through the defined middleware
// in order, but ONLY those with an arity of 4, ignoring
// regular middleware.
app.use(function(err, req, res, next){
// whatever you want here, feel free to populate
// properties on `err` to treat it differently in here,
// or when you next(err) set res.statusCode= etc.
res.send({ error: err.message }, 500);
});
// our custom JSON 404 middleware. Since it's placed last
// it will be the last middleware called, if all others
// invoke next() and do not respond.
app.use(function(req, res){
res.send({ error: "Lame, can't find that" }, 404);
});
/**
* Generate our unique identifier.
*/
function uid() {
return [
Math.random() * 0xffff | 0
, Math.random() * 0xffff | 0
, Math.random() * 0xffff | 0
, Date.now()
].join('-');
}
// map of valid api keys, typically mapped to
// account info with some sort of database like redis.
// api keys do _not_ serve as authentication, merely to
// track API usage or help prevent malicious behavior etc.
var apiKeys = [uid(), uid(), uid()];
console.log('valid keys:\n ', apiKeys.join('\n '));
// these two objects will serve as our faux database
var repos = [
{ name: 'express', url: 'http://github.com/visionmedia/express' }
, { name: 'stylus', url: 'http://github.com/learnboost/stylus' }
, { name: 'cluster', url: 'http://github.com/learnboost/cluster' }
];
var users = [
{ name: 'tobi' }
, { name: 'loki' }
, { name: 'jane' }
];
var userRepos = {
tobi: [repos[0], repos[1]]
, loki: [repos[1]]
, jane: [repos[2]]
};
// we now can assume the api key is valid,
// and simply expose the data
app.get('/api/v1/users', function(req, res, next){
res.send(users);
});
app.get('/api/v1/repos', function(req, res, next){
res.send(repos);
});
app.get('/api/v1/user/:name/repos', function(req, res, next){
var name = req.params.name
, user = userRepos[name];
if (user) res.send(user);
else next();
});
app.listen(3000);
console.log('Express server listening on port 3000');
+1 -1
Ver Arquivo
@@ -28,7 +28,7 @@ var exports = module.exports = connect.middleware;
* Framework version.
*/
exports.version = '2.3.12';
exports.version = '2.4.5';
/**
* Shortcut for `new Server(...)`.
+72 -13
Ver Arquivo
@@ -66,7 +66,6 @@ app.init = function(middleware){
this.dynamicViewHelpers = {};
this.errorHandlers = [];
this.set('home', '/');
this.set('env', process.env.NODE_ENV || 'development');
// expose objects to each other
@@ -208,7 +207,7 @@ app.registerErrorHandlers = function(){
*/
app.use = function(route, middleware){
var app, home, handle;
var app, base, handle;
if ('string' != typeof route) {
middleware = route, route = '/';
@@ -234,9 +233,10 @@ app.use = function(route, middleware){
// mounted an app, invoke the hook
// and adjust some settings
if (app) {
home = app.set('home');
if ('/' == home) home = '';
app.set('home', app.route + home);
base = this.set('basepath') || this.route;
if ('/' == base) base = '';
base = base + (app.set('basepath') || app.route);
app.set('basepath', base);
app.parent = this;
if (app.__mounted) app.__mounted.call(app, this);
}
@@ -312,7 +312,7 @@ app.dynamicHelpers = function(obj){
};
/**
* Map the given param placeholder `name`(s) to the given callback `fn`.
* Map the given param placeholder `name`(s) to the given callback(s).
*
* Param mapping is used to provide pre-conditions to routes
* which us normalized placeholders. This callback has the same
@@ -332,6 +332,38 @@ app.dynamicHelpers = function(obj){
* });
* });
*
* Passing a single function allows you to map logic
* to the values passed to `app.param()`, for example
* this is useful to provide coercion support in a concise manner.
*
* The following example maps regular expressions to param values
* ensuring that they match, otherwise passing control to the next
* route:
*
* app.param(function(name, regexp){
* if (regexp instanceof RegExp) {
* return function(req, res, next, val){
* var captures;
* if (captures = regexp.exec(String(val))) {
* req.params[name] = captures;
* next();
* } else {
* next('route');
* }
* }
* }
* });
*
* We can now use it as shown below, where "/commit/:commit" expects
* that the value for ":commit" is at 5 or more digits. The capture
* groups are then available as `req.params.commit` as we defined
* in the function above.
*
* app.param('commit', /^\d{5,}$/);
*
* For more of this useful functionality take a look
* at [express-params](http://github.com/visionmedia/express-params).
*
* @param {String|Array|Function} name
* @param {Function} fn
* @return {Server} for chaining
@@ -339,18 +371,25 @@ app.dynamicHelpers = function(obj){
*/
app.param = function(name, fn){
var self = this
, fns = [].slice.call(arguments, 1);
// array
if (Array.isArray(name)) {
name.forEach(function(name){
this.param(name, fn);
}, this);
fns.forEach(function(fn){
self.param(name, fn);
});
});
// param logic
} else if ('function' == typeof name) {
this.routes.param(name);
// single
} else {
if (':' == name[0]) name = name.substr(1);
this.routes.param(name, fn);
fns.forEach(function(fn){
self.routes.param(name, fn);
});
}
return this;
@@ -471,17 +510,37 @@ app.redirect = function(key, url){
};
/**
* Configure callback for the given `env`.
* Configure callback for zero or more envs,
* when no env is specified that callback will
* be invoked for all environments. Any combination
* can be used multiple times, in any order desired.
*
* @param {String} env
* Examples:
*
* app.configure(function(){
* // executed for all envs
* });
*
* app.configure('stage', function(){
* // executed staging env
* });
*
* app.configure('stage', 'production', function(){
* // executed for stage and production
* });
*
* @param {String} env...
* @param {Function} fn
* @return {Server} for chaining
* @api public
*/
app.configure = function(env, fn){
if ('function' == typeof env) fn = env, env = 'all';
if ('all' == env || env == this.settings.env) fn.call(this);
var envs = 'all'
, args = toArray(arguments);
fn = args.pop();
if (args.length) envs = args;
if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this);
return this;
};
+61 -24
Ver Arquivo
@@ -52,7 +52,7 @@ res.send = function(body, headers, status){
status = status || this.statusCode;
// allow 0 args as 204
if (!arguments.length || undefined === body) body = status = 204;
if (!arguments.length || undefined === body) status = 204;
// determine content type
switch (typeof body) {
@@ -75,22 +75,13 @@ res.send = function(body, headers, status){
this.contentType('.bin');
}
} else {
if (!this.header('Content-Type')) {
this.charset = this.charset || 'utf-8';
this.contentType('.json');
}
body = JSON.stringify(body);
if (this.req.query.callback && this.app.set('jsonp callback')) {
this.charset = this.charset || 'utf-8';
this.header('Content-Type', 'text/javascript');
body = this.req.query.callback.replace(/[^\w$.]/g, '') + '(' + body + ');';
}
return this.json(body, headers, status);
}
break;
}
// populate Content-Length
if (!this.header('Content-Length')) {
if (undefined !== body && !this.header('Content-Length')) {
this.header('Content-Length', Buffer.isBuffer(body)
? body.length
: Buffer.byteLength(body));
@@ -109,11 +100,59 @@ res.send = function(body, headers, status){
if (204 == status || 304 == status) {
this.removeHeader('Content-Type');
this.removeHeader('Content-Length');
body = '';
}
// respond
this.statusCode = status;
this.end('HEAD' == this.req.method ? undefined : body);
return this;
};
/**
* Send JSON response with `obj`, optional `headers`, and optional `status`.
*
* Examples:
*
* res.json(null);
* res.json({ user: 'tj' });
* res.json('oh noes!', 500);
* res.json('I dont have that', 404);
*
* @param {Mixed} obj
* @param {Object|Number} headers or status
* @param {Number} status
* @return {ServerResponse}
* @api public
*/
res.json = function(obj, headers, status){
var body = JSON.stringify(obj)
, callback = this.req.query.callback
, jsonp = this.app.enabled('jsonp callback');
this.charset = this.charset || 'utf-8';
this.header('Content-Type', 'application/json');
if (callback && jsonp) {
this.header('Content-Type', 'text/javascript');
body = callback.replace(/[^\w$.]/g, '') + '(' + body + ');';
}
return this.send(body, headers, status);
};
/**
* Set status `code`.
*
* @param {Number} code
* @return {ServerResponse}
* @api public
*/
res.status = function(code){
this.statusCode = code;
return this;
};
/**
@@ -232,17 +271,14 @@ res.download = function(path, filename, fn, fn2){
*
* @param {String} name
* @param {String} val
* @return {String}
* @return {ServerResponse} for chaining
* @api public
*/
res.header = function(name, val){
if (val === undefined) {
return this.getHeader(name);
} else {
this.setHeader(name, val);
return val;
}
if (1 == arguments.length) return this.getHeader(name);
this.setHeader(name, val);
return this;
};
/**
@@ -266,6 +302,7 @@ res.clearCookie = function(name, options){
* Options:
*
* - `maxAge` max-age in milliseconds, converted to `expires`
* - `path` defaults to the "basepath" setting which is typically "/"
*
* Examples:
*
@@ -284,6 +321,7 @@ res.clearCookie = function(name, options){
res.cookie = function(name, val, options){
options = options || {};
if ('maxAge' in options) options.expires = new Date(Date.now() + options.maxAge);
if (undefined === options.path) options.path = this.app.set('basepath');
var cookie = utils.serializeCookie(name, val, options);
this.header('Set-Cookie', cookie);
};
@@ -295,8 +333,8 @@ res.cookie = function(name, val, options){
* The given `url` can also be the name of a mapped url, for
* example by default express supports "back" which redirects
* to the _Referrer_ or _Referer_ headers or the application's
* "home" setting. Express also supports "home" out of the box,
* which can be set via `app.set('home', '/blog');`, and defaults
* "basepath" setting. Express also supports "basepath" out of the box,
* which can be set via `app.set('basepath', '/blog');`, and defaults
* to '/'.
*
* Redirect Mapping:
@@ -336,7 +374,7 @@ res.cookie = function(name, val, options){
res.redirect = function(url, status){
var app = this.app
, req = this.req
, base = app.set('home') || '/'
, base = app.set('basepath') || app.route
, status = status || 302
, body;
@@ -360,14 +398,13 @@ res.redirect = function(url, status){
// Relative
if (!~url.indexOf('://')) {
// Respect mount-point
if (app.route) url = join(app.route, url);
if ('/' != base && 0 != url.indexOf(base)) url = base + url;
// Absolute
var host = req.headers.host
, tls = req.connection.encrypted;
url = 'http' + (tls ? 's' : '') + '://' + host + url;
}
// Support text/{plain,html} by default
if (req.accepts('html')) {
+69 -55
Ver Arquivo
@@ -79,10 +79,23 @@ Router.prototype.param = function(name, fn){
throw new Error('invalid param() call for ' + name + ', got ' + fn);
}
this.params[name] = fn;
(this.params[name] = this.params[name] || []).push(fn);
return this;
};
/**
* Return a `Collection` of all routes defined.
*
* @return {Collection}
* @api public
*/
Router.prototype.all = function(){
return this.find(function(){
return true;
});
};
/**
* Remove the given `route`, returns
* a bool indicating if the route was present
@@ -182,14 +195,18 @@ Router.prototype._dispatch = function(req, res, next){
, self = this;
// route dispatch
(function pass(i){
var route
(function pass(i, err){
var paramCallbacks
, paramIndex = 0
, paramVal
, route
, keys
, key
, ret;
// match next route
function nextRoute() {
pass(req._route_index + 1);
function nextRoute(err) {
pass(req._route_index + 1, err);
}
// match route
@@ -199,7 +216,7 @@ Router.prototype._dispatch = function(req, res, next){
if (!route && 'OPTIONS' == req.method) return self._options(req, res);
// no route
if (!route) return next();
if (!route) return next(err);
// we have a route
// start at param 0
@@ -207,50 +224,57 @@ Router.prototype._dispatch = function(req, res, next){
keys = route.keys;
i = 0;
(function param(err) {
var key = keys[i++]
, val = key && req.params[key.name]
, fn = key && params[key.name]
, ret;
// param callbacks
function param(err) {
key = keys[i++];
paramVal = key && req.params[key.name];
paramCallbacks = key && params[key.name];
try {
if ('route' == err) {
nextRoute();
} else if (err) {
next(err);
} else if (fn && undefined !== val) {
fn(req, res, param, val);
i = 0;
callbacks(err);
} else if (paramCallbacks && undefined !== paramVal) {
paramCallback();
} else if (key) {
param();
} else {
i = 0;
middleware();
callbacks();
}
} catch (err) {
next(err);
}
})();
// invoke route middleware
function middleware(err) {
var fn = route.middleware[i++];
if ('route' == err) {
nextRoute();
} else if (err) {
next(err);
} else if (fn) {
fn(req, res, middleware);
} else {
done();
param(err);
}
};
// invoke middleware callback
function done() {
route.callback.call(self, req, res, function(err){
if (err) return next(err);
pass(req._route_index + 1);
});
param(err);
// single param callbacks
function paramCallback(err) {
var fn = paramCallbacks[paramIndex++];
if (err || !fn) return param(err);
fn(req, res, paramCallback, paramVal, key.name);
}
// invoke route callbacks
function callbacks(err) {
var fn = route.callbacks[i++];
try {
if ('route' == err) {
nextRoute();
} else if (err && fn) {
if (fn.length < 4) return callbacks(err);
fn(err, req, res, callbacks);
} else if (fn) {
fn(req, res, callbacks);
} else {
nextRoute(err);
}
} catch (err) {
callbacks(err);
}
}
})(0);
};
@@ -344,36 +368,26 @@ Router.prototype._match = function(req, i){
};
/**
* Route `method`, `path`, and optional middleware
* to the callback `fn`.
* Route `method`, `path`, and one or more callbacks.
*
* @param {String} method
* @param {String} path
* @param {Function} ...
* @param {Function} fn
* @param {Function} callback...
* @return {Router} for chaining
* @api private
*/
Router.prototype._route = function(method, path, fn){
Router.prototype._route = function(method, path, callbacks){
var app = this.app
, middleware = [];
, callbacks = utils.flatten(toArray(arguments, 2));
// slice middleware
if (arguments.length > 3) {
middleware = toArray(arguments, 2);
fn = middleware.pop();
middleware = utils.flatten(middleware);
}
// ensure path and callback are given
if (!path) throw new Error(method + 'route requires a path');
if (!fn) throw new Error(method + ' route ' + path + ' requires a callback');
// ensure path was given
if (!path) throw new Error('app.' + method + '() requires a path');
// create the route
var route = new Route(method, path, fn, {
var route = new Route(method, path, callbacks, {
sensitive: app.enabled('case sensitive routes')
, middleware: middleware
, strict: app.enabled('strict routing')
});
// add it
+17 -14
Ver Arquivo
@@ -13,27 +13,29 @@ module.exports = Route;
/**
* Initialize `Route` with the given HTTP `method`, `path`,
* and callback `fn` and `options`.
* and an array of `callbacks` and `options`.
*
* Options:
*
* - `sensitive` enable case-sensitive routes
* - `middleware` array of middleware
* - `sensitive` enable case-sensitive routes
* - `strict` enable strict matching for trailing slashes
*
* @param {String} method
* @param {String} path
* @param {Function} fn
* @param {Array} callbacks
* @param {Object} options.
* @api private
*/
function Route(method, path, fn, options) {
function Route(method, path, callbacks, options) {
options = options || {};
this.callback = fn;
this.path = path;
this.method = method;
this.regexp = normalize(path, this.keys = [], options.sensitive);
this.middleware = options.middleware;
this.callbacks = callbacks;
this.regexp = normalize(path
, this.keys = []
, options.sensitive
, options.strict);
}
/**
@@ -60,14 +62,15 @@ Route.prototype.match = function(path){
* @param {String|RegExp} path
* @param {Array} keys
* @param {Boolean} sensitive
* @param {Boolean} strict
* @return {RegExp}
* @api private
*/
function normalize(path, keys, sensitive) {
if (path instanceof RegExp) return path;
function normalize(path, keys, sensitive, strict) {
if (path instanceof RegExp) return path;
path = path
.concat('/?')
.concat(strict ? '' : '/?')
.replace(/\/\(/g, '(?:/')
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){
keys.push({ name: key, optional: !! optional });
@@ -76,10 +79,10 @@ function normalize(path, keys, sensitive) {
+ (optional ? '' : slash)
+ '(?:'
+ (optional ? slash : '')
+ (format || '') + (capture || '([^/]+?)') + ')'
+ (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
+ (optional || '');
})
.replace(/([\/.])/g, '\\$1')
.replace(/\*/g, '(.+)');
.replace(/\*/g, '(.*)');
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
}
}
+2 -5
Ver Arquivo
@@ -61,6 +61,7 @@ exports.compile = function(view, cache, cid, options){
}
// compile
options.filename = view.path;
view.fn = view.templateEngine.compile(view.contents, options);
cache[cid] = view;
@@ -178,8 +179,6 @@ function renderPartial(res, view, options, parentLocals, parent){
options[name] = object;
} else if (name === global) {
merge(options, object);
} else {
options.scope = object;
}
}
return res.render(view, options, null, parent, true);
@@ -318,8 +317,7 @@ res.render = function(view, opts, fn, parent, sub){
// callback given
if (fn) {
fn(err);
// unwind to root call to prevent
// several next(err) calls
// unwind to root call to prevent multiple callbacks
} else if (sub) {
throw err;
// root template, next(err)
@@ -414,7 +412,6 @@ res._render = function(view, opts, fn, parent, sub){
// View lookup
options.hint = app.enabled('hints');
view = exports.compile(view, app.cache, cid, options);
options.filename = view.path;
// layout helper
options.layout = function(path){
+3 -3
Ver Arquivo
@@ -1,7 +1,7 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "2.3.12",
"version": "2.4.5",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
{ "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" },
@@ -10,9 +10,9 @@
{ "name": "Guillermo Rauch", "email": "rauchg@gmail.com" }
],
"dependencies": {
"connect": ">= 1.5.1 < 2.0.0",
"connect": ">= 1.5.2 < 2.0.0",
"mime": ">= 0.0.1",
"qs": ">= 0.0.6"
"qs": ">= 0.3.1"
},
"devDependencies": {
"connect-form": "0.2.1",
+34 -8
Ver Arquivo
@@ -236,7 +236,6 @@ module.exports = {
var server = express.createServer();
server.set('env', 'development');
// Config blocks
var ret = server.configure(function(){
assert.equal(this, server, 'Test context of configure() is the server');
calls.push('any');
@@ -297,7 +296,23 @@ module.exports = {
{ url: '/' },
{ body: 'first route last' });
},
'test #configure() multiple envs': function(){
var app = express.createServer();
app.set('env', 'prod');
var calls = [];
app.configure('stage', 'prod', function(){
calls.push('stage/prod');
});
app.configure('prod', function(){
calls.push('prod');
});
calls.should.eql(['stage/prod', 'prod']);
},
'test #set()': function(){
var app = express.createServer();
var ret = app.set('title', 'My App').set('something', 'else');
@@ -352,15 +367,27 @@ module.exports = {
{ url: '/', method: 'POST', data: 'name=tj', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }},
{ body: '{"name":"tj"}' });
},
'test "basepath" setting': function(){
var app = express.createServer();
app.set('basepath', '/shop');
app.get('/redirect', function(req, res){
res.redirect('/cart');
});
assert.response(app,
{ url: '/redirect', headers: { Host: 'foo.com' }},
{ headers: { Location: 'http://foo.com/shop/cart' }});
},
'test mounting': function(){
var called
, app = express.createServer()
, blog = express.createServer()
, map = express.createServer()
, reg = connect.createServer();
map.set('home', '/map');
map.mounted(function(parent){
called = true;
@@ -381,9 +408,8 @@ module.exports = {
blog.set('test').should.equal('parent setting');
app.get('/', function(req, res){
app.set('home').should.equal('/');
blog.set('home').should.equal('/blog');
map.set('home').should.equal('/contact/map');
blog.set('basepath').should.equal('/blog');
map.set('basepath').should.equal('/contact');
res.send('main app');
});
+1 -1
Ver Arquivo
@@ -3,4 +3,4 @@
- else if (lastInCollection)
li.last= word
- else
li(class: 'word-' + indexInCollection)= word
li(class='word-' + indexInCollection)= word
-1
Ver Arquivo
@@ -1 +0,0 @@
p #{label} #{this.name}
+227 -64
Ver Arquivo
@@ -9,6 +9,57 @@ var express = require('express')
, should = require('should');
module.exports = {
'test #json()': function(){
var app = express.createServer()
, json = 'application/json; charset=utf-8';
app.get('/user', function(req, res, next){
res.json({ name: 'tj' });
});
app.get('/string', function(req, res, next){
res.json('whoop!');
});
app.get('/error', function(req, res, next){
res.json('oh noes!', 500);
});
app.get('/headers', function(req, res, next){
res.json(undefined, { 'X-Foo': 'bar' }, 302);
});
assert.response(app,
{ url: '/error' },
{ body: '"oh noes!"'
, status: 500
, headers: { 'Content-Type': json }});
assert.response(app,
{ url: '/string' },
{ body: '"whoop!"'
, headers: {
'Content-Type': json
, 'Content-Length': 8
}});
assert.response(app,
{ url: '/user' },
{ body: '{"name":"tj"}', headers: { 'Content-Type': json }});
},
'test #status()': function(){
var app = express.createServer();
app.get('/error', function(req, res, next){
res.status(500).send('OH NO');
});
assert.response(app,
{ url: '/error' },
{ body: 'OH NO', status: 500 });
},
'test #send()': function(){
var app = express.createServer();
@@ -17,15 +68,8 @@ module.exports = {
});
app.get('/json', function(req, res){
res.header('X-Foo', 'bar');
res.send({ foo: 'bar' }, { 'X-Foo': 'baz' }, 201);
});
app.get('/jsonp', function(req, res){
app.enable('jsonp callback');
res.header('X-Foo', 'bar');
res.send({ foo: 'bar' }, { 'X-Foo': 'baz' }, 201);
app.disable('jsonp callback');
res.header('X-Foo', 'bar')
.send({ foo: 'bar' }, { 'X-Foo': 'baz' }, 201);
});
app.get('/text', function(req, res){
@@ -53,7 +97,11 @@ module.exports = {
app.get('/noargs', function(req, res, next){
res.send();
});
app.get('/no-content', function(req, res, next){
res.send(204);
});
app.get('/undefined', function(req, res, next){
res.send(undefined);
});
@@ -84,41 +132,6 @@ module.exports = {
, 'X-Foo': 'baz'
}});
assert.response(app,
{ url: '/jsonp?callback=test' },
{ body: 'test({"foo":"bar"});'
, status: 201
, headers: {
'Content-Type': 'text/javascript; charset=utf-8'
, 'X-Foo': 'baz'
}});
assert.response(app,
{ url: '/jsonp?callback=baz' },
{ body: 'baz({"foo":"bar"});'
, status: 201, headers: {
'Content-Type': 'text/javascript; charset=utf-8'
, 'X-Foo': 'baz'
}});
assert.response(app,
{ url: '/jsonp?callback=invalid()[]' },
{ body: 'invalid({"foo":"bar"});'
, status: 201
, headers: {
'Content-Type': 'text/javascript; charset=utf-8'
, 'X-Foo': 'baz'
}});
assert.response(app,
{ url: '/json?callback=test' },
{ body: '{"foo":"bar"}'
, status: 201
, headers: {
'Content-Type': 'application/json; charset=utf-8'
, 'X-Foo': 'baz'
}});
assert.response(app,
{ url: '/text' },
{ body: 'wahoo'
@@ -153,6 +166,13 @@ module.exports = {
'Content-Type': 'application/octet-stream'
, 'Content-Length': '6'
}});
assert.response(app,
{ url: '/no-content' },
{ status: 204 }, function(res){
assert.equal(undefined, res.headers['content-type']);
assert.equal(undefined, res.headers['content-length']);
});
assert.response(app,
{ url: '/noargs' },
@@ -167,8 +187,91 @@ module.exports = {
assert.equal(undefined, res.headers['content-type']);
assert.equal(undefined, res.headers['content-length']);
});
assert.response(app,
{ url: '/json?callback=test' },
{ body: '{"foo":"bar"}'
, status: 201
, headers: {
'Content-Type': 'application/json; charset=utf-8'
, 'X-Foo': 'baz'
}});
},
'test #send() JSONP': function(){
var app = express.createServer();
app.enable('jsonp callback');
app.get('/jsonp', function(req, res){
res.header('X-Foo', 'bar');
res.send({ foo: 'bar' }, { 'X-Foo': 'baz' }, 201);
});
assert.response(app,
{ url: '/jsonp?callback=test' },
{ body: 'test({"foo":"bar"});'
, status: 201
, headers: {
'Content-Type': 'text/javascript; charset=utf-8'
, 'X-Foo': 'baz'
}});
assert.response(app,
{ url: '/jsonp?callback=baz' },
{ body: 'baz({"foo":"bar"});'
, status: 201, headers: {
'Content-Type': 'text/javascript; charset=utf-8'
, 'X-Foo': 'baz'
}});
assert.response(app,
{ url: '/jsonp?callback=invalid()[]' },
{ body: 'invalid({"foo":"bar"});'
, status: 201
, headers: {
'Content-Type': 'text/javascript; charset=utf-8'
, 'X-Foo': 'baz'
}});
},
'test #json() JSONP': function(){
var app = express.createServer();
app.enable('jsonp callback');
app.get('/jsonp', function(req, res){
res.header('X-Foo', 'bar');
res.json({ foo: 'bar' }, { 'X-Foo': 'baz' }, 201);
});
assert.response(app,
{ url: '/jsonp?callback=test' },
{ body: 'test({"foo":"bar"});'
, status: 201
, headers: {
'Content-Type': 'text/javascript; charset=utf-8'
, 'X-Foo': 'baz'
}});
assert.response(app,
{ url: '/jsonp?callback=baz' },
{ body: 'baz({"foo":"bar"});'
, status: 201, headers: {
'Content-Type': 'text/javascript; charset=utf-8'
, 'X-Foo': 'baz'
}});
assert.response(app,
{ url: '/jsonp?callback=invalid()[]' },
{ body: 'invalid({"foo":"bar"});'
, status: 201
, headers: {
'Content-Type': 'text/javascript; charset=utf-8'
, 'X-Foo': 'baz'
}});
},
'test #contentType()': function(){
var app = express.createServer();
@@ -229,13 +332,13 @@ module.exports = {
var app = express.createServer()
, app2 = express.createServer();
app2.set('home', '/blog');
app2.set('basepath', '/blog');
app2.redirect('google', 'http://google.com');
app2.redirect('blog', function(req, res){
return req.params.id
? '/user/' + req.params.id + '/blog'
? '/user/' + req.params.id + '/posts'
: null;
});
@@ -279,56 +382,56 @@ module.exports = {
assert.response(app,
{ url: '/html', headers: { Accept: 'text/html,text/plain', Host: 'foo.com' }},
{ body: '<p>Moved Temporarily. Redirecting to <a href="http://google.com">http://google.com</a></p>' });
assert.response(app,
{ url: '/', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Permanently. Redirecting to http://google.com'
, status: 301, headers: { Location: 'http://google.com' }});
assert.response(app,
{ url: '/back', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/'
, status: 302, headers: { Location: 'http://foo.com/', 'Content-Type': 'text/plain' }});
assert.response(app,
{ url: '/back', headers: { Referer: '/foo', Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/foo'
, status: 302, headers: { Location: 'http://foo.com/foo' }});
assert.response(app,
{ url: '/back', headers: { Referrer: '/foo', Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/foo'
, status: 302, headers: { Location: 'http://foo.com/foo' }});
assert.response(app,
{ url: '/home', headers: { Accept: 'text/plain', Host: 'foo.com' } },
{ body: 'Moved Temporarily. Redirecting to http://foo.com/'
, status: 302, headers: { Location: 'http://foo.com/' }});
assert.response(app2,
{ url: '/', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Permanently. Redirecting to http://google.com'
, status: 301, headers: { Location: 'http://google.com' }});
assert.response(app2,
{ url: '/back', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/blog'
, status: 302, headers: { Location: 'http://foo.com/blog' }});
assert.response(app2,
{ url: '/home', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/blog'
, status: 302, headers: { Location: 'http://foo.com/blog' }});
assert.response(app2,
{ url: '/google', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://google.com'
, status: 302, headers: { Location: 'http://google.com' }});
assert.response(app2,
{ url: '/user/12', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/user/12/blog'
, status: 302, headers: { Location: 'http://foo.com/user/12/blog', 'X-Foo': 'bar' }});
{ body: 'Moved Temporarily. Redirecting to http://foo.com/blog/user/12/posts'
, status: 302, headers: { Location: 'http://foo.com/blog/user/12/posts', 'X-Foo': 'bar' }});
},
'test #redirect() when mounted': function(){
@@ -561,15 +664,55 @@ module.exports = {
});
},
'test #cookie()': function(){
'test #cookie() path default': function(){
var app = express.createServer();
app.set('basepath', '/foo');
app.get('/', function(req, res){
res.cookie('rememberme', 'yes', { expires: new Date(1), httpOnly: true });
res.cookie('something', 'else');
res.redirect('/');
});
assert.response(app,
{ url: '/', headers: { Host: 'foo.com' }},
function(res){
res.headers['set-cookie']
.should.eql(['rememberme=yes; path=/foo; expires=Thu, 01 Jan 1970 00:00:00 GMT; httpOnly', 'something=else; path=/foo']);
});
},
'test #cookie() explicit path': function(){
var app = express.createServer();
app.set('/basepath', '/foo');
app.get('/', function(req, res){
res.cookie('rememberme', 'yes', { path: '/', expires: new Date(1), httpOnly: true });
res.cookie('something', 'else', { path: '/' });
res.redirect('/');
});
assert.response(app,
{ url: '/', headers: { Host: 'foo.com' }},
function(res){
res.headers['set-cookie']
.should.eql(['rememberme=yes; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; httpOnly', 'something=else; path=/']);
});
},
'test #cookie() null path': function(){
var app = express.createServer();
app.set('/basepath', '/foo');
app.get('/', function(req, res){
res.cookie('rememberme', 'yes', { path: null, expires: new Date(1), httpOnly: true });
res.cookie('something', 'else', { path: null });
res.redirect('/');
});
assert.response(app,
{ url: '/', headers: { Host: 'foo.com' }},
function(res){
@@ -577,10 +720,30 @@ module.exports = {
.should.eql(['rememberme=yes; expires=Thu, 01 Jan 1970 00:00:00 GMT; httpOnly', 'something=else']);
});
},
'test #clearCookie()': function(){
'test #clearCookie() default path': function(){
var app = express.createServer();
app.set('basepath', '/foo');
app.get('/', function(req, res){
res.clearCookie('rememberme');
res.redirect('/');
});
assert.response(app,
{ url: '/' },
function(res){
res.headers['set-cookie']
.should.eql(['rememberme=; path=/foo; expires=Thu, 01 Jan 1970 00:00:00 GMT']);
});
},
'test #clearCookie() explicit path': function(){
var app = express.createServer();
app.set('basepath', '/bar');
app.get('/', function(req, res){
res.clearCookie('rememberme', { path: '/foo' });
res.redirect('/');
@@ -593,7 +756,7 @@ module.exports = {
.should.eql(['rememberme=; path=/foo; expires=Thu, 01 Jan 1970 00:00:00 GMT']);
});
},
'test HEAD': function(){
var app = express.createServer();
+411 -3
Ver Arquivo
@@ -74,6 +74,245 @@ module.exports = {
});
},
'test app.param() multiple mapping functions': function(){
var app = express.createServer();
app.param(function(name, fn){
if (fn.length < 3) {
return function(req, res, next, val){
val = req.params[name] = fn(val);
if (false === val) {
next('route');
} else {
next();
}
};
}
});
app.param(function(name, range){
if (!~String(range).indexOf('..')) return;
var parts = range.split('..')
, from = parseInt(parts.shift())
, to = parseInt(parts.shift());
return function(req, res, next, val){
if (val < from || val > to) return next('route');
next();
}
});
app.param('user', Number);
app.param('user', '0..5');
app.get('/user/:user', function(req, res){
res.json(req.params.user);
});
assert.response(app,
{ url: '/user/3' },
{ body: '3' });
assert.response(app,
{ url: '/user/6' },
{ status: 404 });
},
'test app.param() name passing': function(){
var app = express.createServer();
app.param(function(name, fn){
if (fn.length < 3) {
return function(req, res, next, val){
val = req.params[name] = fn(val);
if (false === val) {
next('route');
} else {
next();
}
};
}
});
function within(a, b) {
return function(req, res, next, val, name){
if (val < a || val > b) {
return next(new Error(name + ' should be within ' + a + '..' + b));
}
next();
}
}
app.param('user', Number);
app.param('user', within(0, 5));
app.get('/user/:user', function(req, res){
res.json(req.params.user);
});
app.use(function(err, req, res, next){
res.json({ error: err.message });
});
assert.response(app,
{ url: '/user/0' },
{ body: '0' });
assert.response(app,
{ url: '/user/6' },
{ body: '{"error":"user should be within 0..5"}' });
},
'test app.param() multiple callbacks and array of params': function(){
var app = express.createServer();
var users = [{ name: 'tj' }];
var pets = [['tobi', 'loki', 'jane', 'manny', 'luna']];
function loadUser(req, res, next, id) {
req.user = users[id];
next();
}
function loadUserPets(req, res, next, id) {
req.user.pets = pets[id];
next();
}
app.param(['user_id', 'user'], loadUser, loadUserPets);
app.get('/user/:user_id', function(req, res){
res.send(req.user);
});
app.get('/account/:user', function(req, res){
res.send(req.user);
});
assert.response(app,
{ url: '/account/0' },
{ body: '{"name":"tj","pets":["tobi","loki","jane","manny","luna"]}' });
assert.response(app,
{ url: '/user/0' },
{ body: '{"name":"tj","pets":["tobi","loki","jane","manny","luna"]}' });
},
'test app.param() multiple callbacks': function(){
var app = express.createServer();
var users = [{ name: 'tj' }];
var pets = [['tobi', 'loki', 'jane', 'manny', 'luna']];
function loadUser(req, res, next, id) {
req.user = users[id];
next();
}
function loadUserPets(req, res, next, id) {
req.user.pets = pets[id];
next();
}
app.param('user_id', loadUser, loadUserPets);
app.get('/user/:user_id', function(req, res){
res.send(req.user);
});
assert.response(app,
{ url: '/user/0' },
{ body: '{"name":"tj","pets":["tobi","loki","jane","manny","luna"]}' });
},
'test app.param() multiple calls with error': function(){
var app = express.createServer();
var commits = ['foo', 'bar', 'baz'];
app.param('commit', function(req, res, next, id){
req.commit = parseInt(id);
if (isNaN(req.commit)) return next('route');
next();
});
app.param('commit', function(req, res, next, id){
req.commit = commits[req.commit];
next(new Error('failed'));
});
app.get('/commit/:commit', function(req, res){
res.send(req.commit);
});
assert.response(app,
{ url: '/commit/0' },
{ status: 500 });
},
'test app.param() multiple calls': function(){
var app = express.createServer();
var commits = ['foo', 'bar', 'baz'];
app.param('commit', function(req, res, next, id){
req.commit = parseInt(id);
if (isNaN(req.commit)) return next('route');
next();
});
app.param('commit', function(req, res, next, id){
req.commit = commits[req.commit];
next();
});
app.get('/commit/:commit', function(req, res){
res.send(req.commit);
});
assert.response(app,
{ url: '/commit/0' },
{ body: 'foo' });
assert.response(app,
{ url: '/commit/0x01' },
{ body: 'bar' });
assert.response(app,
{ url: '/commit/asdf' },
{ status: 404 });
},
'test app.param(fn)': function(){
var app = express.createServer();
app.param(function(name, fn){
if (fn instanceof RegExp) {
return function(req, res, next, val){
var captures;
if (captures = fn.exec(String(val))) {
req.params[name] = captures[1];
next();
} else {
next('route');
}
}
}
});
app.param('commit', /^(\d+)$/);
app.get('/commit/:commit', function(req, res){
res.send(req.params.commit);
});
assert.response(app,
{ url: '/commit/12' },
{ body: '12' });
assert.response(app,
{ url: '/commit/asdf' },
{ status: 404 });
},
'test precedence': function(){
var app = express.createServer();
@@ -138,6 +377,50 @@ module.exports = {
{ body: 'Cannot GET /user/ab' });
},
'test named capture group after dot': function(){
var app = express.createServer();
app.get('/user/:name.:format?', function(req, res){
res.send(req.params.name + ' - ' + (req.params.format || ''));
});
assert.response(app,
{ url: '/user/foo' },
{ body: 'foo - ' });
assert.response(app,
{ url: '/user/foo.json' },
{ body: 'foo - json' });
assert.response(app,
{ url: '/user/foo.bar.json' },
{ body: 'foo.bar - json' });
},
'test optional * value': function(){
var app = express.createServer();
app.get('/admin*', function(req, res){
res.send(req.params[0]);
});
app.get('/file/*.*', function(req, res){
res.send(req.params[0] + ' - ' + req.params[1]);
});
assert.response(app,
{ url: '/file/some.foo.bar' },
{ body: 'some.foo - bar' });
assert.response(app,
{ url: '/admin', },
{ body: '', status: 200 });
assert.response(app,
{ url: '/adminify', },
{ body: 'ify', status: 200 });
},
'test app.param()': function(){
var app = express.createServer();
@@ -225,7 +508,7 @@ module.exports = {
var route = app.get('/user/:id')[0]
route.should.be.an.instanceof(Route);
route.callback.should.be.a('function');
route.callbacks.should.be.an.instanceof(Array);
route.path.should.equal('/user/:id');
route.regexp.should.be.an.instanceof(RegExp);
route.method.should.equal('get');
@@ -287,7 +570,6 @@ module.exports = {
var route = app.match.get('/user/12')[0];
route.should.be.an.instanceof(Route);
route.callback.should.be.a('function');
route.path.should.equal('/user/:id');
route.regexp.should.be.an.instanceof(RegExp);
route.method.should.equal('get');
@@ -304,6 +586,17 @@ module.exports = {
app.match('/user/123').should.have.length(3);
},
'test app.routes.all()': function(){
var app = express.createServer();
app.get('/user', function(){});
app.get('/user/:id', function(){});
app.get('/user/:id/:op?', function(){});
app.put('/user/:id', function(){});
app.get('/user/:id/edit', function(){});
app.routes.all()[0].should.be.an.instanceof(Route);
app.routes.all().length.should.equal(5);
},
'test Collection': function(){
var app = express.createServer();
app.get('/user', function(){});
@@ -317,7 +610,29 @@ module.exports = {
app.match.all('/user/12').should.have.length(0);
app.get('/user/:id').should.have.length(0);
},
'test "strict routing" setting': function(){
var app = express.createServer();
app.enable('strict routing');
app.get('/:path', function(req, res, next){
res.send({ type: 'directory' });
});
app.get('/:path/', function(req, res, next){
res.send(['.', '..', 'foo.js', 'bar.js']);
});
assert.response(app,
{ url: '/lib' },
{ body: '{"type":"directory"}' });
assert.response(app,
{ url: '/lib/' },
{ body: '[".","..","foo.js","bar.js"]' });
},
'test "case sensitive routes" setting': function(){
var app = express.createServer();
@@ -381,5 +696,98 @@ module.exports = {
function(){
routes.should.eql(['/:foo?', '/foo']);
});
},
'test route callback error handling': function(){
var app = express.createServer()
, calls = [];
app.get('/user/:id', function(req, res, next){
calls.push('one');
next();
});
app.get('/user/:id', function(req, res, next){
calls.push('two');
next(new Error('fail'));
});
app.get('/user/:id', function(req, res, next){
calls.push('three');
next();
});
app.get('/user/*', function(err, req, res, next){
res.statusCode = 500;
res.send('error: ' + err.message);
});
app.get('/user/*', function(req, res, next){
calls.push('four');
next();
});
assert.response(app,
{ url: '/user/12' },
{ body: 'error: fail' }, function(){
calls.should.eql(['one', 'two']);
});
},
'test route callback thrown error handling': function(){
var app = express.createServer()
, calls = [];
app.get('/user/:id', function(req, res, next){
calls.push('one');
next();
});
app.get('/user/:id', function(req, res, next){
calls.push('two');
throw new Error('fail');
});
app.get('/user/:id', function(req, res, next){
calls.push('three');
next();
});
app.get('/user/*', function(err, req, res, next){
res.statusCode = 500;
res.send('error: ' + err.message);
});
app.get('/user/*', function(req, res, next){
calls.push('four');
next();
});
assert.response(app,
{ url: '/user/12' },
{ body: 'error: fail' }, function(){
calls.should.eql(['one', 'two']);
});
},
'test route callback error recovery': function(){
var app = express.createServer();
app.get('/user/:id', function(req, res, next){
next(new Error('fail'));
});
app.get('/user/*', function(err, req, res, next){
req.error = err;
next();
});
app.get('/user/*', function(req, res, next){
res.send('recovered from error: ' + req.error.message);
});
assert.response(app,
{ url: '/user/12' },
{ body: 'recovered from error: fail' });
}
};
-26
Ver Arquivo
@@ -453,19 +453,6 @@ module.exports = {
{ url: '/user' },
{ body: '<p>tj</p>' });
// as: this collection option
app.get('/person', function(req, res){
res.partial('person.jade', {
as: this,
collection: [{ name: 'tj' }],
locals: { label: 'name:' }
});
});
assert.response(app,
{ url: '/person' },
{ body: '<p>name: tj</p>' });
// as: global collection option
app.get('/videos', function(req, res){
res.partial('video.jade', {
@@ -513,19 +500,6 @@ module.exports = {
{ url: '/video-global' },
{ body: '<p>Tim Burton</p>' });
app.get('/person-this', function(req, res){
res.partial('person.jade', {
object: { name: 'tj' },
locals: { label: 'User:' },
as: this
});
});
// Non-collection as: this
assert.response(app,
{ url: '/person-this' },
{ body: '<p>User: tj</p>' });
// No options
app.get('/nothing', function(req, res){
res.partial('hello.ejs');