# 新手指南
### 開始
在確認已經安裝了node之后([下載](http://nodejs.org/#download)), 在你的機器上創建一個目錄,讓我們來開始你的第一個應用程序吧
```
$ mkdir hello-world
```
在這個目錄中你首先得定義一下你的應用程序“包”文件,它和其它的node程序包是一樣的。 你得在這個目錄中創建一個package.json文件,在里面express作為一個依賴。 你也可以使用 `npm info express version` 來獲取express最新的版本號, 最好使用最新的版本號而不是下面的3.x,這樣新出的功能就不會讓你感覺到奇怪了。
```
{
"name": "hello-world",
"description": "hello world test app",
"version": "0.0.1",
"private": true,
"dependencies": {
"express": "3.x"
}
}
```
現在package.json文件已經準備好了,使用`npm(1)` 安裝依賴, 這里的依賴僅僅是Express。
```
$ npm install
```
當npm完成后,Express 3.x 和它的依賴就安裝到你的 ./node_modules 目錄里了。 你可以通過 `npm ls` 來確認一下,它會把Express 和它的依賴展示成下面的樹狀結構。
```
$ npm ls
hello-world@0.0.1 /private/tmp
└─┬ express@3.0.0beta7
├── commander@0.6.1
├─┬ connect@2.3.9
│ ├── bytes@0.1.0
│ ├── cookie@0.0.4
│ ├── crc@0.2.0
│ ├── formidable@1.0.11
│ └── qs@0.4.2
├── cookie@0.0.3
├── debug@0.7.0
├── fresh@0.1.0
├── methods@0.0.1
├── mkdirp@0.3.3
├── range-parser@0.0.4
├─┬ response-send@0.0.1
│ └── crc@0.2.0
└─┬ send@0.0.3
└── mime@1.2.6
```
現在我們來寫真正的代碼了!創建一個名為app.js 或者 server.js的文件,叫什么看你個人喜好了。 載入express 然后使用代碼 `express()`創建一個新的應用程序:
```
var express = require('express');
var app = express();
```
在這個應用程序實例里,你可以通過 `app.VERB()`定義路由,下面的例子是"GET /"返回 "Hello World" 字符串。 `req` 和 `res` 對象是和node原生提供給你的一致的,你也可以執行 `res.pipe()`, `req.on('data', callback)` 等任何事情在沒有Express的情況下可以做的事情。
Express 給這些對象加了一個封裝好的方法,比如 `res.send()`, 它會幫你設置Content-Length:
```
app.get('/hello.txt', function(req, res){
res.send('Hello World');
});
```
現在我們通過執行 `app.listen()` 來綁定并監聽連接。 它接受的參數和node[net.Server#listen()](http://nodejs.org/api/net.html#net_server_listen_port_host_backlog_listeninglistener)的方法一致:
```
var server = app.listen(3000, function() {
console.log('Listening on port %d', server.address().port);
});
```
### 使用express(1) 來生成一個應用程序
Express團隊維護了一個可以快速生成項目模板的可執行文件,這里命名為 `express(1)`. 如果你使用npm全局安裝的express-generator, 在你的機器任何位置它都是可用的:
```
$ npm install -g express-generator
```
這個工具提供了一個非常簡單的生成一個程序骨架的功能,但是它也有局限,比如它只支持很少的幾個模板引擎。 而事實上Express幾乎支持所有的為node所建的模板引擎。 使用 `--help`查看一下幫助:
```
Usage: express [options]
Options:
-h, --help 輸出幫助信息
-V, --version 輸出版本號
-e, --ejs 添加 ejs 模板引擎支持 (默認為jade)
-H, --hogan 添加 hogan.js模板引擎支持
-c, --css <engine> 樣式 <引擎> 支持 (less|stylus) (默認為css)
-f, --force 強制在非空目錄執行</engine>
```
如果你想生成一個支持Jade, Stylus的應用程序,只需要簡單的執行下面的命令:
```
$ express --sessions --css stylus --ejs myapp
create : myapp
create : myapp/package.json
create : myapp/app.js
create : myapp/public
create : myapp/public/javascripts
create : myapp/public/images
create : myapp/public/stylesheets
create : myapp/public/stylesheets/style.styl
create : myapp/routes
create : myapp/routes/index.js
create : myapp/views
create : myapp/views/index.jade
create : myapp/views/layout.jade
install dependencies:
$ cd myapp && npm install
run the app:
$ DEBUG=myapp node app
```
和其它node程序一樣,你必須安裝依賴:
```
$ cd myapp
$ npm install
```
然后讓我們運行它吧!
```
$ node app
```
這些就是一個簡單的應用程序創建和運行的所有步驟。 記住Express沒有限定任何的目錄結構,這只是一個方便你工作的基本結構。 如果你想得到更多怎么組織目錄結構選擇,可以查看github上的[示例](https://github.com/visionmedia/express/tree/master/examples)。
### 錯誤處理
錯誤處理的中間件和普通的中間件定義是一樣的, 只是它必須有4個形參,這是它的形式: `(err, req, res, next)`:
```
app.use(function(err, req, res, next){
console.error(err.stack);
res.send(500, 'Something broke!');
});
```
一般來說非強制性的錯誤處理一般被定義在最后,下面的代碼展示的就是放在別的 `app.use()` 之后:
```
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(function(err, req, res, next){
// logic
});
```
在這些中間件里的響應是可以任意定義的。只要你喜歡,你可以返回任意的內容,譬如HTML頁面, 一個簡單的消息,或者一個JSON字符串。
對于一些組織或者更高層次的框架,你可能會像定義普通的中間件一樣定義一些錯誤處理的中間件。 假設你想定義一個中間件區別對待通過XHR和其它請求的錯誤處理,你可以這么做:
```
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(logErrors);
app.use(clientErrorHandler);
app.use(errorHandler);
```
通常`logErrors`用來紀錄諸如stderr, loggly, 或者類似服務的錯誤信息:
```
function logErrors(err, req, res, next) {
console.error(err.stack);
next(err);
}
```
`clientErrorHandler` 定義如下,注意錯誤非常明確的向后傳遞了。
```
function clientErrorHandler(err, req, res, next) {
if (req.xhr) {
res.send(500, { error: 'Something blew up!' });
} else {
next(err);
}
}
```
下面的`errorHandler` "捕獲所有" 的異常, 定義為:
```
function errorHandler(err, req, res, next) {
res.status(500);
res.render('error', { error: err });
}
```
### 在線用戶計數
這一小節我們講解一個小而全的應用程序,它通過[Redis](http://redis.io)記錄在線用戶數。 首先你需要創建一個package.json 文件,包含兩個依賴, 一個是redis 客戶端,另一個是Express。 另外需要確認你安裝了redis, 可以能過執行`$ redis-server`來確認:
```
{
"name": "app",
"version": "0.0.1",
"dependencies": {
"express": "3.x",
"redis": "*"
}
}
```
接下來你需要你創建一個應用程序,和一個redis連接:
```
var express = require('express');
var redis = require('redis');
var db = redis.createClient();
var app = express();
```
接下來是紀錄用戶在線的中間件。 這里我們使用sorted sets, 它的一個好處是我們可以查詢最近N毫秒內在線的用戶。 我們通過傳入一個時間戳來當作成員的"score"。 注意我們使用 User-Agent 作為一個標識用戶的id。
```
app.use(function(req, res, next){
var ua = req.headers['user-agent'];
db.zadd('online', Date.now(), ua, next);
});
```
下一個中間件是通過**zrevrangebyscore**來查詢上一分鐘在線用戶。 我們將能得到從當前時間算起在60,000毫秒內活躍的用戶。
```
app.use(function(req, res, next){
var min = 60 * 1000;
var ago = Date.now() - min;
db.zrevrangebyscore('online', '+inf', ago, function(err, users){
if (err) return next(err);
req.online = users;
next();
});
});
```
最后我們來使用它,綁定到一個端口!這些就是這個程序的一切了,在不同的瀏覽器里訪問這個應用程序,你會看到計數的增長。
```
app.get('/', function(req, res){
res.send(req.online.length + ' users online');
});
app.listen(3000);
```
### 給Express加一層代理
在Express的前端使用一個反向代理,比如 Varnish 或者 Nginx是非常常見的,它不需要額外的配置。 在通過`app.enable('trust proxy')`激活了"trust proxy" 設置后, Express 就會知道它在一個代理的后面,`X-Forwarded-*` 必須被信任, 通常情況下這些頭是很容易被偽裝的。
使用了這個設置后會有一些很棒的小變化。 首先由代理設置的`X-Forwarded-Proto` 會告訴程序它是https 還是http 。 這個值會影響[req.protocol](/api#req.protocol).
第二個變化是 [req.ip](/api#req.ip) 和 [req.ips](/api#req.ips) 的值會被`X-Forwarded-For`列表里的地址取代。
### 調試 Express
Express使用 [debug](https://github.com/visionmedia/debug) 模塊 來輸出信息。如果想看到這些信息,可以在運行你的程序時設置 `DEBUG` 環境變量為 `express:*` ,調試信息會輸出在終端里 。
```
$ DEBUG=express:* node app.js
```
使用上面的方式運行 `hello world` 的例子,將會輸出下面的內容
```
express:application booting in development mode +0ms
express:router defined get /hello.txt +0ms
express:router defined get /hello.txt +1ms
```
獲取更多關于 `debug`的信息,可以查看 [debug 文檔](https://github.com/visionmedia/debug)