nodejs開發 過程當中express路由與中間件的理解

nodejs開發 express路由與中間件

 

路由

一般HTTP URL的格式是這樣的:javascript

http://host[:port][path]css

http表示協議。html

host表示主機。java

port爲端口,可選字段,不提供時默認爲80。node

path指定請求資源的URI(Uniform Resource Identifier,統一資源定位符),若是URL中沒有給出path,通常會默認成「/」(一般由瀏覽器或其它HTTP客戶端完成補充上)。正則表達式

所謂路由,就是如何處理HTTP請求中的路徑部分。好比「http://xxx.com/users/profile」這個URL,路由將決定怎麼處理/users/profile這個路徑。express

來回顧咱們在Node.js開發入門——Express安裝與使用中提供的express版本的HelloWorld代碼:api

複製代碼
var express = require('express');
var app = express();

app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.listen(8000, function () {
  console.log('Hello World is listening at port 8000');
});
複製代碼

上面代碼裏的app.get()調用,實際上就爲咱們的網站添加了一條路由,指定「/」這個路徑由get的第二個參數所表明的函數來處理。數組

express對象能夠針對常見的HTTP方法指定路由,使用下面的方法:瀏覽器

app.METHOD(path, callback [, callback ...])

路由路徑

使用字符串的路由路徑示例:

字符 ?、+、* 和 () 是正則表達式的子集,- 和 . 在基於字符串的路徑中按照字面值解釋。
使用正則表達式的路由路徑示例:

 

路由句柄

能夠爲請求處理提供多個回調函數,其行爲相似 中間件。惟一的區別是這些回調函數有可能調用 next('route') 方法而略過其餘路由回調函數。能夠利用該機制爲路由定義前提條件,若是在現有路徑上繼續執行沒有意義,則可將控制權交給剩下的路徑。

路由句柄有多種形式,能夠是一個函數、一個函數數組,或者是二者混合,以下所示.

使用一個回調函數處理路由:

使用多個回調函數處理路由(記得指定 next 對象):

使用回調函數數組處理路由:

混合使用函數和函數數組處理路由:

 

METHOD能夠是GET、POST等HTTP方法的小寫,例如app.get,app.post。path部分呢,既能夠是字符串字面量,也能夠是正則表達式。最簡單的例子,把前面代碼裏的app.get()調用的一個參數’/’修改成’*’,含義就不同。改動以前,只有訪問「http://localhost:8000」或「http://localhost:8000/」這種形式的訪問纔會返回「Hello World!」,而改以後呢,像「http://localhost:8000/xxx/yyyy.zz」這種訪問也會返回「Hello World!」。

使用express構建Web服務器時,很重要的一部分工做就是決定怎麼響應針對某個路徑的請求,也即路由處理。

最直接的路由配置方法,就是調用app.get()、app.post()一條一條的配置,不過對於須要處理大量路由的網站來說,這會搞出人命來的。因此呢,咱們實際開發中須要結合路由參數(query string、正則表達式、自定義的參數、post參數)來減少工做量提升可維護性。更詳細的信息,參考http://expressjs.com/guide/routing.html

中間件

Express裏有個中間件(middleware)的概念。所謂中間件,就是在收到請求後和發送響應以前這個階段執行的一些函數。

要在一條路由的處理鏈上插入中間件,可使用express對象的use方法。該方法原型以下:

app.use([path,] function [, function...])

當app.use沒有提供path參數時,路徑默認爲「/」。當你爲某個路徑安裝了中間件,則當以該路徑爲基礎的路徑被訪問時,都會應用該中間件。好比你爲「/abcd」設置了中間件,那麼「/abcd/xxx」被訪問時也會應用該中間件。

中間件函數的原型以下:

function (req, res, next)

第一個參數是Request對象req。第二個參數是Response對象res。第三個則是用來驅動中間件調用鏈的函數next,若是你想讓後面的中間件繼續處理請求,就須要調用next方法。

給某個路徑應用中間件函數的典型調用是這樣的:

app.use('/abcd', function (req, res, next) {
  console.log(req.baseUrl);
  next();
})

app.static中間件

Express提供了一個static中間件,能夠用來處理網站裏的靜態文件的GET請求,能夠經過express.static訪問。

express.static的用法以下:

express.static(root, [options])

第一個參數root,是要處理的靜態資源的根目錄,能夠是絕對路徑,也能夠是相對路徑。第二個可選參數用來指定一些選項,好比maxAge、lastModified等,更多選項的介紹看這裏:http://expressjs.com/guide/using-middleware.html#middleware.built-in

一個典型的express.static應用以下:

複製代碼
var options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now());
  }
}

app.use(express.static('public', options));
複製代碼

上面這段代碼將當前路徑下的public目錄做爲靜態文件,而且爲Cache-Control頭部的max-age選項爲1天。還有其它一些屬性,請對照express.static的文檔來理解。

使用express建立的HelloExpress項目的app.js文件裏有這樣一行代碼:

app.use(express.static(path.join(__dirname, 'public')));

這行代碼將HelloExpress目錄下的public目錄做爲靜態文件交給static中間件來處理,對應的HTTP URI爲「/」。path是一個Node.js模塊,__dirname是Node.js的全局變量,指向當前運行的js腳本所在的目錄。path.join()則用來拼接目錄。

有了上面的代碼,你就能夠在瀏覽器裏訪問「http://localhost:3000/stylesheets/style.css」。咱們作一點改動,把上面的代碼修改爲下面這樣:

app.use('/static', express.static(path.join(__dirname, 'public')));

上面的代碼呢,針對/static路徑使用static中間件處理public目錄。這時你再用瀏覽器訪問「http://localhost:3000/stylesheets/」就會看到一個404頁面,將地址換成「http://localhost:3000/static/stylesheets/style.css」就能夠了。

Router

Express還提供了一個叫作Router的對象,行爲很像中間件,你能夠把Router直接傳遞給app.use,像使用中間件那樣使用Router。另外你還可使用router來處理針對GET、POST等的路由,也能夠用它來添加中間件,總之你能夠將Router看做一個微縮版的app。

下面的代碼建立一個Router實例:

var router = express.Router([options]);

而後你就能夠像使用app同樣使用router(代碼來自http://expressjs.com/4x/api.html#router):

複製代碼
// invoked for any requests passed to this router
router.use(function(req, res, next) {
  // .. some logic here .. like any other middleware
  next();
});

// will handle any request that ends in /events
// depends on where the router is "use()'d"
router.get('/events', function(req, res, next) {
  // ..
});
複製代碼

定義了router後,也能夠將其做爲中間件傳遞給app.use:

app.use('/events', router);

上面這種用法,會針對URL中的「/events」路徑應用router,你在router對象上配置的各類路由策略和中間件,都會被在合適的時候應用。

路由模塊

express工具建立的應用,有一個routes目錄,下面保存了應用到網站的Router模塊,index.js和user.js。這兩個模塊基本同樣,咱們研究一下index.js。

下面是index.js的內容:

複製代碼
var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;
複製代碼

index.js建立了一個Router實例,而後調用router.get爲「/」路徑應用了路由函數。最後呢使用module.exports將Router對象導出。

下面是app.js裏引用到index.js的代碼:

var routes = require('./routes/index');
...
app.use('/', routes);

第一處,require(‘./routes/index’)將其做爲模塊使用,這行代碼導入了index.js,而且將index.js導出的router對象保存在變量routes裏以供後續使用。注意,上面代碼裏的routes就是index.js裏的router。

第二處代碼,把routes做爲一箇中間件,掛載到了「/」路徑上。

模塊

前面分析index.js時看到了module.exports的用法。module.exports用來導出一個Node.js模塊內的對象,調用者使用require加載模塊時,就會得到導出的對象的實例。

咱們的index.js導出了Router對象。app.js使用require(‘./routes/index’)獲取了一個Router實例。

module.exports還有一個輔助用法,即直接使用exports來導出。

複製代碼
exports.signup = function(req, res){
  //some code
}

exports.login = function(req, res){
  //some code
}
複製代碼

上面的代碼(假定在users.js文件裏)直接使用exports來導出。當使用exports來導出時,你設置給exports的屬性和方法,實際上都是module.exports的。這個模塊最終導出的是module.exports對象,你使用相似「exports.signup」這種形式設置的方法或屬性,調用方在require後均可以直接使用。

使用users模塊的代碼多是這樣的:

複製代碼
var express = require('express');
var app = express();
...
var users = require('./routes/users');
app.post('/signup', users.signup);
app.post('/login', users.login);
...
複製代碼

 

 

 1.  什麼是router路徑,什麼是middleware?

 

咱們輸入www.baidu.com 來訪問百度的主頁,瀏覽器會自動轉換爲 http://www.baidu.com:80/(省略一些參數)。 http://表明咱們同服務器鏈接使用的是http協議,www.baidu.com 表明的是服務器的主機地址,會被咱們的pc經過DNS解析爲IP地址。80是默認的應用層端口。/ 即爲咱們訪問的服務器(www.baidu.com)的路徑,服務器要對咱們訪問的這個路徑作出響應,採起必定的動做。咱們能夠把這一過程看作一個路由。

 訪問的路徑‘/’即爲router的路徑,服務器採起的動做即爲middleware,即爲一個個特殊的函數。

 

  2. router路徑

   www.baidu.com/test: 路徑爲 /test

      www.baidu.com/test?name=1&number=2: 路徑一樣爲/test, ?後面會被服務器理解傳給路徑的參數。

   3. Middleware 

複製代碼
複製代碼
An Express application is essentially a stack of middleware which are executed serially.(express應用其實就是由一系列順序執行的Middleware組成。)
A middleware is a function with access to the request object (req), the response object (res), and the next middleware in line in the request-response cycle of an Express application. It is commonly denoted by a variable named next. Each middleware has the capacity to execute any code, make changes to the request and the reponse object, end the request-response cycle, and call the next middleware in the stack. Since middleware are execute serially, their order of inclusion is important.(中間件其實就是一個訪問express應用串入的req,res,nex參數的函數,這個函數能夠訪問任何經過req,res傳入的資源。)
If the current middleware is not ending the request-response cycle, it is important to call next() to pass on the control to the next middleware, else the request will be left hanging.(若是當前中間件沒有完成對網頁的res響應 ,還能夠經過next把router 留給下一個middleware繼續執行)
With an optional mount path, middleware can be loaded at the application level or at the router level. Also, a series of middleware functions can be loaded together, creating a sub-stack of middleware system at a mount point.
複製代碼
複製代碼

 

  路由的產生是經過HTTP的各類方法(GET, POST)產生的,Middleware能夠跟router路徑跟特定的HTTP方法綁定,也能夠跟全部的方法綁定。

  3.1 經過express應用的use(all),把Middleware同router路徑上的全部HTTP方法綁定:

1 app.use(function (req, res, next) {
2   console.log('Time: %d', Date.now());
3   next();
4 })

 

  3.2 經過express應用的http.verb,把Middleware同router路徑上的特定的HTTP方法綁定:

複製代碼
複製代碼
1 app.get('/', function(req, res){
2   res.send('hello world');
3 });
4 
5 
6 app.post('/', function(req, res){
7   res.send('hello world');
8 });
複製代碼
複製代碼

 

 

  4.  Express的Router對象

  當express實例的路由愈來愈多的時候,最好把路由分類獨立出去,express的實例(app) 能更好的處理其餘邏輯流程。Express的Router對象是一個簡化的 app實例,只具備路由相關的功能,包括use, http verbs等等。最後這個Router再經過app的use掛載到app的相關路徑下。

 

複製代碼
複製代碼
 1 var express = require('express');
 2 var app = express();
 3 var router = express.Router();
 4 
 5 // simple logger for this router's requests
 6 // all requests to this router will first hit this middleware
 7 router.use(function(req, res, next) {
 8   console.log('%s %s %s', req.method, req.url, req.path);
 9   next();
10 });
11 
12 // this will only be invoked if the path ends in /bar
13 router.use('/bar', function(req, res, next) {
14   // ... maybe some additional /bar logging ...
15   next();
16 });
17 
18 // always invoked
19 router.use(function(req, res, next) {
20   res.send('Hello World');
21 });
22 
23 app.use('/foo', router);
24 
25 app.listen(3000);
複製代碼
複製代碼

 

 

 

  router的路由必須經過app.use和app.verbs 掛載到app上才能被響應。因此上述代碼,只有在app捕捉到 /foo路徑上的路由時,才能router中定義的路由,雖然router中有針對 '/' 的路由,可是被app中的路由給覆蓋了。

 

 

附:app.verbs和app.use的路由路徑區別:

先看一段測試代碼:

複製代碼
var express = require('express');

var app = express();
var router = express.Router();

app.get('/', function(req, res){
     console.log('test1');
});

app.use('/', function(req, res){
     console.log('test2');
});

router.get('/', function(req, res){
     console.log('test3');
});

app.listen(4000);
複製代碼

 

輸入url: localhost:4000

輸出結果:test1
 
輸入url: localhost:4000/hello
輸出結果:test2
 
  結論:app.get掛載‘/’的路由只響應跟'/'精確匹配的GET請求。 而app.use掛載的'/'的路由響應全部以'/' 爲起始路由的路由,且不限制HTTP訪問的方法。如下說明:Mounting a middleware at a path will cause the middleware function to be executed whenever the base of the requested path matches the path.
 
1 app.use([path], [function...], function)
2 Mount the middleware function(s) at the path. If path is not specified, it defaults to "/".
3 
4 Mounting a middleware at a path will cause the middleware function to be executed whenever the base of the requested path matches the path.
相關文章
相關標籤/搜索